package com.kaidi.oa.web; import com.kaidi.oa.common.ApiResp; import com.kaidi.oa.domain.BomItem; import com.kaidi.oa.repository.BomItemRepository; import com.kaidi.oa.service.MrpService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * 制造管理中心·MRP 物料需求计划。给一个生产需求(产品 + 数量),按多级 BOM 展开毛需求, * 冲减现有库存 / 采购在途 / 在制工单,算出逐物料净需求与采购/生产建议。运算逻辑见 * {@link MrpService}。 * * 读侧会端出 BOM 物料口径,写口(运算请求体不落库,仅计算)默认受 AuthInterceptor 保护; * MRP 端点读敏感物料结构,已登记进 SENSITIVE_READ_PREFIXES(见 sharedFileSnippets)。 */ @RestController @RequestMapping("/api/oa/mrp") public class MrpController { private final MrpService mrpService; private final BomItemRepository bomRepo; public MrpController(MrpService mrpService, BomItemRepository bomRepo) { this.mrpService = mrpService; this.bomRepo = bomRepo; } /** 可选 product 列表项:顶层 BOM 项(无 parentId 即一份可独立投产的物料清单顶层)。 */ public record RootProduct(Long bomItemId, String name, String unit, String projectName, String category) { } /** * GET /products -> 列出可作为 MRP 输入的顶层产品(无 parentId 的 BOM 顶层项)。 * 供前端下拉选择「要生产什么」。 */ @GetMapping("/products") public ApiResp> products() { List out = new ArrayList<>(); // 顶层项 = parentId 为 null。派生查询 findByParentId(null) 会生成 `= NULL` 永不命中, // 故在内存里过滤(BOM 行量级小,且本端点仅供下拉枚举)。 for (BomItem b : bomRepo.findAll()) { if (b.getParentId() == null) { out.add(new RootProduct(b.getId(), b.getName(), b.getUnit(), b.getProjectName(), b.getCategory())); } } return ApiResp.ok(out); } public record MrpRequest( Long rootBomItemId, String product, Double demandQty, Map inTransit) { } /** * POST /run -> 运算 MRP 净需求。请求体给产品(rootBomItemId 优先,否则 product 名)+ * demandQty(生产需求量),可选 inTransit(物料名→采购在途量)覆盖。返回逐物料 * {物料,毛需求,现有,在途,在制,净需求,建议}。运算不落库(纯只读计算)。 */ @PostMapping("/run") public ApiResp run(@RequestBody MrpRequest req) { double demand = req.demandQty() == null ? 0d : req.demandQty(); return ApiResp.ok(mrpService.compute( req.rootBomItemId(), req.product(), demand, req.inTransit())); } }