guardia-mes/backend/src/main/java/com/zioinfo/mes/plan/PlanController.java
2026-06-14 12:35:18 +09:00

93 lines
3.4 KiB
Java

package com.zioinfo.mes.plan;
import com.zioinfo.mes.admin.AuditService;
import com.zioinfo.mes.ai.AiService;
import com.zioinfo.mes.common.ApiResponse;
import com.zioinfo.mes.common.AuthSupport;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
/**
* 생산계획 API + AI 일정 최적화. 조회 Viewer+, 변경 Manager+.
*/
@RestController
@RequestMapping("/api/mes/plans")
@RequiredArgsConstructor
public class PlanController {
private final PlanMapper mapper;
private final AuditService audit;
private final AiService ai;
@GetMapping
public ApiResponse<List<MesPlan>> list(@RequestParam(required = false) String status,
@RequestParam(required = false) String itemCode) {
return ApiResponse.ok(mapper.findAll(status, itemCode));
}
@GetMapping("/{id}")
public ApiResponse<MesPlan> get(@PathVariable Long id) {
return ApiResponse.ok(require(id));
}
@PostMapping
public ApiResponse<MesPlan> create(@RequestBody MesPlan p, Authentication auth) {
AuthSupport.requireManager(auth);
if (p.getItemCode() == null) throw new IllegalArgumentException("ERR-PLAN-400: itemCode 필수");
if (p.getPlanNo() == null || p.getPlanNo().isBlank())
p.setPlanNo("PLAN-" + LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE) + "-" + (System.currentTimeMillis() % 100000));
if (p.getStatus() == null) p.setStatus("PLANNED");
p.setCreatedBy(AuthSupport.actor(auth));
mapper.insert(p);
audit.log("PLAN_CREATE", p.getPlanNo(), "item=" + p.getItemCode());
return ApiResponse.ok(mapper.findById(p.getId()));
}
@PutMapping("/{id}")
public ApiResponse<MesPlan> update(@PathVariable Long id, @RequestBody MesPlan p, Authentication auth) {
AuthSupport.requireManager(auth);
require(id);
p.setId(id);
mapper.update(p);
audit.log("PLAN_UPDATE", p.getPlanNo(), "id=" + id);
return ApiResponse.ok(mapper.findById(id));
}
@PutMapping("/{id}/status")
public ApiResponse<MesPlan> status(@PathVariable Long id, @RequestBody StatusRequest req, Authentication auth) {
AuthSupport.requireManager(auth);
MesPlan cur = require(id);
mapper.updateStatus(id, req.status());
audit.log("PLAN_STATUS", cur.getPlanNo(), cur.getStatus() + " -> " + req.status());
return ApiResponse.ok(mapper.findById(id));
}
/** AI 일정 최적화 — 납기/셋업 기준 순서 제안. */
@PostMapping("/optimize")
public ApiResponse<List<Map<String, Object>>> optimize(@RequestBody List<Map<String, Object>> orders) {
return ApiResponse.ok(ai.scheduleOptimize(orders));
}
@DeleteMapping("/{id}")
public ApiResponse<Void> delete(@PathVariable Long id, Authentication auth) {
AuthSupport.requireManager(auth);
mapper.delete(id);
audit.log("PLAN_DELETE", String.valueOf(id), "");
return ApiResponse.ok(null);
}
private MesPlan require(Long id) {
MesPlan p = mapper.findById(id);
if (p == null) throw new RuntimeException("ERR-PLAN-404: 생산계획 없음");
return p;
}
record StatusRequest(String status) {}
}