Zhu Zhonghua
2025-05-22 15ad2e8add262f3af8792bafb6ffd931db631a36
Merge branch 'dev' of http://192.168.50.149:8085/r/aps-backend into dev
已修改17个文件
1092 ■■■■■ 文件已修改
aps-modules/aps-core/src/main/java/com/aps/core/controller/ApsGasPipingRouteStatController.java 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aps-modules/aps-core/src/main/java/com/aps/core/domain/ApsGasPipingRouteStat.java 48 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aps-modules/aps-core/src/main/java/com/aps/core/mapper/ApsGasPipingRouteStatMapper.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aps-modules/aps-core/src/main/java/com/aps/core/service/IApsGasPipingRouteStatService.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aps-modules/aps-core/src/main/java/com/aps/core/service/impl/ApsGasPipingRouteStatServiceImpl.java 630 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aps-modules/aps-core/src/main/java/com/aps/core/service/impl/ApsMaterialStorageManagementServiceImpl.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aps-modules/aps-core/src/main/java/com/aps/core/service/impl/ApsPlate/ApsPlatePlanServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aps-modules/aps-core/src/main/java/com/aps/core/service/impl/ApsStandardProcessRouteHeaderServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aps-modules/aps-core/src/main/resources/mapper/core/ApsGasPipingRouteStatMapper.xml 251 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aps-modules/aps-job/src/main/java/com/aps/job/mapper/ApsBomHeaderJobMapper.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aps-modules/aps-job/src/main/java/com/aps/job/mapper/ApsBomLineJobMapper.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aps-modules/aps-job/src/main/java/com/aps/job/service/impl/ApsBomHeaderJobServiceImpl.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aps-modules/aps-job/src/main/java/com/aps/job/service/impl/ApsMaterialManagementJobServiceImpl.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aps-modules/aps-job/src/main/java/com/aps/job/service/impl/ApsMaterialStorageManagementJobServiceImpl.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aps-modules/aps-job/src/main/java/com/aps/job/service/impl/ApsStandardProcessRouteHeaderJobServiceImpl.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aps-modules/aps-job/src/main/java/com/aps/job/service/impl/ApsWeldSeamStandardJobServiceImpl.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
aps-modules/aps-job/src/main/java/com/aps/job/util/AbstractQuartzJob.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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}")
@@ -106,6 +107,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")
    @RequiresPermissions("aps:apsGasPipingRouteStat: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,12 +120,37 @@
    @Schema(description = "日", type = "String")
    private String planStartDay;
    /** 批次号 */
    @Excel(name = "批次号")
    private String batchNumber;
    /** 计划完成日 */
    @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;
    @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,7 +40,8 @@
               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>
@@ -46,19 +49,18 @@
        <include refid="selectApsGasPipingRouteStatVo"/>
        <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 &gt;= #{searchStartDate} and process_plan_start_day &lt;= #{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="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,7 +94,7 @@
        <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>
@@ -102,13 +115,15 @@
            <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>
@@ -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>
@@ -176,26 +199,32 @@
        delete from aps_gas_piping_route_stat where batch_number != #{batchNumber}
    </delete>
    <insert id="insertApsGasPipingRouteStatBatch">
        INSERT INTO aps_gas_piping_route_stat
    <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=",">
        (
        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=",">
            (
            #{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,
@@ -220,4 +249,154 @@
        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 &lt; #{yearEnd} OR
                 (plan_start_year = #{yearEnd} AND plan_start_month &lt;= #{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) &lt; #{yearEnd} OR
                 (EXTRACT(YEAR FROM process_plan_start_day) = #{yearEnd} AND EXTRACT(MONTH FROM process_plan_start_day) &lt;= #{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");