Commit baed89e8 authored by Tong Li's avatar Tong Li

MP-将kpi设置接入,优化器

parent da795079
......@@ -532,7 +532,8 @@ public class MacroPlannerResultController {
description = "执行数据库转换→数据验证→MIP建模→分层求解→JSON结果保存的完整流程。"
+ "耗时通常数秒到数分钟, 取决于数据规模。")
public R<Map<String, Object>> runOptimization(
@RequestParam("sceneId") @Parameter(description = "场景ID", required = true) String sceneId) {
@RequestParam("sceneId") @Parameter(description = "场景ID", required = true) String sceneId
, @RequestParam("sceneId") @Parameter(description = "场景ID", required = true) Integer sceneI) {
if (sceneId == null || sceneId.trim().isEmpty()) {
return R.failed("sceneId不能为空");
}
......@@ -543,7 +544,7 @@ public class MacroPlannerResultController {
try {
// 1. 数据转换: 数据库工艺物料类 → macroplanner 实体
TestDataBuilder data = macroPlannerDataConverter.convert(sid);
TestDataBuilder data = macroPlannerDataConverter.convert(sid,sceneI);
result.put("dataSummary", buildDataSummary(data));
// 2. 数据验证
......
......@@ -2,10 +2,13 @@ package com.aps.macroplanner.data;
import com.aps.common.util.ParamValidator;
import com.aps.entity.*;
import com.aps.macroplanner.objective.KpiSetting;
import com.aps.macroplanner.scene.MacroSceneContext;
import com.aps.entity.basic.Material;
import com.aps.entity.basic.MaterialSupply;
import com.aps.mapper.EquipShiftCapacityMapper;
import com.aps.service.KpiCategoryItemService;
import com.aps.service.PlanPeriodService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
......@@ -127,8 +130,12 @@ public class MacroPlannerDataConverter {
// ==================== 注入的 Service ====================
@Autowired
private LanuchService lanuchService;
@Autowired
private PlanPeriodService planPeriodService;
@Autowired
private ApsTimeConfigService apsTimeConfigService;
private KpiCategoryItemService kpiCategoryItemService;
// ==================== 公开入口 ====================
......@@ -138,16 +145,16 @@ public class MacroPlannerDataConverter {
* @param sceneId 场景ID
* @return 填充好的 TestDataBuilder, 可直接传给 MacroPlannerOptimizer
*/
public TestDataBuilder convert(String sceneId) {
return MacroSceneContext.execute(sceneId, () -> convertScoped(sceneId));
public TestDataBuilder convert(String sceneId,Integer kpiSetting) {
return MacroSceneContext.execute(sceneId, () -> convertScoped(sceneId,kpiSetting));
}
private TestDataBuilder convertScoped(String sceneId) {
private TestDataBuilder convertScoped(String sceneId,Integer kpiSetting) {
log.info("开始转换场景数据到 macroplanner: sceneId={}", sceneId);
ConvertContext ctx = loadRawData(sceneId);
ConvertContext ctx = loadRawData(sceneId,kpiSetting);
buildMaterials(ctx);
buildDailyCapacityByUnitId(ctx);
......@@ -219,28 +226,28 @@ public class MacroPlannerDataConverter {
return new ArrayList<>(headers.keySet());
}
private ConvertContext loadRawData(String sceneId) {
private ConvertContext loadRawData(String sceneId,Integer kpiSetting) {
ConvertContext ctx = new ConvertContext();
// 0. 读取时间配置, 计算 horizonEnd
// endCount = 期数 (与 periodDimension 结合决定实际天数)
LambdaQueryWrapper<ApsTimeConfig> wrapper= new LambdaQueryWrapper<ApsTimeConfig>();
if(sceneId != null && !sceneId.isEmpty())
{
// wrapper.eq(ApsTimeConfig::getMpSceneId,sceneId);
}
ApsTimeConfig timeConfig = apsTimeConfigService.getOne(wrapper);
ctx.baseTime = LocalDateTime.of(2026, 9, 28, 0, 0, 0);
LambdaQueryWrapper<PlanPeriod> wrapper= new LambdaQueryWrapper<PlanPeriod>();
wrapper.eq(PlanPeriod::getIsplan,1);
wrapper.eq(PlanPeriod::getIsdeleted,0);
wrapper.eq(PlanPeriod::getMpSceneId,sceneId);
PlanPeriod timeConfig = planPeriodService.getOne(wrapper);
ctx.periodDimension = "WEEK";
if (timeConfig != null && timeConfig.getPeriodDimension() != null
&& !timeConfig.getPeriodDimension().trim().isEmpty()) {
ctx.periodDimension = timeConfig.getPeriodDimension().trim().toUpperCase();
if (timeConfig != null && timeConfig.getTimeunit() != null
&& !timeConfig.getTimeunit().trim().isEmpty()) {
ctx.baseTime =timeConfig.getBasetime();
ctx.periodDimension =toPeriodType(timeConfig.getTimeunitid().toString()) ;
}
int periodCount = DEFAULT_PERIOD_COUNT;
if (timeConfig != null && timeConfig.getEndCount() != null) {
int ec = timeConfig.getEndCount().intValue();
if (timeConfig != null && timeConfig.getFuture() != null) {
int ec = timeConfig.getFuture().intValue();
if (ec > 0) {
periodCount = ec;
}
......@@ -251,6 +258,7 @@ public class MacroPlannerDataConverter {
ctx.horizonEnd = calculateHorizonEnd(ctx.periodDimension, baseDate, periodCount);
LocalDateTime horizonEndDateTime = ctx.horizonEnd.atStartOfDay();
log.info("排产周期: dimension={}, periodCount={}, baseTime={}, horizonEnd={}",
ctx.periodDimension, periodCount, ctx.baseTime, ctx.horizonEnd);log.info("排产周期: dimension={}, periodCount={}, baseTime={}, horizonEnd={}",
ctx.periodDimension, periodCount, ctx.baseTime, ctx.horizonEnd);
// 1. 需求订单 (ApsDemandOrder, 按 deliverytime 时间范围过滤)
......@@ -402,7 +410,10 @@ public class MacroPlannerDataConverter {
.eq(EquipShiftCapacity::getIsDeleted, 0));
}
log.info("加载设备产能日历(EquipShiftCapacity): {} 条 (时间范围过滤)", ctx.equipShiftCapacities.size());
ctx.kpiCategoryItems = kpiCategoryItemService.list(
new LambdaQueryWrapper<KpiCategoryItem>()
.eq(KpiCategoryItem::getIsDeleted, 0)
.eq(KpiCategoryItem::getCategoryId, kpiSetting));
return ctx;
}
......@@ -661,12 +672,36 @@ public class MacroPlannerDataConverter {
// 自然月: 从 baseDate 所在月的1号开始, periodCount 个月后
LocalDate monthStart = baseDate.withDayOfMonth(1);
return monthStart.plusMonths(periodCount);
case "YEAR":
// 自然年: 从 baseDate 所在月的1号开始, periodCount 个月后
LocalDate yearStart = baseDate.withDayOfYear(1);
return yearStart.plusYears(periodCount);
case "QUARTER":
// 自然季度: 从 baseDate 所在季度的首月1号开始, periodCount 个季度后
int month = baseDate.getMonthValue();
// 计算当前属于第几季度(1~4),整数除法自动取整
int quarter = (month - 1) / 3 + 1;
// 计算该季度的首月月份
int quarterStartMonth = (quarter - 1) * 3 + 1;
// 构造季度起始日期(首月1号)
LocalDate quarterStart = baseDate.withMonth(quarterStartMonth).withDayOfMonth(1);
// 加上 periodCount 个季度(每个季度3个月)
return quarterStart.plusMonths((long) periodCount * 3);
case "DAY":
default:
return baseDate.plusDays(periodCount);
}
}
public static String toPeriodType(String code) {
switch (code) {
case "2": return "WEEK";
case "3": return "MONTH";
case "4": return "QUARTER";
case "5": return "YEAR";
case "1":
default: return "DAY";
}
}
/**
* 计算给定日期范围内的有效工作日数 (不含周日)。
*/
......@@ -702,7 +737,7 @@ public class MacroPlannerDataConverter {
fillPeriodsAndUnitPeriods(ctx);
fillSalesDemands(ctx);
fillInventorySpecsAndSupplySpecs(ctx);
fillKpiWeights();
fillKpiWeights(ctx);
}
// ---------- 步骤3: Product + StockingPoint ----------
......@@ -1415,23 +1450,32 @@ public class MacroPlannerDataConverter {
}
// ---------- 步骤10: KPIWeights (默认值, 复用 RoutingTestDataBuilder) ----------
private void fillKpiWeights(ConvertContext ctx) {
private void fillKpiWeights() {
kpiWeights = new KPIWeights(
100.0, // fulfillmentWeight
10.0, // lotSizeWeight
5.0, // maxInventoryLevelWeight
5.0, // minInventoryLevelWeight
8.0, // targetInventoryLevelWeight
3.0, // unitCapacityWeight
8.0, // supplyTargetWeight
5.0, // minSupplyWeight
5.0, // maxSupplyWeight
1.0, // salesDemandPriorityWeight
20.0, // postponementPenaltyWeight
5.0 // processMaxQuantityWeight
);
List<KpiSetting> kpis=new ArrayList<>();
for (KpiCategoryItem item:ctx.kpiCategoryItems) {
KpiSetting kpi=new KpiSetting(item.getKpiName(), item.getKpiCode(),item.getWeight().doubleValue(),item.getSort().intValue());
kpis.add(kpi);
}
kpiSettings = kpis;
}
// private void fillKpiWeights() {
// kpiWeights = new KPIWeights(
// 100.0, // fulfillmentWeight
// 10.0, // lotSizeWeight
// 5.0, // maxInventoryLevelWeight
// 5.0, // minInventoryLevelWeight
// 8.0, // targetInventoryLevelWeight
// 3.0, // unitCapacityWeight
// 8.0, // supplyTargetWeight
// 5.0, // minSupplyWeight
// 5.0, // maxSupplyWeight
// 1.0, // salesDemandPriorityWeight
// 20.0, // postponementPenaltyWeight
// 5.0 // processMaxQuantityWeight
// );
// }
// ---------- 工具方法 ----------
......@@ -1495,5 +1539,7 @@ public class MacroPlannerDataConverter {
Set<String> unlimitedUnitIds = new HashSet<>();
/** 排产周期数, 从 ApsTimeConfig 计算, 维度为 DAY 时是天数, WEEK 时是周数, MONTH 时是月数 */
int periodCount = 7;
List<KpiCategoryItem> kpiCategoryItems = new ArrayList<>();
}
}
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