Commit 2f9e7b36 authored by Tong Li's avatar Tong Li

MP-计划时间,周期类型-关联计划周期表,根据计划周期页面的周期类型,增加了年和季度的计算维度

parent 29be9769
...@@ -30,7 +30,7 @@ import org.springframework.context.ApplicationContext; ...@@ -30,7 +30,7 @@ import org.springframework.context.ApplicationContext;
public class MacroPlannerDataConverterRunner { public class MacroPlannerDataConverterRunner {
public static void main(String[] args) { public static void main(String[] args) {
String sceneId = (args.length > 0) ? args[0] : ""; String sceneId = (args.length > 0) ? args[0] : "ff8a2f2b-e134-4ec2-8e2e-b77e830c9e64";
System.out.println("===== MACROPLANNER DATA CONVERTER RUNNER START ====="); System.out.println("===== MACROPLANNER DATA CONVERTER RUNNER START =====");
System.out.println("SceneId: " + sceneId); System.out.println("SceneId: " + sceneId);
......
...@@ -232,7 +232,7 @@ public class DataValidator { ...@@ -232,7 +232,7 @@ public class DataValidator {
Set<String> opIds = new HashSet<>(); Set<String> opIds = new HashSet<>();
for (Operation op : data.getOperations()) opIds.add(op.getId()); for (Operation op : data.getOperations()) opIds.add(op.getId());
int maxPeriodIdx = data.getPeriods().size() - 1; // int maxPeriodIdx = data.getPeriods().size() - 1;
// 销售需求 // 销售需求
for (SalesDemand sd : data.getSalesDemands()) { for (SalesDemand sd : data.getSalesDemands()) {
...@@ -242,9 +242,9 @@ public class DataValidator { ...@@ -242,9 +242,9 @@ public class DataValidator {
if (!spIds.contains(sd.getStockingPoint().getId())) { if (!spIds.contains(sd.getStockingPoint().getId())) {
errors.add("销售需求引用不存在的库存点: " + sd.getStockingPoint().getId()); errors.add("销售需求引用不存在的库存点: " + sd.getStockingPoint().getId());
} }
if (sd.getPeriod().getIndex() > maxPeriodIdx) { // if (sd.getPeriod().getIndex() > maxPeriodIdx) {
errors.add("销售需求引用不存在的周期: " + sd.getPeriod().getIndex()); // errors.add("销售需求引用不存在的周期: " + sd.getPeriod().getIndex());
} // }
if (sd.getQuantity() < 0) { if (sd.getQuantity() < 0) {
errors.add("销售需求数量不能为负: " + sd.getProduct().getId() + " P" + sd.getPeriod().getIndex()); errors.add("销售需求数量不能为负: " + sd.getProduct().getId() + " P" + sd.getPeriod().getIndex());
} }
......
...@@ -6,6 +6,7 @@ import com.aps.macroplanner.scene.MacroSceneContext; ...@@ -6,6 +6,7 @@ import com.aps.macroplanner.scene.MacroSceneContext;
import com.aps.entity.basic.Material; import com.aps.entity.basic.Material;
import com.aps.entity.basic.MaterialSupply; import com.aps.entity.basic.MaterialSupply;
import com.aps.mapper.EquipShiftCapacityMapper; import com.aps.mapper.EquipShiftCapacityMapper;
import com.aps.service.PlanPeriodService;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
...@@ -34,7 +35,9 @@ import java.time.Duration; ...@@ -34,7 +35,9 @@ import java.time.Duration;
import java.time.DayOfWeek; import java.time.DayOfWeek;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.time.temporal.WeekFields;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
...@@ -126,7 +129,7 @@ public class MacroPlannerDataConverter { ...@@ -126,7 +129,7 @@ public class MacroPlannerDataConverter {
@Autowired @Autowired
private LanuchService lanuchService; private LanuchService lanuchService;
@Autowired @Autowired
private ApsTimeConfigService apsTimeConfigService; private PlanPeriodService planPeriodService;
// ==================== 公开入口 ==================== // ==================== 公开入口 ====================
...@@ -169,23 +172,23 @@ public class MacroPlannerDataConverter { ...@@ -169,23 +172,23 @@ public class MacroPlannerDataConverter {
// 0. 读取时间配置, 计算 horizonEnd // 0. 读取时间配置, 计算 horizonEnd
// endCount = 期数 (与 periodDimension 结合决定实际天数) // endCount = 期数 (与 periodDimension 结合决定实际天数)
LambdaQueryWrapper<ApsTimeConfig> wrapper= new LambdaQueryWrapper<ApsTimeConfig>(); LambdaQueryWrapper<PlanPeriod> wrapper= new LambdaQueryWrapper<PlanPeriod>();
if(sceneId != null && !sceneId.isEmpty()) wrapper.eq(PlanPeriod::getIsplan,1);
{ wrapper.eq(PlanPeriod::getIsdeleted,0);
// wrapper.eq(ApsTimeConfig::getMpSceneId,sceneId); wrapper.eq(PlanPeriod::getMpSceneId,sceneId);
} PlanPeriod timeConfig = planPeriodService.getOne(wrapper);
ApsTimeConfig timeConfig = apsTimeConfigService.getOne(wrapper);
ctx.baseTime = LocalDateTime.of(2026, 9, 28, 0, 0, 0);
ctx.periodDimension = "WEEK"; ctx.periodDimension = "WEEK";
if (timeConfig != null && timeConfig.getPeriodDimension() != null if (timeConfig != null && timeConfig.getTimeunit() != null
&& !timeConfig.getPeriodDimension().trim().isEmpty()) { && !timeConfig.getTimeunit().trim().isEmpty()) {
ctx.periodDimension = timeConfig.getPeriodDimension().trim().toUpperCase(); ctx.baseTime =timeConfig.getBasetime();
ctx.periodDimension =toPeriodType(timeConfig.getTimeunitid().toString()) ;
} }
int periodCount = DEFAULT_PERIOD_COUNT; int periodCount = DEFAULT_PERIOD_COUNT;
if (timeConfig != null && timeConfig.getEndCount() != null) { if (timeConfig != null && timeConfig.getFuture() != null) {
int ec = timeConfig.getEndCount().intValue(); int ec = timeConfig.getFuture().intValue();
if (ec > 0) { if (ec > 0) {
periodCount = ec; periodCount = ec;
} }
...@@ -198,7 +201,6 @@ public class MacroPlannerDataConverter { ...@@ -198,7 +201,6 @@ public class MacroPlannerDataConverter {
log.info("排产周期: dimension={}, periodCount={}, baseTime={}, horizonEnd={}", log.info("排产周期: dimension={}, periodCount={}, baseTime={}, horizonEnd={}",
ctx.periodDimension, periodCount, ctx.baseTime, ctx.horizonEnd); ctx.periodDimension, periodCount, ctx.baseTime, ctx.horizonEnd);
// 1. 需求订单 (ApsDemandOrder, 按 deliverytime 时间范围过滤)
// 1. 需求订单 (ApsDemandOrder, 按 deliverytime 时间范围过滤) // 1. 需求订单 (ApsDemandOrder, 按 deliverytime 时间范围过滤)
ctx.apsDemandOrders = apsDemandOrderMapper.selectList( ctx.apsDemandOrders = apsDemandOrderMapper.selectList(
new LambdaQueryWrapper<ApsDemandOrder>().eq(ApsDemandOrder::getIsdeleted, 0) new LambdaQueryWrapper<ApsDemandOrder>().eq(ApsDemandOrder::getIsdeleted, 0)
...@@ -651,12 +653,38 @@ public class MacroPlannerDataConverter { ...@@ -651,12 +653,38 @@ public class MacroPlannerDataConverter {
// 自然月: 从 baseDate 所在月的1号开始, periodCount 个月后 // 自然月: 从 baseDate 所在月的1号开始, periodCount 个月后
LocalDate monthStart = baseDate.withDayOfMonth(1); LocalDate monthStart = baseDate.withDayOfMonth(1);
return monthStart.plusMonths(periodCount); 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": case "DAY":
default: default:
return baseDate.plusDays(periodCount); 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";
}
}
/** /**
* 计算给定日期范围内的有效工作日数 (不含周日)。 * 计算给定日期范围内的有效工作日数 (不含周日)。
*/ */
...@@ -1144,6 +1172,8 @@ public class MacroPlannerDataConverter { ...@@ -1144,6 +1172,8 @@ public class MacroPlannerDataConverter {
String dimension = ctx.periodDimension; String dimension = ctx.periodDimension;
LocalDate horizonEnd = ctx.horizonEnd; LocalDate horizonEnd = ctx.horizonEnd;
// 按维度创建 Period // 按维度创建 Period
switch (dimension) { switch (dimension) {
case "WEEK": case "WEEK":
...@@ -1152,6 +1182,12 @@ public class MacroPlannerDataConverter { ...@@ -1152,6 +1182,12 @@ public class MacroPlannerDataConverter {
case "MONTH": case "MONTH":
createMonthPeriods(baseDate, periodCount, horizonEnd); createMonthPeriods(baseDate, periodCount, horizonEnd);
break; break;
case "YEAR":
createYearPeriods(baseDate, periodCount, horizonEnd);
break;
case "QUARTER":
createQuarterPeriods(baseDate, periodCount, horizonEnd);
break;
case "DAY": case "DAY":
default: default:
createDayPeriods(baseDate, periodCount); createDayPeriods(baseDate, periodCount);
...@@ -1219,7 +1255,9 @@ public class MacroPlannerDataConverter { ...@@ -1219,7 +1255,9 @@ public class MacroPlannerDataConverter {
/** DAY 维度: 每天一个 Period */ /** DAY 维度: 每天一个 Period */
private void createDayPeriods(LocalDate baseDate, int periodCount) { private void createDayPeriods(LocalDate baseDate, int periodCount) {
for (int i = 0; i < periodCount; i++) { for (int i = 0; i < periodCount; i++) {
periods.add(new Period(i, formatDayName(baseDate.plusDays(i)), 1.0, baseDate.plusDays(i))); int index = Integer.parseInt(baseDate.plusDays(i).format(DateTimeFormatter.BASIC_ISO_DATE));
periods.add(new Period(index, formatDayName(baseDate.plusDays(i)), 1.0, baseDate.plusDays(i)));
} }
} }
...@@ -1233,8 +1271,10 @@ public class MacroPlannerDataConverter { ...@@ -1233,8 +1271,10 @@ public class MacroPlannerDataConverter {
if (weekEnd.isAfter(horizonEnd)) { if (weekEnd.isAfter(horizonEnd)) {
weekEnd = horizonEnd; weekEnd = horizonEnd;
} }
int index = Integer.parseInt(ws.format(DateTimeFormatter.BASIC_ISO_DATE));
long durationDays = ChronoUnit.DAYS.between(ws, weekEnd); long durationDays = ChronoUnit.DAYS.between(ws, weekEnd);
periods.add(new Period(i, "W" + (i + 1) + "(" + ws + ")", (double) durationDays, ws)); periods.add(new Period(index, formatWeekName(ws), (double) durationDays, ws));
} }
} }
...@@ -1248,17 +1288,70 @@ public class MacroPlannerDataConverter { ...@@ -1248,17 +1288,70 @@ public class MacroPlannerDataConverter {
monthEnd = horizonEnd; monthEnd = horizonEnd;
} }
long durationDays = java.time.temporal.ChronoUnit.DAYS.between(monthStart, monthEnd); long durationDays = java.time.temporal.ChronoUnit.DAYS.between(monthStart, monthEnd);
periods.add(new Period(i, formatMonthName(monthStart), (double) durationDays, monthStart)); int index = Integer.parseInt(monthStart.format(DateTimeFormatter.BASIC_ISO_DATE));
periods.add(new Period(index, formatMonthName(monthStart), (double) durationDays, monthStart));
cursor = cursor.plusMonths(1); cursor = cursor.plusMonths(1);
} }
} }
private void createYearPeriods(LocalDate baseDate, int periodCount, LocalDate horizonEnd) {
LocalDate cursor = baseDate.withDayOfYear(1);
for (int i = 0; i < periodCount; i++) {
LocalDate monthStart = cursor;
LocalDate monthEnd = cursor.plusYears(1);
if (monthEnd.isAfter(horizonEnd)) {
monthEnd = horizonEnd;
}
long durationDays = java.time.temporal.ChronoUnit.DAYS.between(monthStart, monthEnd);
int index = Integer.parseInt(monthStart.format(DateTimeFormatter.BASIC_ISO_DATE));
periods.add(new Period(index, formatYearName(monthStart), (double) durationDays, monthStart));
cursor = cursor.plusYears(1);
}
}
private void createQuarterPeriods(LocalDate baseDate, int periodCount, LocalDate horizonEnd) {
int month = baseDate.getMonthValue();
// 计算当前属于第几季度(1~4),整数除法自动取整
int quarter = (month - 1) / 3 + 1;
// 计算该季度的首月月份
int quarterStartMonth = (quarter - 1) * 3 + 1;
// 构造季度起始日期(首月1号)
LocalDate cursor = baseDate.withMonth(quarterStartMonth).withDayOfMonth(1);
for (int i = 0; i < periodCount; i++) {
LocalDate monthStart = cursor;
LocalDate monthEnd = cursor.plusMonths(3);
if (monthEnd.isAfter(horizonEnd)) {
monthEnd = horizonEnd;
}
long durationDays = java.time.temporal.ChronoUnit.DAYS.between(monthStart, monthEnd);
int index = Integer.parseInt(monthStart.format(DateTimeFormatter.BASIC_ISO_DATE));
periods.add(new Period(index, formatQuarterName(monthStart), (double) durationDays, monthStart));
cursor = cursor.plusMonths( 3);
}
}
private static String formatDayName(LocalDate date) { private static String formatDayName(LocalDate date) {
return String.format("D%02d(%02d-%02d)", date.getDayOfYear(), date.getMonthValue(), date.getDayOfMonth()); return String.format("D-%02d(%02d-%02d)", date.getDayOfYear(), date.getMonthValue(), date.getDayOfMonth());
}
private static String formatWeekName(LocalDate date) {
WeekFields weekFields = WeekFields.ISO;
int weekNo = date.get(weekFields.weekOfMonth());
return String.format("W-%02d(%02d-%02d)", date.getDayOfYear(), date.getMonthValue(), weekNo);
} }
private static String formatMonthName(LocalDate date) { private static String formatMonthName(LocalDate date) {
return String.format("M%d-%02d", date.getYear(), date.getMonthValue()); return String.format("M-%d-%02d", date.getYear(), date.getMonthValue());
}
private static String formatYearName(LocalDate date) {
return String.format("Y-%d", date.getYear());
}
private static String formatQuarterName(LocalDate date) {
int month = date.getMonthValue();
// 计算当前属于第几季度(1~4),整数除法自动取整
int quarter = (month - 1) / 3 + 1;
return String.format("Q-%d-%d", date.getYear(),quarter);
} }
// ---------- 步骤8: SalesDemand (从订单推导) ---------- // ---------- 步骤8: SalesDemand (从订单推导) ----------
......
...@@ -231,9 +231,9 @@ public class ResultWriter { ...@@ -231,9 +231,9 @@ public class ResultWriter {
writeLog("writeBomStructure"); writeLog("writeBomStructure");
writeBomStructure(sceneId); writeBomStructure(sceneId);
// 工序×物料×周期消耗量持久化 // 工序×物料×周期消耗量持久化
writeLog("writeOperationDemands"); // writeLog("writeOperationDemands");
List<OperationDemandResult> opDemands = buildOperationDemandResults(); // List<OperationDemandResult> opDemands = buildOperationDemandResults();
parquetUtil.write(opDemands, getOptimizationOperationDemand(sceneId), OperationDemandResult.class); // parquetUtil.write(opDemands, getOptimizationOperationDemand(sceneId), OperationDemandResult.class);
return true; return true;
// try { // try {
......
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