| | |
| | | import com.aps.common.core.utils.uuid.IdUtils; |
| | | import com.aps.common.security.utils.SecurityUtils; |
| | | import com.aps.core.domain.*; |
| | | import com.aps.core.domain.ApsPlate.ApsPlateStandardRequireError; |
| | | import com.aps.core.mapper.*; |
| | | import com.aps.core.service.IApsGasMaterialUsageService; |
| | | import com.aps.core.service.IApsGasPipingRouteStatService; |
| | | import com.aps.core.service.IApsStandardProcessService; |
| | | import com.aps.core.mapper.ApsPlateStandardRequireErrorMapper; |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.poi.ss.usermodel.*; |
| | |
| | | |
| | | @Autowired |
| | | private IApsStandardProcessService apsStandardProcessService; |
| | | |
| | | @Autowired |
| | | private ApsPlateStandardRequireErrorMapper apsPlateStandardRequireErrorMapper; |
| | | |
| | | /** |
| | | * 查询气体管路产能负载统计 |
| | |
| | | // 处理手工气体工单数据 - 使用联合查询获取所有相关数据 |
| | | List<Map<String, Object>> moDataList = apsGasPipingRouteStatMapper.selectMoRouteData(); |
| | | |
| | | // 获取所有的MO数据,用于检查哪些MO没有关联到工艺路线 |
| | | List<Map<String, Object>> allMoData = apsGasPipingRouteStatMapper.selectAllMoData(); |
| | | |
| | | // 记录已关联工艺路线的MO编号 |
| | | Set<String> processedMoSet = new HashSet<>(); |
| | | |
| | | // 按工单号分组,便于处理同一工单的不同工序 |
| | | 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); |
| | | |
| | | // 标记该工单已处理 |
| | | processedMoSet.add(workOrderNo); |
| | | |
| | | // 按工序号排序 |
| | | workOrderData.sort((a, b) -> { |
| | |
| | | |
| | | statList.addAll(processList); |
| | | } |
| | | } |
| | | |
| | | // 处理没有关联到工艺路线的MO数据 |
| | | List<ApsPlateStandardRequireError> errorList = new ArrayList<>(); |
| | | for (Map<String, Object> moData : allMoData) { |
| | | String workOrderNo = moData.get("mo").toString(); |
| | | // 如果该工单没有被处理过,说明没有关联到工艺路线 |
| | | if (!processedMoSet.contains(workOrderNo)) { |
| | | ApsPlateStandardRequireError error = ApsPlateStandardRequireError.builder() |
| | | .id(Long.valueOf(IdUtils.fastSimpleUUID().hashCode())) |
| | | .batchNumber(batchNumber) |
| | | .requireId(IdUtil.getSnowflakeNextId()) |
| | | .docNum(workOrderNo) |
| | | .itemNum(moData.get("material_code").toString()) |
| | | .orgCode(moData.get("factory").toString()) |
| | | .message("标准工艺路线不存在") |
| | | .delFlag("0") |
| | | .build(); |
| | | |
| | | // 设置基类属性 |
| | | error.setCreateBy(SecurityUtils.getUsername()); |
| | | error.setCreateTime(truncateToSeconds(DateUtils.getNowDate())); |
| | | |
| | | errorList.add(error); |
| | | } |
| | | } |
| | | |
| | | // 批量插入异常数据 |
| | | if (!errorList.isEmpty()) { |
| | | apsPlateStandardRequireErrorMapper.batchInsert(errorList); |
| | | } |
| | | |
| | | // 批量插入数据 |
| | |
| | | // 查询原始数据(不依赖数据库聚合) |
| | | List<Map<String, Object>> rawData = apsGasPipingRouteStatMapper.selectRawStatData(queryParams); |
| | | |
| | | // 如果没有查询到原始数据,但需要返回所有工序/车间的数据 |
| | | if (rawData.isEmpty()) { |
| | | // 创建没有时间限制的基础查询参数 |
| | | Map<String, Object> baseParams = new HashMap<>(queryParams); |
| | | baseParams.remove("yearStart"); |
| | | baseParams.remove("monthStart"); |
| | | baseParams.remove("yearEnd"); |
| | | baseParams.remove("monthEnd"); |
| | | |
| | | // 直接从aps_gas_piping_route_stat表查询所有可能的工序和车间 |
| | | List<Map<String, Object>> baseData = apsGasPipingRouteStatMapper.selectBaseStatData(baseParams); |
| | | |
| | | // 为每个工序/车间创建基于时间的空数据 |
| | | for (Map<String, Object> data : baseData) { |
| | | String rowGroupValue = getStringValue(data, rowGroupBy); |
| | | if (rowGroupValue == null || rowGroupValue.trim().isEmpty()) continue; |
| | | |
| | | Map<String, Object> rowEntry = new HashMap<>(); |
| | | Map<String, Object> rowDetail = new HashMap<>(); |
| | | |
| | | // 添加基本信息 |
| | | String plant = getStringValue(data, "plant"); |
| | | String major = getStringValue(data, "major"); |
| | | String workshop = getStringValue(data, "workshop"); |
| | | |
| | | if (groupByPlant) rowDetail.put("plant", plant); |
| | | if (groupByMajor) rowDetail.put("major", major); |
| | | if (groupByWorkshop) rowDetail.put("workshop", workshop); |
| | | |
| | | // 处理工序名称 - 如果rowGroupBy为workshop,需要添加processName字段 |
| | | if ("workshop".equals(rowGroupBy)) { |
| | | String processName = getStringValue(data, "processName"); |
| | | rowDetail.put("processName", processName != null ? processName : ""); |
| | | } |
| | | |
| | | // 为每个时间点创建零值数据 |
| | | List<Map<String, Object>> timeDataList = new ArrayList<>(); |
| | | for (String timePoint : timePoints) { |
| | | Map<String, Object> pointData = new HashMap<>(); |
| | | pointData.put("planDay", timePoint); |
| | | pointData.put("requireTimes", BigDecimal.ZERO); |
| | | pointData.put("designTimes", BigDecimal.ZERO); |
| | | pointData.put("capacityLoad", BigDecimal.ZERO); |
| | | 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; |
| | | } |
| | | |
| | |
| | | // 使用组合key来实现多维度分组(动态rowGroupBy字段 + 可选的工厂/专业/车间) |
| | | Map<String, Map<String, Object>> groupInfoMap = new HashMap<>(); |
| | | Map<String, Map<String, BigDecimal>> groupTimeDataMap = new HashMap<>(); |
| | | // 存储每个groupKey对应的processName集合 |
| | | Map<String, Set<String>> groupProcessNamesMap = 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); |
| | | if (rowGroupValue == null || rowGroupValue.trim().isEmpty()) { |
| | | log.warn("跳过处理:{} 字段值为空", rowGroupBy); |
| | | continue; |
| | | } |
| | | |
| | | // 获取工序名称(用于日志和后续处理) |
| | | String processName = getStringValue(data, "processName"); |
| | | |
| | | // 处理开工日期 |
| | | Date processPlanStartDay = (Date) data.get("processPlanStartDay"); |
| | |
| | | // 构建分组键 - 基于row分组字段和可选的其他维度 |
| | | String plant = getStringValue(data, "plant"); |
| | | String major = getStringValue(data, "major"); |
| | | String workshop = getStringValue(data, "workshop"); |
| | | |
| | | StringBuilder groupKeyBuilder = new StringBuilder(rowGroupValue); |
| | | |
| | |
| | | groupInfo.put("workshop", workshop); |
| | | groupInfo.put("processName", processName); |
| | | groupInfoMap.put(groupKey, groupInfo); |
| | | } |
| | | |
| | | // 收集processName |
| | | if (processName != null && !processName.trim().isEmpty()) { |
| | | if (!groupProcessNamesMap.containsKey(groupKey)) { |
| | | groupProcessNamesMap.put(groupKey, new HashSet<>()); |
| | | } |
| | | groupProcessNamesMap.get(groupKey).add(processName); |
| | | } |
| | | |
| | | // 计算时间点Key |
| | |
| | | rowDetail.put("workshop", groupInfo.get("workshop")); |
| | | } |
| | | |
| | | // 保留工序名称信息,以便前端展示 |
| | | if (!"processName".equals(rowGroupBy)) { |
| | | // 处理processName - 如果rowGroupBy为workshop,则将所有processName用分号连接 |
| | | if ("workshop".equals(rowGroupBy)) { |
| | | Set<String> processNames = groupProcessNamesMap.getOrDefault(groupKey, new HashSet<>()); |
| | | if (!processNames.isEmpty()) { |
| | | String joinedProcessNames = String.join(";", processNames); |
| | | rowDetail.put("processName", joinedProcessNames); |
| | | } else { |
| | | rowDetail.put("processName", groupInfo.get("processName")); |
| | | } |
| | | } else if (!"processName".equals(rowGroupBy)) { |
| | | // 保留工序名称信息,以便前端展示 |
| | | rowDetail.put("processName", groupInfo.get("processName")); |
| | | } |
| | | |
| | |
| | | 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("designTimes", 0); |
| | | pointData.put("capacityLoad", 0); |
| | | |
| | | timeDataList.add(pointData); |
| | |
| | | rowEntry.put(rowGroupValue, rowDetail); |
| | | plantTable.add(rowEntry); |
| | | } |
| | | |
| | | |
| | | // 在返回前查询设计产能数据并计算产能负荷 |
| | | for (Map<String, Object> rowEntry : plantTable) { |
| | | for (String rowKey : rowEntry.keySet()) { |
| | | Map<String, Object> rowDetail = (Map<String, Object>) rowEntry.get(rowKey); |
| | | List<Map<String, Object>> timeDataList = (List<Map<String, Object>>) rowDetail.get("timeData"); |
| | | |
| | | // 获取基本信息 |
| | | String plant = rowDetail.containsKey("plant") ? (String) rowDetail.get("plant") : null; |
| | | String major = rowDetail.containsKey("major") ? (String) rowDetail.get("major") : null; |
| | | String workshop = rowDetail.containsKey("workshop") ? (String) rowDetail.get("workshop") : null; |
| | | |
| | | // 处理不同的rowGroupBy情况 |
| | | if ("workshop".equals(rowGroupBy) && rowDetail.containsKey("processName")) { |
| | | // 情况1: 按workshop聚合,需要拆分processName字段 |
| | | String processNamesStr = (String) rowDetail.get("processName"); |
| | | String[] processNames = processNamesStr.split(";"); |
| | | |
| | | // 优化查询 - 创建缓存,按月份缓存查询结果 |
| | | // Key: year-month-processName, Value: 查询结果列表 |
| | | Map<String, List<ApsGasPipelineCapacityPlan>> capacityPlanCache = new HashMap<>(); |
| | | |
| | | // 遍历每个时间点 |
| | | for (Map<String, Object> timeData : timeDataList) { |
| | | String planDay = (String) timeData.get("planDay"); |
| | | BigDecimal requireTimes = new BigDecimal(timeData.get("requireTimes").toString()); |
| | | BigDecimal totalDesignTimes = BigDecimal.ZERO; |
| | | |
| | | // 拆分年月日 |
| | | String[] dateParts = planDay.split("-"); |
| | | String year = dateParts[0]; |
| | | String month = dateParts[1]; |
| | | // 统一month格式为整数字符串,去掉前导零 |
| | | month = String.valueOf(Integer.parseInt(month)); |
| | | String yearMonth = year + "-" + month; |
| | | |
| | | // 累加多个工序的设计产能 |
| | | for (String processName : processNames) { |
| | | // 构建缓存键 |
| | | String cacheKey = yearMonth + "-" + processName.trim(); |
| | | |
| | | // 从缓存获取或查询数据 |
| | | List<ApsGasPipelineCapacityPlan> capacityPlans; |
| | | if (capacityPlanCache.containsKey(cacheKey)) { |
| | | capacityPlans = capacityPlanCache.get(cacheKey); |
| | | } else { |
| | | // 使用专用查询方法查询设计产能数据 |
| | | // 按文档要求:根据process_name和plant去aps_gas_pipeline_capacity_plan表中查询 |
| | | capacityPlans = apsGasPipelineCapacityPlanMapper.selectDesignCapacityForInterface2( |
| | | processName.trim(), year, month, null, plant); |
| | | |
| | | // 将结果存入缓存 |
| | | capacityPlanCache.put(cacheKey, capacityPlans); |
| | | } |
| | | |
| | | // 累加设计产能值 |
| | | for (ApsGasPipelineCapacityPlan plan : capacityPlans) { |
| | | if ("day".equalsIgnoreCase(timeGranularity)) { |
| | | // 日粒度使用日产出数量 |
| | | if (plan.getDayProduceAllNum() != null) { |
| | | totalDesignTimes = totalDesignTimes.add(plan.getDayProduceAllNum()); |
| | | } |
| | | } else { |
| | | // 月粒度使用月产出总数量 |
| | | if (plan.getMonthProduceAllNum() != null) { |
| | | totalDesignTimes = totalDesignTimes.add(plan.getMonthProduceAllNum()); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 更新设计工时 |
| | | timeData.put("designTimes", totalDesignTimes); |
| | | |
| | | // 计算产能负荷 = 需求产能/设计产能×100% |
| | | if (totalDesignTimes.compareTo(BigDecimal.ZERO) > 0) { |
| | | BigDecimal capacityLoad = requireTimes |
| | | .divide(totalDesignTimes, 2, RoundingMode.HALF_UP) |
| | | .multiply(new BigDecimal(100)); |
| | | timeData.put("capacityLoad", capacityLoad); |
| | | } else { |
| | | timeData.put("capacityLoad", 0); |
| | | } |
| | | } |
| | | } else { |
| | | // 情况2: 按processName或其他字段聚合 |
| | | String processName = rowDetail.containsKey("processName") ? |
| | | (String) rowDetail.get("processName") : rowKey; |
| | | |
| | | // 优化查询 - 创建缓存,按月份缓存查询结果 |
| | | Map<String, List<ApsGasPipelineCapacityPlan>> capacityPlanCache = new HashMap<>(); |
| | | |
| | | // 遍历每个时间点 |
| | | for (Map<String, Object> timeData : timeDataList) { |
| | | String planDay = (String) timeData.get("planDay"); |
| | | BigDecimal requireTimes = new BigDecimal(timeData.get("requireTimes").toString()); |
| | | |
| | | // 拆分年月日 |
| | | String[] dateParts = planDay.split("-"); |
| | | String year = dateParts[0]; |
| | | String month = dateParts[1]; |
| | | // 统一month格式为整数字符串,去掉前导零 |
| | | month = String.valueOf(Integer.parseInt(month)); |
| | | String yearMonth = year + "-" + month; |
| | | |
| | | // 构建缓存键 |
| | | String cacheKey = yearMonth + "-" + processName; |
| | | |
| | | // 从缓存获取或查询数据 |
| | | List<ApsGasPipelineCapacityPlan> capacityPlans; |
| | | if (capacityPlanCache.containsKey(cacheKey)) { |
| | | capacityPlans = capacityPlanCache.get(cacheKey); |
| | | } else { |
| | | // 使用专用查询方法查询设计产能数据 |
| | | // 按文档要求:根据process_name和plant去aps_gas_pipeline_capacity_plan表中查询 |
| | | capacityPlans = apsGasPipelineCapacityPlanMapper.selectDesignCapacityForInterface2( |
| | | processName, year, month, null, plant); |
| | | |
| | | // 将结果存入缓存 |
| | | capacityPlanCache.put(cacheKey, capacityPlans); |
| | | } |
| | | |
| | | // 累加设计产能值 |
| | | BigDecimal totalDesignTimes = BigDecimal.ZERO; |
| | | for (ApsGasPipelineCapacityPlan plan : capacityPlans) { |
| | | if ("day".equalsIgnoreCase(timeGranularity)) { |
| | | // 日粒度使用日产出数量 |
| | | if (plan.getDayProduceAllNum() != null) { |
| | | totalDesignTimes = totalDesignTimes.add(plan.getDayProduceAllNum()); |
| | | } |
| | | } else { |
| | | // 月粒度使用月产出总数量 |
| | | if (plan.getMonthProduceAllNum() != null) { |
| | | totalDesignTimes = totalDesignTimes.add(plan.getMonthProduceAllNum()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // 更新设计工时 |
| | | timeData.put("designTimes", totalDesignTimes); |
| | | |
| | | // 计算产能负荷 = 需求产能/设计产能×100% |
| | | if (totalDesignTimes.compareTo(BigDecimal.ZERO) > 0) { |
| | | BigDecimal capacityLoad = requireTimes |
| | | .divide(totalDesignTimes, 2, RoundingMode.HALF_UP) |
| | | .multiply(new BigDecimal(100)); |
| | | timeData.put("capacityLoad", capacityLoad); |
| | | } else { |
| | | timeData.put("capacityLoad", 0); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | result.put("plantTable", plantTable); |
| | | result.put("timePoints", timePoints); |
| | | result.put("rowGroupBy", rowGroupBy); |