| | |
| | | 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; |
| | |
| | | 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; |
| | | } |
| | | } |