Commit be4b7722 authored by Tong Li's avatar Tong Li

MP

parent d1745f27
package com.aps.controller;
import com.aps.common.util.FlatParquetUtil;
import com.aps.common.util.ParamValidator;
import com.aps.common.util.R;
import com.aps.entity.ApsDemandOrder;
import com.aps.entity.MaterialInfo;
......@@ -13,8 +12,9 @@ import com.aps.macroplanner.data.MacroPlannerDataConverter;
import com.aps.macroplanner.data.TestDataBuilder;
import com.aps.macroplanner.output.ResultWriter;
import com.aps.macroplanner.output.dto.*;
import com.aps.service.MacroPlannerResultService;
import com.aps.service.MacroPlannerProductNetworkService;
import com.aps.service.MacroPlannerResultService;
import com.aps.service.MacroPlannerProductNetworkService1;
import com.aps.service.MpPispipResultPersistenceService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.ortools.Loader;
......@@ -456,7 +456,7 @@ public class MacroPlannerResultController {
description = "成品→半成品→原材料完整BOM供应链树。"
+ "包含各层级的供应来源(含每周期产量)、消耗者、BOM子物料(含每周期消耗量)。"
+ "支持按 productId / spId / unitId / periodIndex 可选过滤。")
public R<SupplyChainNode> getProductNetwork(
public R<TraceNode> getProductNetwork(
@RequestParam("sceneId") @Parameter(description = "场景ID", required = true) String sceneId,
@RequestParam(value = "productId", required = false) @Parameter(description = "按产品ID过滤成品根节点(可选)") String productId,
@RequestParam(value = "spId", required = false) @Parameter(description = "按库存点ID过滤(可选)") String spId,
......@@ -464,7 +464,7 @@ public class MacroPlannerResultController {
if (productId == null || productId.trim().isEmpty() || spId == null || spId.trim().isEmpty()) {
return R.failed("productId 和 spId 不能为空");
}
SupplyChainNode node = macroPlannerProductNetworkService.buildProductNetwork(
TraceNode node = macroPlannerProductNetworkService.buildProductNetwork(
sceneId, productId.trim(), spId.trim(), period);
if (node == null) {
return R.failed("未找到场景 " + sceneId + " 中 " + productId + "@" + spId + " 的产品网络");
......
......@@ -36,7 +36,6 @@ import java.util.Map;
* 求解器被激励去增加生产来满足需求,而非简单地降低销售量。</p>
*
* @see BalanceConstraint 物料平衡约束(DemandSlack 在流入侧)
* @see DemandVariableBuilder 需求变量定义(SalesDemandQty, DemandSlack)
*/
public class DemandSlackLinkageConstraint {
......
......@@ -323,81 +323,82 @@ public class ResultWriter {
private BomStructureData buildBomStructureData() {
BomStructureData s = new BomStructureData();
for (Product p : data.getProducts()) {
BomStructureData.ProductInfo pi = new BomStructureData.ProductInfo();
pi.id = p.getId();
pi.name = p.getName();
pi.code = p.getCode();
s.products.add(pi);
}
for (StockingPoint sp : data.getStockingPoints()) {
BomStructureData.StockingPointInfo si = new BomStructureData.StockingPointInfo();
si.id = sp.getId();
si.name = sp.getName();
s.stockingPoints.add(si);
}
// for (Product p : data.getProducts()) {
// BomStructureData.ProductInfo pi = new BomStructureData.ProductInfo();
// pi.id = p.getId();
// pi.name = p.getName();
// pi.code = p.getCode();
// s.products.add(pi);
// }
// for (StockingPoint sp : data.getStockingPoints()) {
// BomStructureData.StockingPointInfo si = new BomStructureData.StockingPointInfo();
// si.id = sp.getId();
// si.name = sp.getName();
// s.stockingPoints.add(si);
// }
// 产品→库存点映射 (通过 getStockingPointsForProduct 反推)
for (Product p : data.getProducts()) {
for (StockingPoint sp : data.getStockingPointsForProduct(p.getId())) {
BomStructureData.ProductSpMappingInfo mi = new BomStructureData.ProductSpMappingInfo();
mi.productId = p.getId();
mi.spId = sp.getId();
s.productSpMappings.add(mi);
}
}
for (Operation op : data.getOperations()) {
BomStructureData.OperationInfo oi = new BomStructureData.OperationInfo();
oi.id = op.getId();
oi.name = op.getName();
oi.leadTimeDays = op.getLeadTimeDays();
oi.hasLotSize = op.hasLotSize();
oi.lotSize = op.hasLotSize() ? op.getLotSize() : null;
for (OperationOutput oo : op.getOutputs()) {
BomStructureData.OperationOutputInfo ooi = new BomStructureData.OperationOutputInfo();
ooi.productId = oo.getProductId();
ooi.spId = oo.getSpId();
oi.outputs.add(ooi);
}
for (UnitOperation uo : op.getUnitOperations()) {
BomStructureData.UnitOperationInfo uoi = new BomStructureData.UnitOperationInfo();
uoi.unitId = uo.getUnitId();
uoi.unitName = uo.getUnitName();
uoi.capacityCoeff = uo.getCapacityCoeff();
oi.unitOperations.add(uoi);
}
s.operations.add(oi);
}
// for (Product p : data.getProducts()) {
// for (StockingPoint sp : data.getStockingPointsForProduct(p.getId())) {
// BomStructureData.ProductSpMappingInfo mi = new BomStructureData.ProductSpMappingInfo();
// mi.productId = p.getId();
// mi.spId = sp.getId();
// s.productSpMappings.add(mi);
// }
// }
// for (Operation op : data.getOperations()) {
// BomStructureData.OperationInfo oi = new BomStructureData.OperationInfo();
// oi.id = op.getId();
// oi.name = op.getName();
// oi.leadTimeDays = op.getLeadTimeDays();
// oi.hasLotSize = op.hasLotSize();
// oi.lotSize = op.hasLotSize() ? op.getLotSize() : null;
// for (OperationOutput oo : op.getOutputs()) {
// BomStructureData.OperationOutputInfo ooi = new BomStructureData.OperationOutputInfo();
// ooi.productId = oo.getProductId();
// ooi.spId = oo.getSpId();
// oi.outputs.add(ooi);
// }
// for (UnitOperation uo : op.getUnitOperations()) {
// BomStructureData.UnitOperationInfo uoi = new BomStructureData.UnitOperationInfo();
// uoi.unitId = uo.getUnitId();
// uoi.unitName = uo.getUnitName();
// uoi.capacityCoeff = uo.getCapacityCoeff();
// oi.unitOperations.add(uoi);
// }
// s.operations.add(oi);
// }
for (OperationInput input : data.getOperationInputs()) {
BomStructureData.OperationInputInfo ii = new BomStructureData.OperationInputInfo();
ii.operationId = input.getOperation().getId();
ii.inputProductId = input.getInputProduct().getId();
ii.inputProductCode = input.getInputProduct().getCode();
ii.inputSpId = input.getInputSp().getId();
ii.inputSpName = input.getInputSp().getName();
ii.factor = input.getFactor();
s.operationInputs.add(ii);
}
for (Period p : data.getPeriods()) {
BomStructureData.PeriodInfo pi = new BomStructureData.PeriodInfo();
pi.index = p.getIndex();
pi.name = p.getName();
pi.startDate = p.getStartDate() == null ? null : p.getStartDate().toString();
s.periods.add(pi);
}
for (InitialInventory inv : data.getInitialInventories()) {
BomStructureData.InitialInventoryInfo ii = new BomStructureData.InitialInventoryInfo();
ii.productId = inv.getProduct().getId();
ii.spId = inv.getStockingPoint().getId();
ii.quantity = inv.getQuantity();
s.initialInventories.add(ii);
}
for (InTransitSupply its : data.getInTransitSupplies()) {
BomStructureData.InTransitSupplyInfo ti = new BomStructureData.InTransitSupplyInfo();
ti.productId = its.getProduct().getId();
ti.spId = its.getStockingPoint().getId();
ti.arrivalDate = its.getArrivalDate() == null ? null : its.getArrivalDate().toString();
ti.quantity = its.getQuantity();
s.inTransitSupplies.add(ti);
}
// for (Period p : data.getPeriods()) {
// BomStructureData.PeriodInfo pi = new BomStructureData.PeriodInfo();
// pi.index = p.getIndex();
// pi.name = p.getName();
// pi.startDate = p.getStartDate() == null ? null : p.getStartDate().toString();
// s.periods.add(pi);
// }
// for (InitialInventory inv : data.getInitialInventories()) {
// BomStructureData.InitialInventoryInfo ii = new BomStructureData.InitialInventoryInfo();
// ii.productId = inv.getProduct().getId();
// ii.spId = inv.getStockingPoint().getId();
// ii.quantity = inv.getQuantity();
// s.initialInventories.add(ii);
// }
// for (InTransitSupply its : data.getInTransitSupplies()) {
// BomStructureData.InTransitSupplyInfo ti = new BomStructureData.InTransitSupplyInfo();
// ti.productId = its.getProduct().getId();
// ti.spId = its.getStockingPoint().getId();
// ti.arrivalDate = its.getArrivalDate() == null ? null : its.getArrivalDate().toString();
// ti.quantity = its.getQuantity();
// s.inTransitSupplies.add(ti);
// }
return s;
}
......
......@@ -64,6 +64,7 @@ public class BomStructureData {
public static class OperationInputInfo {
public String operationId;
public String inputProductId;
public String inputProductCode;
public String inputSpId;
public String inputSpName;
public double factor;
......
package com.aps.macroplanner.output.dto;
/**
* 作者:佟礼
* 时间:2026-09-20
*/
import java.util.ArrayList;
import java.util.List;
/**
* 产品数量追溯网络节点。
*
* <pre>
* Product + Warehouse
* |
* | demand
* v
* Unit
* |
* | productionQty
* v
* BOM
* |
* v
* Product + Warehouse
* |
* v
* 最近历史 Output
* </pre>
*/
public class TraceNode {
/**
* 产品 ID
*/
public String productId;
/**
* 产品编码
*/
public String productCode;
/**
* 库房 / SP
*/
public String warehouseId;
/**
* 库房名称
*/
public String warehouseName;
/**
* 当前节点需求数量。
*
* 根节点:
* 成品需求量
*
* 子节点:
* Unit生产量 × BOM factor
*/
public double quantity;
/**
* BOM factor。
*
* 根节点一般为 1。
*/
public double factor = 1.0;
/**
* 当前节点匹配到的 Output 总数量。
*
* 注意:
* outputQty 可以 > quantity。
*/
public double outputQty;
/**
* 当前节点库存/库房已有供应量。
*
* 这里表示没有通过生产 Output 继续追溯的数量。
*/
public double inventorySupplyQty;
/**
* 当前节点匹配到的生产 Output。
*
* 一个产品可能对应多个 Output。
*/
// public final List<OutputNode> outputs = new ArrayList<>();
/**
* 当前节点对应的 Unit。
*
* 不同 Unit 必须独立。
*/
public final List<UnitNode> units = new ArrayList<>();
/**
* 实际生产 Output 节点。
*/
public static class OutputNode {
/**
* Output 对应的 dataKey。
*/
public String dataKey;
/**
* 产品 ID。
*/
public String productId;
/**
* 产品 Code。
*/
public String productCode;
/**
* 库房。
*/
public String warehouseId;
public String warehouseName;
/**
* Output 日期/期间。
*/
public Integer periodIndex;
/**
* Output 日期。
*
* 如果项目实际使用 LocalDate/LocalDateTime,
* 可以替换这里的类型。
*/
public String outputDate;
/**
* Output 实际数量。
*
* 可以大于当前 demand。
*/
public double outputQty;
/**
* 对应生产 Unit。
*/
public UnitNode unit;
}
/**
* 独立生产 Unit 节点。
*
* 每个 Unit 单独展开 BOM。
*/
public static class UnitNode {
public String unitId;
public String unitName;
/**
* 当前 Unit 的实际生产量。
*/
public double productionQty;
/**
* 产能占用。
*/
public double capacityUsed;
/**
* 工序。
*/
public String operationId;
public String operationName;
/**
* 当前 Unit 的 BOM 上游节点。
*/
public final List<TraceNode> children =
new ArrayList<>();
}
}
package com.aps.macroplanner.output.dto;
/**
* 作者:佟礼
* 时间:2026-09-20
* 数量追溯用的实际生产Output。
*
* OutputInfo + PeriodTaskResult 合并。
*/
public class TraceOutput {
public String productId;
public String warehouseId;
public String warehouseName;
public String dataKey;
public Integer periodIndex;
public double outputQty;
public String outputDate;
public String operationId;
public String operationName;
public String unitId;
public String unitName;
public double capacityUsed;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment