aps-modules/aps-core/src/main/java/com/aps/core/controller/ApsGasPipingRouteStatController.java
@@ -16,6 +16,7 @@ import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.Map; /** * 气体管路产能负载统计Controller @@ -62,7 +63,7 @@ /** * 获取气体管路产能负载统计详细信息 */ @Operation(summary = "获取气体管路产能负载统计详细信息", description = "根据id获取") @Operation(summary = "获取气体管路产能负载统计详细信息", description = "根据ID查询") @RequiresPermissions("aps:apsGasPipingRouteStat:query") @GetMapping(value = "/{id}") public AjaxResult getInfo(@PathVariable("id") String id) @@ -73,7 +74,7 @@ /** * 新增气体管路产能负载统计 */ @Operation(summary = "新增气体管路产能负载统计", description = "单个新增") @Operation(summary = "新增气体管路产能负载统计", description = "新增") @RequiresPermissions("aps:apsGasPipingRouteStat:add") @Log(title = "气体管路产能负载统计", businessType = BusinessType.INSERT) @PostMapping @@ -85,7 +86,7 @@ /** * 修改气体管路产能负载统计 */ @Operation(summary = "修改气体管路产能负载统计", description = "单个修改") @Operation(summary = "修改气体管路产能负载统计", description = "修改") @RequiresPermissions("aps:apsGasPipingRouteStat:edit") @Log(title = "气体管路产能负载统计", businessType = BusinessType.UPDATE) @PutMapping @@ -97,7 +98,7 @@ /** * 删除气体管路产能负载统计 */ @Operation(summary = "删除气体管路产能负载统计", description = "批量删除") @Operation(summary = "删除气体管路产能负载统计", description = "删除") @RequiresPermissions("aps:apsGasPipingRouteStat:remove") @Log(title = "气体管路产能负载统计", businessType = BusinessType.DELETE) @DeleteMapping("/{ids}") @@ -105,6 +106,29 @@ { return toAjax(apsGasPipingRouteStatService.deleteApsGasPipingRouteStatByIds(ids)); } /** * 生成气体管路产能负载统计数据 */ @Operation(summary = "生成气体管路产能负载统计数据", description = "根据预测数据和工单数据生成统计数据") @RequiresPermissions("aps:apsGasPipingRouteStat:generate") @Log(title = "气体管路产能负载统计", businessType = BusinessType.INSERT) @PostMapping("/generate") public AjaxResult generateGasPipingRouteStatData() { return toAjax(apsGasPipingRouteStatService.generateGasPipingRouteStatData()); } /** * 聚合气体管路产能负载统计数据 */ @Operation(summary = "聚合气体管路产能负载统计数据", description = "聚合统计数据,支持动态选择行维度(rowGroupBy)进行聚合,如按工序名称、车间、工厂、专业等") @RequiresPermissions("aps:apsGasPipingRouteStat:aggregate") @PostMapping("/aggregate") public AjaxResult aggregateGasPipingRouteStat(@RequestBody Map<String, Object> params) { return success(apsGasPipingRouteStatService.aggregateGasPipingRouteStat(params)); } @Operation(summary = "计算气体管路产能负载统计", description = "计算") @PostMapping("/computeCapacity") aps-modules/aps-core/src/main/java/com/aps/core/domain/ApsGasPipingRouteStat.java
@@ -61,17 +61,18 @@ private BigDecimal processTotalTime; /** 计划开工日 */ @Excel(name = "计划开工日", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @Excel(name = "计划开工日", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") @Schema(description = "计划开工日", type = "Date") private Date processPlanStartDay; /** 设计工时 */ @Excel(name = "设计工时") @Schema(description = "设计工时", type = "Long") private Long designTimes; @Schema(description = "设计工时", type = "BigDecimal") private BigDecimal designTimes; /** 删除标志(0代表存在 2代表删除) */ @Schema(description = "删除标志(0代表存在 2代表删除)", type = "String") @Schema(description = "删除标志", type = "String") private String delFlag; /** 工序名称 */ @@ -99,7 +100,7 @@ @Schema(description = "设计产能", type = "BigDecimal") private BigDecimal designCapacity; /** 设计产能 */ /** 专业 */ @Excel(name = "专业") @Schema(description = "专业", type = "String") private String major; @@ -119,13 +120,38 @@ @Schema(description = "日", type = "String") private String planStartDay; /** 计划完成日 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @Excel(name = "计划完成日", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") @Schema(description = "计划完成日", type = "Date") private Date processPlanEndDay; /** 订单计划完成日 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @Excel(name = "订单计划完成日", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") @Schema(description = "订单计划完成日", type = "Date") private Date orderPlanEndDay; /** 警告 */ @Excel(name = "警告") @Schema(description = "警告", type = "Boolean") private Boolean warning; /** 工厂 */ @Excel(name = "工厂") @Schema(description = "工厂", type = "String") private String plant; /** 车间 */ @Excel(name = "车间") @Schema(description = "车间", type = "String") private String workshop; /** 批次号 */ @Excel(name = "批次号") @Schema(description = "批次号", type = "String") private String batchNumber; /** 工厂 */ private String plant; @Transient private String searchStartDate; @@ -135,14 +161,8 @@ @Transient private String searchType; private Boolean warning; private Integer num; private BigDecimal routeProcessNumber; private Date orderPlanEndDay; /** 计划完成日 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") @Excel(name = "计划完工日", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss") private Date processPlanEndDay; @Override public String toString() { aps-modules/aps-core/src/main/java/com/aps/core/mapper/ApsGasPipingRouteStatMapper.java
@@ -4,6 +4,7 @@ import org.apache.ibatis.annotations.Mapper; import java.util.List; import java.util.Map; /** * 气体管路产能负载统计Mapper接口 @@ -69,7 +70,6 @@ */ public int deleteApsGasPipingRouteStatByBatchNum(String batchNumber); /** * 批量插入管路气柜产能数据 * @param apsGasPipingRouteStatList @@ -78,4 +78,41 @@ public int insertApsGasPipingRouteStatBatch(List<ApsGasPipingRouteStat> apsGasPipingRouteStatList); List<ApsGasPipingRouteStat> queryTempStat(); /** * 删除所有气体管路产能负载统计数据 * * @return 结果 */ public int deleteAll(); /** * 联合查询手工气体预测数据相关信息 * * @return 手工气体预测数据及关联信息 */ public List<Map<String, Object>> selectPredictionRouteData(); /** * 联合查询手工气体工单数据相关信息 * * @return 手工气体工单数据及关联信息 */ public List<Map<String, Object>> selectMoRouteData(); /** * 根据时间维度聚合查询统计数据 * * @param params 查询参数,包含时间范围和过滤条件 * @return 按工序名称和时间维度聚合的数据 */ public List<Map<String, Object>> selectAggregatedStatData(Map<String, Object> params); /** * 查询原始统计数据(不进行聚合) * * @param params 查询参数 * @return 原始统计数据列表 */ public List<Map<String, Object>> selectRawStatData(Map<String, Object> params); } aps-modules/aps-core/src/main/java/com/aps/core/service/IApsGasPipingRouteStatService.java
@@ -6,6 +6,7 @@ import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.Map; /** * 气体管路产能负载统计Service接口 @@ -88,4 +89,20 @@ @Transactional void saveGasPipingRoutStateList(); /** * 根据手工气体预测数据和手工气体工单数据生成统计数据 * * @return 结果 */ @Transactional public int generateGasPipingRouteStatData(); /** * 聚合气体管路产能负载统计数据 * * @param params 聚合参数 * @return 聚合结果 */ public Map<String, Object> aggregateGasPipingRouteStat(Map<String, Object> params); } aps-modules/aps-core/src/main/java/com/aps/core/service/impl/ApsGasPipingRouteStatServiceImpl.java
@@ -6,9 +6,7 @@ import com.aps.common.core.utils.uuid.IdUtils; import com.aps.common.security.utils.SecurityUtils; import com.aps.core.domain.*; import com.aps.core.mapper.ApsGasPipelineCapacityPlanMapper; import com.aps.core.mapper.ApsGasPipingPlanMapper; import com.aps.core.mapper.ApsGasPipingRouteStatMapper; import com.aps.core.mapper.*; import com.aps.core.service.IApsGasMaterialUsageService; import com.aps.core.service.IApsGasPipingRouteStatService; import com.aps.core.service.IApsStandardProcessService; @@ -1009,4 +1007,630 @@ log.info("批量插入数据完成,batchNum:"+batchNum); } /** * 根据手工气体预测数据和手工气体工单数据生成统计数据 * * @return 结果 */ @Override @Transactional public int generateGasPipingRouteStatData() { int count = 0; // 清空之前的数据 apsGasPipingRouteStatMapper.deleteAll(); // 生成批次号 String batchNumber = IdUtils.fastSimpleUUID(); List<ApsGasPipingRouteStat> statList = new ArrayList<>(); // 处理手工气体预测数据 - 使用联合查询获取所有相关数据 List<Map<String, Object>> predictionDataList = apsGasPipingRouteStatMapper.selectPredictionRouteData(); // 按物料代码分组,便于处理同一物料的不同工序 Map<String, List<Map<String, Object>>> predictionGroups = predictionDataList.stream() .collect(Collectors.groupingBy(data -> data.get("material_code").toString())); // 处理每个物料的工艺路线 for (String materialCode : predictionGroups.keySet()) { List<Map<String, Object>> materialData = predictionGroups.get(materialCode); // 按工序号排序 materialData.sort((a, b) -> { String numA = a.get("process_number").toString(); String numB = b.get("process_number").toString(); return numA.compareTo(numB); }); // 获取所有工序的信息并创建统计记录 List<ApsGasPipingRouteStat> processList = new ArrayList<>(); for (Map<String, Object> data : materialData) { ApsGasPipingRouteStat stat = new ApsGasPipingRouteStat(); stat.setId(IdUtils.fastSimpleUUID()); stat.setWorkOrderNo(""); // 预测数据没有工单号 stat.setItemNumber(data.get("material_code").toString()); stat.setProcessName(data.get("process_name").toString()); stat.setRoadProcessNumber(new BigDecimal(data.get("process_number").toString())); // 生产数量和标准工时 BigDecimal quantity = new BigDecimal(data.get("predict_quantity").toString()).setScale(4, RoundingMode.HALF_UP); BigDecimal standardTime = new BigDecimal(data.get("standard_time").toString()).setScale(4, RoundingMode.HALF_UP); stat.setProductionQuantity(quantity); stat.setStandardTime(standardTime); stat.setProcessTotalTime(standardTime.multiply(quantity).setScale(4, RoundingMode.HALF_UP)); // 工厂、专业和车间 stat.setPlant(data.get("factory").toString()); stat.setMajor(data.get("domain") != null ? data.get("domain").toString() : ""); stat.setWorkshop(data.get("workshop") != null ? data.get("workshop").toString() : ""); // 批次号和创建信息 stat.setBatchNumber(batchNumber); stat.setCreateBy(SecurityUtils.getUsername()); stat.setCreateTime(truncateToSeconds(DateUtils.getNowDate())); processList.add(stat); } // 计算计划完工日和计划开工日 if (!processList.isEmpty()) { int lastIndex = processList.size() - 1; // 最后一道工序的计划完工日期为预测日期 ApsGasPipingRouteStat lastProcess = processList.get(lastIndex); Date predictDate = (Date) materialData.get(lastIndex).get("predict_date"); lastProcess.setProcessPlanEndDay(truncateToSeconds(predictDate)); // 标记是否出现过时间冲突的情况 boolean hasTimeConflict = false; // 计算每个工序的计划开工日和完工日 - 从最后一道工序开始倒推 for (int i = lastIndex; i >= 0; i--) { ApsGasPipingRouteStat current = processList.get(i); // 如果当前是最后一道工序,计划开工日 = 计划完工日 - 工序总工时 if (i == lastIndex) { hasTimeConflict = calculateProcessPlanStartDay(current, hasTimeConflict); } else { // 非最后一道工序,计划完工日为下一道工序的计划开工日 ApsGasPipingRouteStat next = processList.get(i + 1); current.setProcessPlanEndDay(next.getProcessPlanStartDay()); // 如果已经出现时间冲突,后续工序的计划开工日和计划完工日都设为当前时间 if (hasTimeConflict) { current.setProcessPlanStartDay(truncateToSeconds(new Date())); } else { // 否则正常计算,并检查是否出现时间冲突 hasTimeConflict = calculateProcessPlanStartDay(current, hasTimeConflict); } } // 设置年、月、日 setDateComponents(current); } statList.addAll(processList); } } // 处理手工气体工单数据 - 使用联合查询获取所有相关数据 List<Map<String, Object>> moDataList = apsGasPipingRouteStatMapper.selectMoRouteData(); // 按工单号分组,便于处理同一工单的不同工序 Map<String, List<Map<String, Object>>> moGroups = moDataList.stream() .collect(Collectors.groupingBy(data -> data.get("work_order_no").toString())); // 处理每个工单的工艺路线 for (String workOrderNo : moGroups.keySet()) { List<Map<String, Object>> workOrderData = moGroups.get(workOrderNo); // 按工序号排序 workOrderData.sort((a, b) -> { String numA = a.get("process_number").toString(); String numB = b.get("process_number").toString(); return numA.compareTo(numB); }); // 获取所有工序的信息并创建统计记录 List<ApsGasPipingRouteStat> processList = new ArrayList<>(); for (Map<String, Object> data : workOrderData) { ApsGasPipingRouteStat stat = new ApsGasPipingRouteStat(); stat.setId(IdUtils.fastSimpleUUID()); stat.setWorkOrderNo(data.get("work_order_no").toString()); stat.setItemNumber(data.get("material_code").toString()); stat.setProcessName(data.get("process_name").toString()); stat.setRoadProcessNumber(new BigDecimal(data.get("process_number").toString())); // 生产数量和标准工时 BigDecimal quantity = new BigDecimal(data.get("quantity").toString()).setScale(4, RoundingMode.HALF_UP); BigDecimal standardTime = new BigDecimal(data.get("standard_time").toString()).setScale(4, RoundingMode.HALF_UP); stat.setProductionQuantity(quantity); stat.setStandardTime(standardTime); stat.setProcessTotalTime(standardTime.multiply(quantity).setScale(4, RoundingMode.HALF_UP)); // 工厂、专业和车间 stat.setPlant(data.get("factory").toString()); stat.setMajor(data.get("domain") != null ? data.get("domain").toString() : ""); stat.setWorkshop(data.get("workshop") != null ? data.get("workshop").toString() : ""); // 批次号和创建信息 stat.setBatchNumber(batchNumber); stat.setCreateBy(SecurityUtils.getUsername()); stat.setCreateTime(truncateToSeconds(DateUtils.getNowDate())); processList.add(stat); } // 计算计划完工日和计划开工日 if (!processList.isEmpty()) { int lastIndex = processList.size() - 1; // 最后一道工序的计划完工日期为工单计划完工日期 ApsGasPipingRouteStat lastProcess = processList.get(lastIndex); Date planEnd = (Date) workOrderData.get(0).get("plan_end"); lastProcess.setProcessPlanEndDay(truncateToSeconds(planEnd)); // 标记是否出现过时间冲突的情况 boolean hasTimeConflict = false; // 计算每个工序的计划开工日和完工日 - 从最后一道工序开始倒推 for (int i = lastIndex; i >= 0; i--) { ApsGasPipingRouteStat current = processList.get(i); // 如果当前是最后一道工序,计划开工日 = 计划完工日 - 工序总工时 if (i == lastIndex) { hasTimeConflict = calculateProcessPlanStartDay(current, hasTimeConflict); } else { // 非最后一道工序,计划完工日为下一道工序的计划开工日 ApsGasPipingRouteStat next = processList.get(i + 1); current.setProcessPlanEndDay(next.getProcessPlanStartDay()); // 如果已经出现时间冲突,后续工序的计划开工日和计划完工日都设为当前时间 if (hasTimeConflict) { current.setProcessPlanStartDay(truncateToSeconds(new Date())); } else { // 否则正常计算,并检查是否出现时间冲突 hasTimeConflict = calculateProcessPlanStartDay(current, hasTimeConflict); } } // 设置年、月、日 setDateComponents(current); } statList.addAll(processList); } } // 批量插入数据 if (!statList.isEmpty()) { for (int i = 0; i < statList.size(); i += 500) { int endIndex = Math.min(i + 500, statList.size()); List<ApsGasPipingRouteStat> batch = statList.subList(i, endIndex); count += apsGasPipingRouteStatMapper.insertApsGasPipingRouteStatBatch(batch); } } return count; } /** * 计算工序计划开工日,并判断是否发生时间冲突 * * @param stat 当前工序统计对象 * @param hasTimeConflict 之前是否已经发生时间冲突 * @return 是否发生时间冲突 */ private boolean calculateProcessPlanStartDay(ApsGasPipingRouteStat stat, boolean hasTimeConflict) { Date planEndDay = stat.getProcessPlanEndDay(); if (planEndDay == null) { return hasTimeConflict; } // 计算工序总工时对应的秒数 long processTotalTimeSeconds = stat.getProcessTotalTime() .multiply(BigDecimal.valueOf(60 * 60)) .longValue(); // 获取当前时间并精确到秒级别 Date now = truncateToSeconds(new Date()); // 计算开工日期并精确到秒级别 Calendar calendar = Calendar.getInstance(); calendar.setTime(planEndDay); // 分批减去秒数,避免int溢出 long seconds = processTotalTimeSeconds; while (seconds > 0) { int step = (seconds > Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int) seconds; calendar.add(Calendar.SECOND, -step); seconds -= step; } Date calculatedStartDay = truncateToSeconds(calendar.getTime()); // 如果计划完工日期小于等于当前时间,则计划开工日也设为计划完工日(当前时间) if (planEndDay.compareTo(now) <= 0) { stat.setProcessPlanStartDay(truncateToSeconds(planEndDay)); return true; // 发生时间冲突 } // 如果计算出的计划开工日小于等于当前时间,则设为当前时间 else if (calculatedStartDay.compareTo(now) <= 0) { stat.setProcessPlanStartDay(now); return true; // 首次出现时间冲突 } // 否则正常设置计划开工日 else { stat.setProcessPlanStartDay(calculatedStartDay); return hasTimeConflict; // 保持原有冲突状态 } } /** * 将日期精确到秒级别,去除毫秒信息 * * @param date 原始日期 * @return 精确到秒的日期 */ private Date truncateToSeconds(Date date) { if (date == null) { return null; } Calendar calendar = Calendar.getInstance(); calendar.setTime(date); calendar.set(Calendar.MILLISECOND, 0); return calendar.getTime(); } /** * 设置日期相关组件(年、月、日) */ private void setDateComponents(ApsGasPipingRouteStat stat) { if (stat.getProcessPlanStartDay() != null) { SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy"); SimpleDateFormat monthFormat = new SimpleDateFormat("MM"); SimpleDateFormat dayFormat = new SimpleDateFormat("dd"); stat.setPlanStartYear(yearFormat.format(stat.getProcessPlanStartDay())); stat.setPlanStartMonth(monthFormat.format(stat.getProcessPlanStartDay())); stat.setPlanStartDay(dayFormat.format(stat.getProcessPlanStartDay())); } } /** * 聚合气体管路产能负载统计数据 * 在Service层完成聚合处理,支持多维度分组 * * @param params 聚合参数 * @return 聚合结果 */ @Override public Map<String, Object> aggregateGasPipingRouteStat(Map<String, Object> params) { Map<String, Object> result = new HashMap<>(); List<Map<String, Object>> plantTable = new ArrayList<>(); // 获取时间颗粒度参数 String timeGranularity = params.containsKey("timeGranularity") ? (String) params.get("timeGranularity") : "day"; // 默认为"日"粒度 // 获取row维度的聚合字段 - 默认为工序名称(processName) String rowGroupBy = params.containsKey("rowGroupBy") ? (String) params.get("rowGroupBy") : "processName"; // 确定分组维度 - 工厂、专业、车间 boolean groupByPlant = params.containsKey("groupByPlant") && Boolean.TRUE.equals(params.get("groupByPlant")); boolean groupByMajor = params.containsKey("groupByMajor") && Boolean.TRUE.equals(params.get("groupByMajor")); boolean groupByWorkshop = params.containsKey("groupByWorkshop") && Boolean.TRUE.equals(params.get("groupByWorkshop")); // 如果rowGroupBy已经是某个维度,则该维度不需要再次分组 if ("plant".equals(rowGroupBy)) groupByPlant = false; if ("major".equals(rowGroupBy)) groupByMajor = false; if ("workshop".equals(rowGroupBy)) groupByWorkshop = false; // 构建查询条件 Map<String, Object> queryParams = new HashMap<>(); // 设置查询条件 - 分类条件 if (params.containsKey("plant")) { Object plantParam = params.get("plant"); if (plantParam instanceof List) { queryParams.put("plants", plantParam); } else if (plantParam instanceof String) { // 处理可能的逗号分隔字符串 String plantStr = (String) plantParam; if (plantStr.contains(",")) { List<String> plantList = Arrays.asList(plantStr.split(",")); queryParams.put("plants", plantList); } else { queryParams.put("plant", plantStr); } } } if (params.containsKey("major")) { Object majorParam = params.get("major"); if (majorParam instanceof List) { queryParams.put("majors", majorParam); } else if (majorParam instanceof String) { // 处理可能的逗号分隔字符串 String majorStr = (String) majorParam; if (majorStr.contains(",")) { List<String> majorList = Arrays.asList(majorStr.split(",")); queryParams.put("majors", majorList); } else { queryParams.put("major", majorStr); } } } if (params.containsKey("workshop")) { Object workshopParam = params.get("workshop"); if (workshopParam instanceof List) { queryParams.put("workshops", workshopParam); } else if (workshopParam instanceof String) { // 处理可能的逗号分隔字符串 String workshopStr = (String) workshopParam; if (workshopStr.contains(",")) { List<String> workshopList = Arrays.asList(workshopStr.split(",")); queryParams.put("workshops", workshopList); } else { queryParams.put("workshop", workshopStr); } } } // 确定时间范围 List<String> timePoints = new ArrayList<>(); if ("day".equalsIgnoreCase(timeGranularity)) { // "日"粒度:传入一个月份,聚合该月每一天的数据 if (!params.containsKey("yearMonth")) { result.put("plantTable", plantTable); result.put("message", "日粒度聚合需要指定yearMonth参数"); return result; } String yearMonth = (String) params.get("yearMonth"); // 生成指定月份中的每一天 YearMonth ym = YearMonth.parse(yearMonth); int daysInMonth = ym.lengthOfMonth(); for (int day = 1; day <= daysInMonth; day++) { String dayStr = String.format("%02d", day); timePoints.add(yearMonth + "-" + dayStr); } // 设置查询参数 String[] ymParts = yearMonth.split("-"); queryParams.put("yearStart", ymParts[0]); queryParams.put("monthStart", ymParts[1]); queryParams.put("yearEnd", ymParts[0]); queryParams.put("monthEnd", ymParts[1]); } else if ("month".equalsIgnoreCase(timeGranularity)) { // "月"粒度:传入一个时间区间,聚合该区间内每个月的数据 if (!params.containsKey("startDate") || !params.containsKey("endDate")) { result.put("plantTable", plantTable); result.put("message", "月粒度聚合需要指定startDate和endDate参数"); return result; } String startDate = (String) params.get("startDate"); String endDate = (String) params.get("endDate"); // 解析开始和结束年月 YearMonth start = YearMonth.parse(startDate); YearMonth end = YearMonth.parse(endDate); // 生成区间内的每个月 timePoints = getYearMonthsInRange(start, end); // 设置查询参数 String[] startParts = startDate.split("-"); String[] endParts = endDate.split("-"); queryParams.put("yearStart", startParts[0]); queryParams.put("monthStart", startParts[1]); queryParams.put("yearEnd", endParts[0]); queryParams.put("monthEnd", endParts[1]); } if (timePoints.isEmpty()) { result.put("plantTable", plantTable); result.put("message", "未能生成有效的时间点列表"); return result; } // 查询原始数据(不依赖数据库聚合) List<Map<String, Object>> rawData = apsGasPipingRouteStatMapper.selectRawStatData(queryParams); if (rawData.isEmpty()) { result.put("plantTable", plantTable); result.put("timePoints", timePoints); return result; } // 在Service层完成聚合 // 使用组合key来实现多维度分组(动态rowGroupBy字段 + 可选的工厂/专业/车间) Map<String, Map<String, Object>> groupInfoMap = new HashMap<>(); Map<String, Map<String, BigDecimal>> groupTimeDataMap = new HashMap<>(); // 遍历原始数据,按多维度分组进行聚合 for (Map<String, Object> data : rawData) { // 过滤工序名称为空的数据 String processName = getStringValue(data, "processName"); if (processName == null || processName.trim().isEmpty()) { log.warn("跳过处理:工序名称为空"); continue; } // 过滤车间为空的数据 String workshop = getStringValue(data, "workshop"); if (workshop == null || workshop.trim().isEmpty()) { log.warn("跳过处理:车间为空,工序名称={}", processName); continue; } // 获取行分组字段值 String rowGroupValue = getStringValue(data, rowGroupBy); if (rowGroupValue == null) { log.warn("跳过处理:{} 字段值为null", rowGroupBy); continue; } // 处理开工日期 Date processPlanStartDay = (Date) data.get("processPlanStartDay"); if (processPlanStartDay == null) { log.warn("跳过处理:计划开工日为null, {}={}", rowGroupBy, rowGroupValue); continue; } // 构建分组键 - 基于row分组字段和可选的其他维度 String plant = getStringValue(data, "plant"); String major = getStringValue(data, "major"); StringBuilder groupKeyBuilder = new StringBuilder(rowGroupValue); // 根据用户选择的分组维度添加到分组键 if (groupByPlant && plant != null) { groupKeyBuilder.append("_PLANT_").append(plant); } if (groupByMajor && major != null) { groupKeyBuilder.append("_MAJOR_").append(major); } if (groupByWorkshop && workshop != null) { groupKeyBuilder.append("_WORKSHOP_").append(workshop); } String groupKey = groupKeyBuilder.toString(); // 记录分组的基本信息(只记录一次) if (!groupInfoMap.containsKey(groupKey)) { Map<String, Object> groupInfo = new HashMap<>(); groupInfo.put(rowGroupBy, rowGroupValue); groupInfo.put("plant", plant); groupInfo.put("major", major); groupInfo.put("workshop", workshop); groupInfo.put("processName", processName); groupInfoMap.put(groupKey, groupInfo); } // 计算时间点Key String timeKey; LocalDate planStartLocalDate = processPlanStartDay.toInstant().atZone(ZoneId.systemDefault()).toLocalDate(); if ("day".equalsIgnoreCase(timeGranularity)) { // 日粒度: 2025-05-25 timeKey = planStartLocalDate.format(DateTimeFormatter.ISO_LOCAL_DATE); } else { // 月粒度: 2025-05 timeKey = planStartLocalDate.getYear() + "-" + String.format("%02d", planStartLocalDate.getMonthValue()); } // 获取该分组的时间点映射,如果不存在则创建 if (!groupTimeDataMap.containsKey(groupKey)) { groupTimeDataMap.put(groupKey, new HashMap<>()); } Map<String, BigDecimal> timeMap = groupTimeDataMap.get(groupKey); // 累加该分组在该时间点的工时数据 BigDecimal processTotalTime = getBigDecimalValue(data, "processTotalTime"); if (processTotalTime != null) { BigDecimal currentTotal = timeMap.getOrDefault(timeKey, BigDecimal.ZERO); timeMap.put(timeKey, currentTotal.add(processTotalTime)); } } // 构建最终返回的数据结构 for (String groupKey : groupInfoMap.keySet()) { Map<String, Object> rowEntry = new HashMap<>(); Map<String, Object> rowDetail = new HashMap<>(); // 获取该分组的基本信息 Map<String, Object> groupInfo = groupInfoMap.get(groupKey); String rowGroupValue = (String) groupInfo.get(rowGroupBy); // 添加分组基本信息 if (groupByPlant) { rowDetail.put("plant", groupInfo.get("plant")); } if (groupByMajor) { rowDetail.put("major", groupInfo.get("major")); } if (groupByWorkshop) { rowDetail.put("workshop", groupInfo.get("workshop")); } // 保留工序名称信息,以便前端展示 if (!"processName".equals(rowGroupBy)) { rowDetail.put("processName", groupInfo.get("processName")); } // 添加时间数据 List<Map<String, Object>> timeDataList = new ArrayList<>(); Map<String, BigDecimal> timeMap = groupTimeDataMap.getOrDefault(groupKey, new HashMap<>()); for (String timePoint : timePoints) { Map<String, Object> pointData = new HashMap<>(); pointData.put("planDay", timePoint); pointData.put("designTimes", 0); // 设计工时暂时给0 // 获取该时间点的需求工时,如果不存在则设为0 BigDecimal requireTimes = timeMap.getOrDefault(timePoint, BigDecimal.ZERO); pointData.put("requireTimes", requireTimes); // 计算产能负荷(暂时为0) pointData.put("capacityLoad", 0); timeDataList.add(pointData); } rowDetail.put("timeData", timeDataList); rowEntry.put(rowGroupValue, rowDetail); plantTable.add(rowEntry); } result.put("plantTable", plantTable); result.put("timePoints", timePoints); result.put("rowGroupBy", rowGroupBy); return result; } /** * 从Map中安全获取String值 */ private String getStringValue(Map<String, Object> data, String key) { Object value = data.get(key); if (value == null) { return null; } return value.toString(); } /** * 从Map中安全获取BigDecimal值 */ private BigDecimal getBigDecimalValue(Map<String, Object> data, String key) { Object value = data.get(key); if (value == null) { return null; } if (value instanceof BigDecimal) { return (BigDecimal) value; } else if (value instanceof Number) { return new BigDecimal(((Number) value).toString()); } else if (value instanceof String) { try { return new BigDecimal((String) value); } catch (NumberFormatException e) { log.warn("无法将值转换为BigDecimal: {}", value); return null; } } return null; } } aps-modules/aps-core/src/main/java/com/aps/core/service/impl/ApsMaterialStorageManagementServiceImpl.java
@@ -7,6 +7,7 @@ import com.aps.core.domain.ApsMaterialStorageManagement; import com.aps.core.mapper.ApsMaterialStorageManagementMapper; import com.aps.core.service.IApsMaterialStorageManagementService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; @@ -21,6 +22,7 @@ * @date 2025-04-17 */ @Service @Slf4j public class ApsMaterialStorageManagementServiceImpl implements IApsMaterialStorageManagementService { @Autowired @@ -150,6 +152,7 @@ @Override public boolean setStorageDataToRedis(String orgCode) { try { log.info("开始同步 物料库存至Redis!"); Set<String> keys = redisTemplate.keys("MaterialStorage:Material_*"); if (keys != null && !keys.isEmpty()) { redisTemplate.delete(keys); @@ -169,14 +172,12 @@ bulkData.put("MaterialStorage:Material_"+apsMaterialStorageManagement.getApplicableFactories()+"_"+apsMaterialStorageManagement.getItemNumber(), jsonObject); }); redisTemplate.opsForValue().multiSet(bulkData); log.info("完成同步 物料库存至Redis!"); } catch (Exception e) { e.printStackTrace(); log.error("同步物料库存至Redis失败!"+e.getMessage()); return false; } // Set<String> keys = redisTemplate.keys("MaterialStorage:Material_*"); // if (keys != null && !keys.isEmpty()) { // redisTemplate.delete(keys); // } return true; } aps-modules/aps-core/src/main/java/com/aps/core/service/impl/ApsPlate/ApsPlatePlanServiceImpl.java
@@ -13,6 +13,7 @@ import com.aps.core.mapper.ApsPlatePlanTempMapper; import com.aps.core.service.ApsPlate.IApsPlatePlanService; import com.aps.system.api.domain.SysDictData; import lombok.extern.slf4j.Slf4j; import org.apache.logging.log4j.util.Strings; import org.springframework.beans.BeanUtils; import org.springframework.beans.factory.annotation.Autowired; @@ -29,6 +30,7 @@ * @author ruoyi * @date 2025-04-08 */ @Slf4j @Service public class ApsPlatePlanServiceImpl implements IApsPlatePlanService { @@ -221,6 +223,7 @@ @Override public boolean setSubPlansToRedis() { try { log.info("开始设置钣金子计划到redis"); Set<String> keys = redisTemplate.keys("PLATE_SUB_PLAN:*"); if (keys != null && !keys.isEmpty()) { redisTemplate.delete(keys); @@ -235,9 +238,11 @@ bulkData.put("PLATE_SUB_PLAN:"+key, value); } redisTemplate.opsForValue().multiSet(bulkData); log.info("设置钣金子计划到redis完成"); return true; } catch (Exception e) { e.printStackTrace(); log.error("设置钣金子计划到redis失败"+e.getMessage()); return false; } } aps-modules/aps-core/src/main/java/com/aps/core/service/impl/ApsStandardProcessRouteHeaderServiceImpl.java
@@ -5,6 +5,7 @@ import com.aps.core.domain.ApsStandardProcessRouteHeader; import com.aps.core.mapper.ApsStandardProcessRouteHeaderMapper; import com.aps.core.service.IApsStandardProcessRouteHeaderService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; @@ -21,6 +22,7 @@ * @date 2025-05-06 */ @Service @Slf4j public class ApsStandardProcessRouteHeaderServiceImpl implements IApsStandardProcessRouteHeaderService { @Autowired @@ -106,6 +108,7 @@ @Override public boolean setProcessRouteDataToRedis(String orgCode) { try { log.info("开始同步标准工艺路线数据至Redis"); Set<String> keys = redisTemplate.keys("ROUTE:ROUTE_*"); if (keys != null && !keys.isEmpty()) { redisTemplate.delete(keys); @@ -116,9 +119,11 @@ bulkData.put("ROUTE:ROUTE_"+jsonObject.getString("org_code")+"_"+jsonObject.getString("item_code"), jsonObject.getBigDecimal("standardtime")); } redisTemplate.opsForValue().multiSet(bulkData); log.info("完成同步标准工艺路线数据至Redis"); return true; } catch (Exception e) { e.printStackTrace(); log.error("同步标准工艺路线数据至Redis失败"+e.getMessage()); return false; } } aps-modules/aps-core/src/main/resources/mapper/core/ApsGasPipingRouteStatMapper.xml
@@ -28,8 +28,10 @@ <result property="planStartDay" column="plan_start_day" /> <result property="processPlanEndDay" column="process_plan_end_day" /> <result property="orderPlanEndDay" column="order_plan_end_day" /> <result property="routeProcessNumber" column="route_process_number" /> <result property="warning" column="warning" /> <result property="plant" column="plant" /> <result property="workshop" column="workshop" /> <result property="batchNumber" column="batch_number" /> </resultMap> <sql id="selectApsGasPipingRouteStatVo"> @@ -38,27 +40,27 @@ process_plan_start_day, design_times, del_flag, create_by, process_name, create_time, item_number, standard_dosage, process_total_dosage, design_capacity, major, plan_start_year, plan_start_month, plan_start_day , warning, plant plan_start_month, plan_start_day, process_plan_end_day, order_plan_end_day, warning, plant, workshop, batch_number from aps_gas_piping_route_stat </sql> <select id="selectApsGasPipingRouteStatList" parameterType="com.aps.core.domain.ApsGasPipingRouteStat" resultMap="ApsGasPipingRouteStatResult"> <include refid="selectApsGasPipingRouteStatVo"/> <where> <where> <if test="workOrderNo != null and workOrderNo != ''"> and work_order_no = #{workOrderNo}</if> <if test="roadProcessNumber != null and roadProcessNumber != ''"> and road_process_number = #{roadProcessNumber}</if> <if test="currentProcessNumber != null and currentProcessNumber != ''"> and current_process_number = #{currentProcessNumber}</if> <if test="productionQuantity != null and productionQuantity != ''"> and production_quantity = #{productionQuantity}</if> <if test="standardTime != null and standardTime != ''"> and standard_time = #{standardTime}</if> <if test="processTotalTime != null and processTotalTime != ''"> and process_total_time = #{processTotalTime}</if> <if test="processPlanStartDay != null and processPlanStartDay != ''"> and process_plan_start_day = #{processPlanStartDay}</if> <if test="roadProcessNumber != null "> and road_process_number = #{roadProcessNumber}</if> <if test="currentProcessNumber != null "> and current_process_number = #{currentProcessNumber}</if> <if test="productionQuantity != null "> and production_quantity = #{productionQuantity}</if> <if test="standardTime != null "> and standard_time = #{standardTime}</if> <if test="processTotalTime != null "> and process_total_time = #{processTotalTime}</if> <if test="processPlanStartDay != null "> and process_plan_start_day = #{processPlanStartDay}</if> <if test="designTimes != null "> and design_times = #{designTimes}</if> <if test="processName != null and processName != ''"> and process_name like '%' || #{processName} || '%')</if> <if test="major != null and major != ''"> and major = #{major} </if> <if test="planStartYear != null and planStartYear != ''"> and plan_start_year = #{planStartYear} </if> <if test="planStartMonth != null and planStartMonth != ''"> and plan_start_month = #{planStartMonth} </if> <if test="planStartDay != null and planStartDay != ''"> and plan_start_day = #{planStartDay} </if> <if test="searchStartDate != null and searchStartDate != '' and searchEndDate != null and searchEndDate != ''"> and (process_plan_start_day >= #{searchStartDate} and process_plan_start_day <= #{searchEndDate}) </if> <if test="processName != null and processName != ''"> and process_name = #{processName}</if> <if test="itemNumber != null and itemNumber != ''"> and item_number = #{itemNumber}</if> <if test="standardDosage != null "> and standard_dosage = #{standardDosage}</if> <if test="processTotalDosage != null "> and process_total_dosage = #{processTotalDosage}</if> <if test="designCapacity != null "> and design_capacity = #{designCapacity}</if> <if test="major != null and major != ''"> <if test="major == 'piping'"> and process_name in (select process_name from aps_standard_process where major='管路') @@ -66,8 +68,19 @@ <if test="major == 'gas'"> and process_name in (select process_name from aps_standard_process where major='气柜') </if> <if test="major != 'piping' and major != 'gas'"> and major = #{major} </if> </if> <if test="plant != null and plant != ''"> and plant = #{plant} </if> <if test="planStartYear != null and planStartYear != ''"> and plan_start_year = #{planStartYear}</if> <if test="planStartMonth != null and planStartMonth != ''"> and plan_start_month = #{planStartMonth}</if> <if test="planStartDay != null and planStartDay != ''"> and plan_start_day = #{planStartDay}</if> <if test="processPlanEndDay != null "> and process_plan_end_day = #{processPlanEndDay}</if> <if test="orderPlanEndDay != null "> and order_plan_end_day = #{orderPlanEndDay}</if> <if test="warning != null "> and warning = #{warning}</if> <if test="plant != null and plant != ''"> and plant = #{plant}</if> <if test="workshop != null and workshop != ''"> and workshop = #{workshop}</if> <if test="batchNumber != null and batchNumber != ''"> and batch_number = #{batchNumber}</if> </where> </select> @@ -81,12 +94,12 @@ <trim prefix="(" suffix=")" suffixOverrides=","> <if test="id != null">id,</if> <if test="workOrderNo != null">work_order_no,</if> <if test="roadProcessNumber != null and roadProcessNumber != ''">road_process_number,</if> <if test="roadProcessNumber != null">road_process_number,</if> <if test="currentProcessNumber != null">current_process_number,</if> <if test="productionQuantity != null">production_quantity,</if> <if test="standardTime != null">standard_time,</if> <if test="processTotalTime != null">process_total_time,</if> <if test="processPlanStartDay != null ">process_plan_start_day,</if> <if test="processPlanStartDay != null">process_plan_start_day,</if> <if test="designTimes != null">design_times,</if> <if test="delFlag != null">del_flag,</if> <if test="createBy != null">create_by,</if> @@ -102,18 +115,20 @@ <if test="planStartDay != null">plan_start_day,</if> <if test="processPlanEndDay != null">process_plan_end_day,</if> <if test="orderPlanEndDay != null">order_plan_end_day,</if> <if test="batchNumber != null">batch_number,</if> <if test="warning != null">warning,</if> <if test="plant != null">plant,</if> <if test="workshop != null">workshop,</if> <if test="batchNumber != null">batch_number,</if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="id != null">#{id},</if> <if test="workOrderNo != null">#{workOrderNo},</if> <if test="roadProcessNumber != null and roadProcessNumber != ''">#{roadProcessNumber},</if> <if test="roadProcessNumber != null">#{roadProcessNumber},</if> <if test="currentProcessNumber != null">#{currentProcessNumber},</if> <if test="productionQuantity != null">#{productionQuantity},</if> <if test="standardTime != null">#{standardTime},</if> <if test="processTotalTime != null">#{processTotalTime},</if> <if test="processPlanStartDay != null ">#{processPlanStartDay},</if> <if test="processPlanStartDay != null">#{processPlanStartDay},</if> <if test="designTimes != null">#{designTimes},</if> <if test="delFlag != null">#{delFlag},</if> <if test="createBy != null">#{createBy},</if> @@ -129,8 +144,10 @@ <if test="planStartDay != null">#{planStartDay},</if> <if test="processPlanEndDay != null">#{processPlanEndDay},</if> <if test="orderPlanEndDay != null">#{orderPlanEndDay},</if> <if test="batchNumber != null">#{batchNumber},</if> <if test="warning != null">#{warning},</if> <if test="plant != null">#{plant},</if> <if test="workshop != null">#{workshop},</if> <if test="batchNumber != null">#{batchNumber},</if> </trim> </insert> @@ -138,12 +155,12 @@ update aps_gas_piping_route_stat <trim prefix="SET" suffixOverrides=","> <if test="workOrderNo != null">work_order_no = #{workOrderNo},</if> <if test="roadProcessNumber != null and roadProcessNumber != ''">road_process_number = #{roadProcessNumber},</if> <if test="roadProcessNumber != null">road_process_number = #{roadProcessNumber},</if> <if test="currentProcessNumber != null">current_process_number = #{currentProcessNumber},</if> <if test="productionQuantity != null">production_quantity = #{productionQuantity},</if> <if test="standardTime != null">standard_time = #{standardTime},</if> <if test="processTotalTime != null">process_total_time = #{processTotalTime},</if> <if test="processPlanStartDay != null and processPlanStartDay != ''">process_plan_start_day = #{processPlanStartDay},</if> <if test="processPlanStartDay != null">process_plan_start_day = #{processPlanStartDay},</if> <if test="designTimes != null">design_times = #{designTimes},</if> <if test="delFlag != null">del_flag = #{delFlag},</if> <if test="createBy != null">create_by = #{createBy},</if> @@ -157,6 +174,12 @@ <if test="planStartYear != null">plan_start_year = #{planStartYear},</if> <if test="planStartMonth != null">plan_start_month = #{planStartMonth},</if> <if test="planStartDay != null">plan_start_day = #{planStartDay},</if> <if test="processPlanEndDay != null">process_plan_end_day = #{processPlanEndDay},</if> <if test="orderPlanEndDay != null">order_plan_end_day = #{orderPlanEndDay},</if> <if test="warning != null">warning = #{warning},</if> <if test="plant != null">plant = #{plant},</if> <if test="workshop != null">workshop = #{workshop},</if> <if test="batchNumber != null">batch_number = #{batchNumber},</if> </trim> where id = #{id} </update> @@ -171,31 +194,37 @@ #{id} </foreach> </delete> <delete id="deleteApsGasPipingRouteStatByBatchNum" parameterType="String"> delete from aps_gas_piping_route_stat where batch_number != #{batchNumber} </delete> <insert id="insertApsGasPipingRouteStatBatch"> INSERT INTO aps_gas_piping_route_stat ( id,work_order_no, road_process_number, current_process_number, production_quantity, standard_time, process_total_time, process_plan_start_day, design_times, del_flag, create_by, process_name, create_time, item_number, standard_dosage, process_total_dosage, design_capacity, major, plan_start_year, plan_start_month, plan_start_day,batch_number,process_plan_end_day,warning,order_plan_end_day,plant ) VALUES <foreach collection="apsGasPipingRouteStatList" item="stat" separator=","> <insert id="insertApsGasPipingRouteStatBatch" parameterType="java.util.List"> insert into aps_gas_piping_route_stat ( id, work_order_no, road_process_number, current_process_number, production_quantity, standard_time, process_total_time, process_plan_start_day, design_times, del_flag, create_by, process_name, create_time, item_number, standard_dosage, process_total_dosage, design_capacity, major, plan_start_year, plan_start_month, plan_start_day, process_plan_end_day, order_plan_end_day, warning, plant, workshop, batch_number ) values <foreach item="item" index="index" collection="list" separator=","> ( #{stat.id},#{stat.workOrderNo}, #{stat.roadProcessNumber}, #{stat.currentProcessNumber}, #{stat.productionQuantity}, #{stat.standardTime}, #{stat.processTotalTime}, #{stat.processPlanStartDay}, #{stat.designTimes}, #{stat.delFlag}, #{stat.createBy}, #{stat.processName}, #{stat.createTime}, #{stat.itemNumber}, #{stat.standardDosage}, #{stat.processTotalDosage}, #{stat.designCapacity}, #{stat.major}, #{stat.planStartYear}, #{stat.planStartMonth}, #{stat.planStartDay}, #{stat.batchNumber}, #{stat.processPlanEndDay},#{stat.warning},#{stat.orderPlanEndDay},#{stat.plant} #{item.id}, #{item.workOrderNo}, #{item.roadProcessNumber}, #{item.currentProcessNumber}, #{item.productionQuantity}, #{item.standardTime}, #{item.processTotalTime}, #{item.processPlanStartDay}, #{item.designTimes}, #{item.delFlag}, #{item.createBy}, #{item.processName}, #{item.createTime}, #{item.itemNumber}, #{item.standardDosage}, #{item.processTotalDosage}, #{item.designCapacity}, #{item.major}, #{item.planStartYear}, #{item.planStartMonth}, #{item.planStartDay}, #{item.processPlanEndDay}, #{item.orderPlanEndDay}, #{item.warning}, #{item.plant}, #{item.workshop}, #{item.batchNumber} ) </foreach> </insert> <delete id="deleteAll"> delete from aps_gas_piping_route_stat </delete> <select id="queryTempStat" resultMap="ApsGasPipingRouteStatResult" > select row_number() over (partition by rt.work_order_no order by rt.process_number desc ) as num, @@ -219,5 +248,155 @@ where pl.document_number is not null and rt.work_order_no is not null and pl.plan_end_day is not null order by rt.work_order_no asc, rt.process_number desc </select> <!-- 联合查询手工气体预测数据相关信息 --> <select id="selectPredictionRouteData" resultType="java.util.Map"> SELECT p.id as prediction_id, p.material_code, p.factory, p.predict_quantity, p.predict_date, h.route_id, l.route_name as process_name, l.route_num as process_number, l.standard_time, m.domain, s.work_shop as workshop FROM aps_gas_pipeline_prediction p JOIN aps_standard_process_route_header h ON p.material_code = h.item_code AND p.factory = h.org_code JOIN aps_standard_process_route_line l ON h.route_id = l.route_id LEFT JOIN aps_material_product_group_management m ON p.material_code = m.material_code AND p.factory = m.factory LEFT JOIN aps_standard_process s ON l.route_name = s.process_name ORDER BY p.material_code, l.route_num </select> <!-- 联合查询手工气体工单数据相关信息 --> <select id="selectMoRouteData" resultType="java.util.Map"> SELECT mo.id as mo_id, mo.mo as work_order_no, mo.material_code, mo.factory, mo.quantity, mo.plan_end, r.process_name, r.process_number, r.standard_time, m.domain, s.work_shop as workshop FROM aps_gas_pipeline_mo mo JOIN aps_process_route r ON mo.mo = r.work_order_no LEFT JOIN aps_material_product_group_management m ON mo.material_code = m.material_code AND mo.factory = m.factory LEFT JOIN aps_standard_process s ON r.process_name = s.process_name ORDER BY mo.mo, r.process_number </select> <!-- 聚合查询统计数据 - 按工序名称和时间聚合 --> <select id="selectAggregatedStatData" parameterType="java.util.Map" resultType="java.util.Map"> SELECT process_name AS "processName", plan_start_year AS "year", plan_start_month AS "month", <choose> <when test="timeGranularity != null and timeGranularity == 'day'"> plan_start_day AS "day" </when> <otherwise> '01' AS "day" </otherwise> </choose>, plant AS "plant", major AS "major", workshop AS "workshop", SUM(process_total_time) AS "totalProcessTime" FROM aps_gas_piping_route_stat <where> <if test="yearStart != null and monthStart != null"> (plan_start_year > #{yearStart} OR (plan_start_year = #{yearStart} AND plan_start_month >= #{monthStart})) AND (plan_start_year < #{yearEnd} OR (plan_start_year = #{yearEnd} AND plan_start_month <= #{monthEnd})) </if> <if test="plant != null and plant != ''"> AND plant = #{plant} </if> <if test="major != null and major != ''"> AND major = #{major} </if> <if test="workshop != null and workshop != ''"> AND workshop = #{workshop} </if> </where> GROUP BY process_name, plan_start_year, plan_start_month <if test="timeGranularity != null and timeGranularity == 'day'"> , plan_start_day </if>, plant, major, workshop ORDER BY process_name, plan_start_year, plan_start_month <if test="timeGranularity != null and timeGranularity == 'day'"> , plan_start_day </if> </select> <!-- 查询原始统计数据(不进行聚合)- 用于在Service层自行处理聚合逻辑 --> <select id="selectRawStatData" parameterType="java.util.Map" resultType="java.util.Map"> SELECT process_name AS "processName", process_plan_start_day AS "processPlanStartDay", plant AS "plant", major AS "major", workshop AS "workshop", process_total_time AS "processTotalTime" FROM aps_gas_piping_route_stat <where> <if test="yearStart != null and monthStart != null"> (EXTRACT(YEAR FROM process_plan_start_day) > #{yearStart} OR (EXTRACT(YEAR FROM process_plan_start_day) = #{yearStart} AND EXTRACT(MONTH FROM process_plan_start_day) >= #{monthStart})) AND (EXTRACT(YEAR FROM process_plan_start_day) < #{yearEnd} OR (EXTRACT(YEAR FROM process_plan_start_day) = #{yearEnd} AND EXTRACT(MONTH FROM process_plan_start_day) <= #{monthEnd})) </if> <if test="plants != null and plants.size() > 0"> AND plant IN <foreach collection="plants" item="item" open="(" separator="," close=")"> #{item} </foreach> </if> <if test="plant != null and plant != ''"> AND plant = #{plant} </if> <if test="majors != null and majors.size() > 0"> AND major IN <foreach collection="majors" item="item" open="(" separator="," close=")"> #{item} </foreach> </if> <if test="major != null and major != ''"> AND major = #{major} </if> <if test="workshops != null and workshops.size() > 0"> AND workshop IN <foreach collection="workshops" item="item" open="(" separator="," close=")"> #{item} </foreach> </if> <if test="workshop != null and workshop != ''"> AND workshop = #{workshop} </if> </where> ORDER BY process_name, process_plan_start_day </select> </mapper> aps-modules/aps-job/src/main/java/com/aps/job/mapper/ApsBomHeaderJobMapper.java
@@ -1,6 +1,7 @@ package com.aps.job.mapper; import com.aps.job.domain.ApsBomHeaderJob; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.List; @@ -11,6 +12,7 @@ * @author hjy * @date 2025-05-08 */ @Mapper public interface ApsBomHeaderJobMapper { /** aps-modules/aps-job/src/main/java/com/aps/job/mapper/ApsBomLineJobMapper.java
@@ -1,6 +1,7 @@ package com.aps.job.mapper; import com.aps.job.domain.ApsBomLineJob; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.List; @@ -11,6 +12,7 @@ * @author hjy * @date 2025-05-08 */ @Mapper public interface ApsBomLineJobMapper { /** aps-modules/aps-job/src/main/java/com/aps/job/service/impl/ApsBomHeaderJobServiceImpl.java
@@ -3,6 +3,7 @@ import cn.hutool.core.util.IdUtil; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson2.JSON; import com.aps.common.core.constant.SecurityConstants; import com.aps.common.core.utils.DateUtils; import com.aps.common.core.utils.StringUtils; @@ -15,6 +16,7 @@ import com.aps.job.mapper.ApsWorkOrderJobLogMapper; import com.aps.job.service.IApsBomHeaderJobService; import com.aps.system.api.RemoteCoreService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpEntity; @@ -35,6 +37,7 @@ * @date 2025-05-08 */ @Service @Slf4j public class ApsBomHeaderJobServiceImpl implements IApsBomHeaderJobService { @Autowired @@ -213,6 +216,8 @@ jobLog.setResult("header:"+headerList.size() + " line:"+lineList.size()); jobLog.setCreateBy(batchNum); jobLogMapper.insertApsWorkOrderJobLog(jobLog); log.info("BOM同步:"+ JSON.toJSONString(jobLog)); pageIndex++; }else{ break; @@ -227,6 +232,7 @@ jobLog.setResult("error"); jobLog.setCreateBy(batchNum); jobLogMapper.insertApsWorkOrderJobLog(jobLog); log.info("BOM同步:"+ JSON.toJSONString(jobLog)); break; } } @@ -241,6 +247,7 @@ jobLog.setResult("error"); jobLog.setCreateBy(batchNum); jobLogMapper.insertApsWorkOrderJobLog(jobLog); log.info("BOM同步异常信息:"+ JSON.toJSONString(jobLog)); return false; } return true; @@ -258,16 +265,24 @@ @Override public boolean syncBomDataJob(Integer pageIndex, Integer pageSize, String orgCode, String itemCodeList){ try { log.info("全量同步BOM数据定时任务开始"); log.info(" DELETE FROM aps_bom_header_job"); apsBomHeaderJobMapper.deleteApsBomHeaderJob(); log.info(" DELETE FROM aps_bom_line_job"); apsBomLineJobMapper.deleteApsBomLineJob(); boolean res = syncBomData(pageIndex, pageSize, orgCode, itemCodeList); if(!res){ return false; } log.info("将BOM同步到Redis"); remoteCoreService.setBomDataToRedis(SecurityConstants.INNER); log.info("deleteApsBomHeader"); apsBomHeaderJobMapper.deleteApsBomHeader(); log.info("deleteApsBomLine"); apsBomLineJobMapper.deleteApsBomLine(); log.info("insertIntoApsBomHeader"); apsBomHeaderJobMapper.insertIntoApsBomHeader(); log.info("insertIntoApsBomLine"); apsBomLineJobMapper.insertIntoApsBomLine(); } catch (Exception e) { e.printStackTrace(); aps-modules/aps-job/src/main/java/com/aps/job/service/impl/ApsMaterialManagementJobServiceImpl.java
@@ -3,6 +3,7 @@ import cn.hutool.core.util.IdUtil; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson2.JSON; import com.aps.common.core.utils.DateUtils; import com.aps.common.core.utils.StringUtils; import com.aps.common.core.utils.uuid.IdUtils; @@ -11,6 +12,7 @@ import com.aps.job.mapper.ApsMaterialManagementJobMapper; import com.aps.job.mapper.ApsWorkOrderJobLogMapper; import com.aps.job.service.IApsMaterialManagementJobService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpEntity; @@ -31,6 +33,7 @@ * @date 2025-05-10 */ @Service @Slf4j public class ApsMaterialManagementJobServiceImpl implements IApsMaterialManagementJobService { @Autowired @@ -182,6 +185,7 @@ jobLog.setResult("material:"+materialList.size()); jobLog.setCreateBy(batchNum); jobLogMapper.insertApsWorkOrderJobLog(jobLog); log.info("物料同步:"+ JSON.toJSONString(jobLog)); pageIndex++; }else{ break; @@ -196,6 +200,7 @@ jobLog.setResult("error"); jobLog.setCreateBy(batchNum); jobLogMapper.insertApsWorkOrderJobLog(jobLog); log.info("物料同步:"+ JSON.toJSONString(jobLog)); break; } } @@ -210,6 +215,7 @@ jobLog.setResult("error"); jobLog.setCreateBy(batchNum); jobLogMapper.insertApsWorkOrderJobLog(jobLog); log.info("物料同步:"+ JSON.toJSONString(jobLog)); return false; } return true; @@ -219,12 +225,16 @@ @Override public boolean syncApsMaterialDataJob(int pageIndex, int pageSize, String orgCode, String itemCodeList) { try { log.info("开始清除物料中间表aps_material_management_job"); apsMaterialManagementJobMapper.deleteApsMaterialJob(); log.info("开始同步物料信息"); boolean res = syncApsMaterialData(pageIndex, pageSize, orgCode, itemCodeList); if(!res){ return false; } log.info("开始清除物料表 aps_material_management"); apsMaterialManagementJobMapper.deleteApsMaterial(); log.info("插入数据到 aps_material_management"); apsMaterialManagementJobMapper.insertIntoApsMaterialManagement(); return true; } catch (Exception e) { aps-modules/aps-job/src/main/java/com/aps/job/service/impl/ApsMaterialStorageManagementJobServiceImpl.java
@@ -1,6 +1,7 @@ package com.aps.job.service.impl; import cn.hutool.core.util.IdUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.aps.common.core.utils.DateUtils; @@ -11,6 +12,7 @@ import com.aps.job.mapper.ApsMaterialStorageManagementJobMapper; import com.aps.job.mapper.ApsWorkOrderJobLogMapper; import com.aps.job.service.IApsMaterialStorageManagementJobService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpEntity; @@ -31,6 +33,7 @@ * @date 2025-05-08 */ @Service @Slf4j public class ApsMaterialStorageManagementJobServiceImpl implements IApsMaterialStorageManagementJobService { @Autowired @@ -174,6 +177,7 @@ jobLog.setResult("storage:"+storageList.size()); jobLog.setCreateBy(batchNum); jobLogMapper.insertApsWorkOrderJobLog(jobLog); log.info("物料同步:"+ JSON.toJSONString(jobLog)); pageIndex++; }else{ break; @@ -188,6 +192,7 @@ jobLog.setResult("error"); jobLog.setCreateBy(batchNum); jobLogMapper.insertApsWorkOrderJobLog(jobLog); log.info("物料同步异常:"+ JSON.toJSONString(jobLog)); break; } } @@ -202,6 +207,7 @@ jobLog.setResult("error"); jobLog.setCreateBy(batchNum); jobLogMapper.insertApsWorkOrderJobLog(jobLog); log.info("物料同步异常:"+ JSON.toJSONString(jobLog)); return false; } return true; @@ -211,12 +217,17 @@ @Override public boolean syncApsMaterialStorageDataJob(int pageIndex, int pageSize, String orgCode, String itemCodeList) { try { log.info("开始同步库存数据"); log.info("delete ApsMaterialStorageJob"); apsMaterialStorageManagementJobMapper.deleteApsMaterialStorageJob(); log.info("sync ApsMaterialStorageData"); boolean res = syncApsMaterialStorageData(pageIndex, pageSize, orgCode, itemCodeList); if(!res){ return false; } log.info("delete ApsMaterialStorage"); apsMaterialStorageManagementJobMapper.deleteApsMaterialStorage(); log.info("insertInto ApsMaterialStorage "); apsMaterialStorageManagementJobMapper.insertIntoApsMaterialStorage(); return true; } catch (Exception e) { aps-modules/aps-job/src/main/java/com/aps/job/service/impl/ApsStandardProcessRouteHeaderJobServiceImpl.java
@@ -1,6 +1,7 @@ package com.aps.job.service.impl; import cn.hutool.core.util.IdUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.aps.common.core.constant.SecurityConstants; @@ -15,6 +16,7 @@ import com.aps.job.mapper.ApsWorkOrderJobLogMapper; import com.aps.job.service.IApsStandardProcessRouteHeaderJobService; import com.aps.system.api.RemoteCoreService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpEntity; @@ -34,6 +36,7 @@ * @author hjy * @date 2025-05-09 */ @Slf4j @Service public class ApsStandardProcessRouteHeaderJobServiceImpl implements IApsStandardProcessRouteHeaderJobService { @@ -213,6 +216,7 @@ jobLog.setResult("header:"+headerList.size() + " line:"+lineList.size()); jobLog.setCreateBy(batchNum); jobLogMapper.insertApsWorkOrderJobLog(jobLog); log.info("标准工艺路线同步:"+ JSON.toJSONString(jobLog)); pageIndex++; }else{ break; @@ -226,6 +230,7 @@ jobLog.setBizType("processRoute"); jobLog.setResult("error"); jobLog.setCreateBy(batchNum); log.info("标准工艺路线同步:"+ JSON.toJSONString(jobLog)); jobLogMapper.insertApsWorkOrderJobLog(jobLog); break; } @@ -240,6 +245,7 @@ jobLog.setBizType("processRoute"); jobLog.setResult("error"); jobLog.setCreateBy(batchNum); log.info("标准工艺路线同步:"+ JSON.toJSONString(jobLog)); jobLogMapper.insertApsWorkOrderJobLog(jobLog); return false; } @@ -250,6 +256,7 @@ @Override public boolean syncProcessRouteDataJob(Integer pageIndex, Integer pageSize, String orgCode, String itemCodeList) { try { apsStandardProcessRouteHeaderJobMapper.deleteProcessRouteHeaderJob(); apsStandardProcessRouteLineJobMapper.deleteProcessRouteLineJob(); boolean res = syncProcessRouteData(pageIndex, pageSize, orgCode, itemCodeList); aps-modules/aps-job/src/main/java/com/aps/job/service/impl/ApsWeldSeamStandardJobServiceImpl.java
@@ -96,6 +96,7 @@ public boolean syncWeldSeamStandardData() { try { log.info("开始同步物料标准焊缝数据"); // 使用POST方法调用接口 HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); @@ -175,7 +176,7 @@ if (jobList.isEmpty()) { return false; } log.info("清空旧数据 deleteAllApsWeldSeamStandardJob"); // 清空旧数据 deleteAllApsWeldSeamStandardJob(); aps-modules/aps-job/src/main/java/com/aps/job/util/AbstractQuartzJob.java
@@ -78,7 +78,11 @@ sysJobLog.setStartTime(startTime); sysJobLog.setStopTime(new Date()); long runMs = sysJobLog.getStopTime().getTime() - sysJobLog.getStartTime().getTime(); sysJobLog.setJobMessage(sysJobLog.getJobName() + " 总共耗时:" + runMs + "毫秒"); long totalSeconds = runMs / 1000; long minutes = totalSeconds / 60; long seconds = totalSeconds % 60; String formattedTime = minutes + "分" + seconds + "秒"; sysJobLog.setJobMessage(sysJobLog.getJobName() + " 总共耗时:" +formattedTime); if (e != null) { sysJobLog.setStatus("1");