huang
8 小时以前 8ec0064cd95292f14027006a8be47c1a71f69af9
mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/GlassInfoServiceImpl.java
@@ -15,11 +15,12 @@
import org.springframework.util.CollectionUtils;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import static java.util.stream.IntStream.range;
/**
 * 玻璃信息服务实现类
@@ -106,9 +107,10 @@
            return false;
        }
        try {
            // 检查是否已存在
            GlassInfo existing = baseMapper.selectByGlassId(glassInfo.getGlassId());
            // 查询包括逻辑删除的记录
            GlassInfo existing = baseMapper.selectByGlassIdIncludingDeleted(glassInfo.getGlassId());
            if (existing != null) {
                // 存在则更新
                glassInfo.setId(existing.getId());
                // 保留原始创建信息
                if (glassInfo.getCreatedTime() == null) {
@@ -117,15 +119,42 @@
                if (glassInfo.getCreatedBy() == null) {
                    glassInfo.setCreatedBy(existing.getCreatedBy());
                }
                // 更新为当前时间
                // 设置更新时间
                if (glassInfo.getUpdatedTime() == null) {
                    glassInfo.setUpdatedTime(new Date());
                }
                if (glassInfo.getUpdatedBy() == null) {
                    glassInfo.setUpdatedBy("system");
                }
                return updateById(glassInfo);
                if (existing.getIsDeleted() != null && existing.getIsDeleted() != 0) {
                    log.info("恢复逻辑删除的玻璃信息: glassId={}, id={}", glassInfo.getGlassId(), existing.getId());
                    int updatedRows = baseMapper.restoreAndUpdateById(
                            existing.getId(),
                            glassInfo.getGlassId(),
                            glassInfo.getGlassLength(),
                            glassInfo.getGlassWidth(),
                            glassInfo.getGlassThickness(),
                            glassInfo.getStatus(),
                            glassInfo.getState(),
                            glassInfo.getEngineeringId(),
                            glassInfo.getUpdatedTime() != null ? glassInfo.getUpdatedTime() : new Date(),
                            glassInfo.getUpdatedBy() != null ? glassInfo.getUpdatedBy() : "system"
                    );
                    boolean updated = updatedRows > 0;
                    log.info("恢复逻辑删除记录结果: glassId={}, updatedRows={}, updated={}",
                            glassInfo.getGlassId(), updatedRows, updated);
                    if (!updated) {
                        log.error("恢复逻辑删除记录失败,可能原因:更新条件不匹配或数据异常, glassId={}, id={}",
                                glassInfo.getGlassId(), existing.getId());
                    }
                    return updated;
                } else {
                    // 正常更新
                    return updateById(glassInfo);
                }
            } else {
                // 不存在则新增
                Date now = new Date();
                if (glassInfo.getCreatedTime() == null) {
                    glassInfo.setCreatedTime(now);
@@ -138,6 +167,9 @@
                }
                if (glassInfo.getUpdatedBy() == null) {
                    glassInfo.setUpdatedBy("system");
                }
                if (glassInfo.getIsDeleted() == null) {
                    glassInfo.setIsDeleted(0);
                }
                return save(glassInfo);
            }
@@ -153,10 +185,19 @@
            return true;
        }
        try {
            int successCount = 0;
            int failCount = 0;
            for (GlassInfo glassInfo : glassInfos) {
                saveOrUpdateGlassInfo(glassInfo);
                boolean result = saveOrUpdateGlassInfo(glassInfo);
                if (result) {
                    successCount++;
                } else {
                    failCount++;
                    log.warn("保存或更新玻璃信息失败: glassId={}", glassInfo != null ? glassInfo.getGlassId() : "null");
                }
            }
            return true;
            log.info("批量保存或更新玻璃信息完成: 总数={}, 成功={}, 失败={}", glassInfos.size(), successCount, failCount);
            return failCount == 0;
        } catch (Exception e) {
            log.error("批量保存或更新玻璃信息失败", e);
            return false;
@@ -232,8 +273,18 @@
            return result;
        }
        // 工程号生成:每次导入都生成新的工程号(使用数据库自增序号,避免重复)
        final String engineerId = engineeringSequenceService.generateAndSaveEngineeringId(new Date());
        // 工程号:代表整个Excel表,优先使用Excel中的工程号(从第一行或任意一行获取),如果所有行都没有则自动生成
        String engineerIdFromExcel = null;
        for (Map<String, Object> row : excelRows) {
            String engineeringId = str(row.get("engineeringId"));
            if (engineeringId != null && !engineeringId.trim().isEmpty()) {
                engineerIdFromExcel = engineeringId.trim();
                break; // 找到第一个非空的工程号就使用
            }
        }
        final String engineerId = engineerIdFromExcel != null
                ? engineerIdFromExcel
                : engineeringSequenceService.generateEngineeringId(new Date());
        final String filmsIdDefault = firstValue(excelRows, "filmsId", "白玻");
        final double thicknessDefault = parseDouble(firstValue(excelRows, "thickness"), 0d);
@@ -242,8 +293,22 @@
        final String filmsIdDefaultFinal = filmsIdDefault;
        final double thicknessDefaultFinal = thicknessDefault;
        // 用于按 flowCardId 全局计数序号
        Map<String, Integer> flowCardSequenceCounter = new HashMap<>();
        // 生成日期字符串(yyMMdd格式),用于流程卡ID生成
        LocalDate localDate = new Date().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
        String dateStr = localDate.format(DateTimeFormatter.ofPattern("yyMMdd"));
        // 检查是否有流程卡ID:如果所有行的流程卡ID都为空,则所有记录共享同一个流程卡ID
        boolean allFlowCardIdEmpty = excelRows.stream()
                .allMatch(row -> {
                    String flowCardId = str(row.get("flowCardId"));
                    return flowCardId == null || flowCardId.trim().isEmpty();
                });
        // 如果所有流程卡ID都为空,生成一个共享的流程卡ID
        String sharedFlowCardId = allFlowCardIdEmpty ? "NG" + dateStr + "01A001" : null;
        // 用于存储每个玻璃ID对应的流程卡ID(同一玻璃ID的多个玻璃共享同一个流程卡ID)
        Map<String, String> glassIdFlowCardIdMap = new HashMap<>();
        // 用于按 flowCardId 计数 temperingFeedSequence
        Map<String, Integer> temperingFeedSequenceCounter = new HashMap<>();
        // 用于按 flowCardId 分配 temperingLayoutId
@@ -254,7 +319,7 @@
        Map<String, Integer> rawSequenceMap = new HashMap<>();
        for (Map<String, Object> row : excelRows) {
            double width = parseDouble(row.get("width"), 0d);
            double height = parseDouble(row.get("height"), 0d);
            double height = parseDouble(row.get("length"), 0d);
            double thickness = parseDouble(row.get("thickness"), thicknessDefaultFinal);
            String filmsId = strOrDefault(row.get("filmsId"), filmsIdDefaultFinal);
            String key = width + "_" + height + "_" + thickness + "_" + filmsId;
@@ -263,96 +328,111 @@
            }
        }
        List<Map<String, Object>> glassInfolList = excelRows.stream()
                .flatMap(row -> {
                    Object qtyObj = row.getOrDefault("quantity", 1);
                    int qty = parseDouble(qtyObj, 1) > 0 ? (int) parseDouble(qtyObj, 1) : 1;
        List<Map<String, Object>> glassInfolList = new ArrayList<>();
        for (Map<String, Object> row : excelRows) {
            Object qtyObj = row.getOrDefault("quantity", 1);
            int qty = parseDouble(qtyObj, 1) > 0 ? (int) parseDouble(qtyObj, 1) : 1;
                    String glassId = str(row.get("glassId"));
                    String filmsId = strOrDefault(row.get("filmsId"), filmsIdDefaultFinal);
                    String flowCardId = str(row.get("flowCardId"));
                    // orderNumber 是整型(玻璃类型),从 Excel 读取或使用默认值 1
                    Object orderNumberObj = row.get("orderNumber");
                    final Integer finalOrderNumber = orderNumberObj != null
                            ? (int) parseDouble(orderNumberObj, 1)
                            : 1;
                    String productName = str(row.get("productName"));
                    String customerName = str(row.get("customerName"));
                    double width = parseDouble(row.get("width"), 0d);
                    double height = parseDouble(row.get("height"), 0d);
                    double thickness = parseDouble(row.get("thickness"), thicknessDefaultFinal);
                    // 计算 rawSequence
                    String rawKey = width + "_" + height + "_" + thickness + "_" + filmsId;
                    Integer rawSequence = rawSequenceMap.get(rawKey);
            String glassId = str(row.get("glassId"));
            String filmsId = strOrDefault(row.get("filmsId"), filmsIdDefaultFinal);
            // 流程卡ID:如果Excel中有,使用Excel的;如果为空,使用共享的流程卡ID
            String flowCardIdFromExcel = str(row.get("flowCardId"));
            String flowCardId;
            if (flowCardIdFromExcel != null && !flowCardIdFromExcel.trim().isEmpty()) {
                // Excel中有流程卡ID,使用Excel的
                flowCardId = flowCardIdFromExcel.trim();
            } else {
                // Excel中流程卡ID为空,使用共享的流程卡ID
                flowCardId = sharedFlowCardId != null ? sharedFlowCardId : getOrGenerateFlowCardId(glassId, dateStr, glassIdFlowCardIdMap);
            }
            // 去掉尾部 "/数字"(如果有)
            String baseFlowCardId = flowCardId.replaceFirst("/\\d+$", "");
            // 层号:如果Excel中有,使用Excel的;如果没有,默认1
            Object layerObj = row.get("layer");
            int layer = layerObj != null ? (int) parseDouble(layerObj, 1) : 1;
            if (layer <= 0) {
                layer = 1;
            }
            // orderNumber 是整型(玻璃类型),从 Excel 读取或使用默认值 1
            Object orderNumberObj = row.get("orderNumber");
            final Integer finalOrderNumber = orderNumberObj != null
                    ? (int) parseDouble(orderNumberObj, 1)
                    : 1;
            String productName = str(row.get("productName"));
            String customerName = str(row.get("customerName"));
            double width = parseDouble(row.get("width"), 0d);
            double height = parseDouble(row.get("length"), 0d);
            double thickness = parseDouble(row.get("thickness"), thicknessDefaultFinal);
            // 计算 rawSequence
            String rawKey = width + "_" + height + "_" + thickness + "_" + filmsId;
            Integer rawSequence = rawSequenceMap.get(rawKey);
                    int finalQty = qty;
                    log.info("解析到数量:row={}, quantity={}, 最终qty={}", row, qtyObj, finalQty);
                    return range(0, qty).mapToObj(idx -> {
                        String baseGlassId = engineerIdFinal + glassId;
                        String finalGlassId = finalQty > 1 ? baseGlassId + (idx + 1) : baseGlassId;
            int finalQty = qty;
            log.info("解析到数量:row={}, quantity={}, 最终qty={}", row, qtyObj, finalQty);
            // 按 flowCardId 分配 temperingLayoutId
            Integer temperingLayoutId = temperingLayoutIdMap.computeIfAbsent(baseFlowCardId, k -> nextTemperingLayoutId.getAndIncrement());
            // 为同一行的多个玻璃生成数据
            for (int idx = 0; idx < qty; idx++) {
                String baseGlassId = engineerIdFinal + glassId;
                String finalGlassId = finalQty > 1 ? baseGlassId + (idx + 1) : baseGlassId;
                // 按 flowCardId 递增 temperingFeedSequence
                int temperingFeedSequence = temperingFeedSequenceCounter.compute(baseFlowCardId, (k, v) -> (v == null ? 0 : v) + 1);
                String finalFlowCardSequence = baseFlowCardId + "/" + 1;
                log.debug("生成玻璃信息: glassId={}, idx={}, baseFlowCardId={}, finalFlowCardSequence={}, temperingLayoutId={}, temperingFeedSequence={}",
                        glassId, idx, baseFlowCardId, finalFlowCardSequence, temperingLayoutId, temperingFeedSequence);
                        String baseFlowCardId = flowCardId.isEmpty() ? baseGlassId : flowCardId;
                        // 如果 baseFlowCardId 已经包含尾部 "/数字",先去掉,再由后端统一追加序号
                        baseFlowCardId = baseFlowCardId.replaceFirst("/\\d+$", "");
                        // 按 flowCardId 全局递增序号
                        int sequenceNum = flowCardSequenceCounter.compute(baseFlowCardId, (k, v) -> (v == null ? 0 : v) + 1);
                        String finalFlowCardSequence = baseFlowCardId + "/" + 1;
                        // 按 flowCardId 分配 temperingLayoutId
                        Integer temperingLayoutId = temperingLayoutIdMap.computeIfAbsent(baseFlowCardId, k -> nextTemperingLayoutId.getAndIncrement());
                        // 按 flowCardId 递增 temperingFeedSequence
                        int temperingFeedSequence = temperingFeedSequenceCounter.compute(baseFlowCardId, (k, v) -> (v == null ? 0 : v) + 1);
                        log.debug("生成flowCardSequence: idx={}, baseFlowCardId={}, sequenceNum={}, finalFlowCardSequence={}, temperingLayoutId={}, temperingFeedSequence={}",
                                idx, baseFlowCardId, sequenceNum, finalFlowCardSequence, temperingLayoutId, temperingFeedSequence);
                        Map<String, Object> m = new HashMap<>();
                        m.put("xAxis", 0);
                        m.put("xCoordinate", 0);
                        m.put("yAxis", 0);
                        m.put("yCoordinate", 0);
                        m.put("glassId", finalGlassId);
                        m.put("engineerId", engineerIdFinal);
                        m.put("flowCardId", baseFlowCardId);
                        m.put("orderNumber", finalOrderNumber);
                        m.put("productSortNumber", 1); // 统一为1
                        m.put("hollowCombineDirection", "");
                        m.put("width", width);
                        m.put("height", height);
                        m.put("thickness", thickness);
                        m.put("filmsId", filmsId);
                        m.put("layer", 1);
                        m.put("totalLayer", 1);
                        m.put("edgWidth", width);
                        m.put("edgHeight", height);
                        m.put("isMultiple", finalQty > 1 ? 1 : 0); // 数量>1时为1
                        m.put("maxWidth", width);
                        m.put("maxHeight", height);
                        m.put("isHorizontal", 0);
                        m.put("rawSequence", rawSequence != null ? rawSequence : 0);
                        m.put("temperingLayoutId", temperingLayoutId);
                        m.put("temperingFeedSequence", temperingFeedSequence);
                        m.put("angle", 0);
                        m.put("ruleId", 0);
                        m.put("combine", 0);
                        m.put("markIcon", "");
                        m.put("filmRemove", 0);
                        m.put("flowCardSequence", finalFlowCardSequence);
                        m.put("process", "");
                        m.put("rawAngle", 0);
                        m.put("graphNo", 0);
                        m.put("processParam", "");
                        return m;
                    });
                })
                .collect(Collectors.toList());
                Map<String, Object> m = new HashMap<>();
                m.put("xAxis", 0);
                m.put("xCoordinate", 0);
                m.put("yAxis", 0);
                m.put("yCoordinate", 0);
                m.put("glassId", finalGlassId);
                m.put("engineerId", engineerIdFinal);
                m.put("flowCardId", baseFlowCardId);
                m.put("orderNumber", finalOrderNumber);
                m.put("productSortNumber", 1);
                m.put("hollowCombineDirection", "");
                m.put("width", width);
                m.put("height", height);
                m.put("thickness", thickness);
                m.put("filmsId", filmsId);
                m.put("layer", layer);
                m.put("totalLayer", 1);
                m.put("edgWidth", width);
                m.put("edgHeight", height);
                m.put("isMultiple", finalQty > 1 ? 1 : 0);
                m.put("maxWidth", width);
                m.put("maxHeight", height);
                m.put("isHorizontal", 0);
                m.put("rawSequence", rawSequence != null ? rawSequence : 0);
                m.put("temperingLayoutId", temperingLayoutId);
                m.put("temperingFeedSequence", temperingFeedSequence);
                m.put("angle", 0);
                m.put("ruleId", 0);
                m.put("combine", 0);
                m.put("markIcon", "");
                m.put("filmRemove", 0);
                m.put("flowCardSequence", finalFlowCardSequence);
                m.put("process", "");
                m.put("rawAngle", 0);
                m.put("graphNo", 0);
                m.put("processParam", "");
                glassInfolList.add(m);
            }
        }
        // 原片信息去重
        Map<String, Map<String, Object>> rawGlassMap = new HashMap<>();
        for (Map<String, Object> row : excelRows) {
            double width = parseDouble(row.get("width"), 0d);
            double height = parseDouble(row.get("height"), 0d);
            double height = parseDouble(row.get("length"), 0d);
            double thickness = parseDouble(row.get("thickness"), thicknessDefaultFinal);
            String filmsId = strOrDefault(row.get("filmsId"), filmsIdDefaultFinal);
            String key = width + "_" + height + "_" + thickness + "_" + filmsId;
@@ -375,9 +455,15 @@
        Map<String, Map<String, Object>> flowCardMap = new HashMap<>();
        for (Map<String, Object> row : excelRows) {
            String glassId = str(row.get("glassId"));
            String flowCardId = str(row.get("flowCardId"));
            if (flowCardId.isEmpty()) {
                flowCardId = engineerIdFinal + glassId;
            // 流程卡ID:如果Excel中有,使用Excel的;如果为空,使用共享的流程卡ID(与glassInfolList逻辑一致)
            String flowCardIdFromExcel = str(row.get("flowCardId"));
            String flowCardId;
            if (flowCardIdFromExcel != null && !flowCardIdFromExcel.trim().isEmpty()) {
                // Excel中有流程卡ID,使用Excel的
                flowCardId = flowCardIdFromExcel.trim();
            } else {
                // Excel中流程卡ID为空,使用共享的流程卡ID
                flowCardId = sharedFlowCardId != null ? sharedFlowCardId : getOrGenerateFlowCardId(glassId, dateStr, glassIdFlowCardIdMap);
            }
            // 去掉尾部 "/数字"(如果有)
            flowCardId = flowCardId.replaceFirst("/\\d+$", "");
@@ -389,7 +475,7 @@
            Object qtyObj = row.getOrDefault("quantity", 1);
            int qty = parseDouble(qtyObj, 1) > 0 ? (int) parseDouble(qtyObj, 1) : 1;
            double width = parseDouble(row.get("width"), 0d);
            double height = parseDouble(row.get("height"), 0d);
            double height = parseDouble(row.get("length"), 0d);
            double thickness = parseDouble(row.get("thickness"), thicknessDefaultFinal);
            String filmsId = strOrDefault(row.get("filmsId"), filmsIdDefaultFinal);
            String productName = str(row.get("productName"));
@@ -547,6 +633,40 @@
            return def;
        }
    }
    /**
     * 获取或生成流程卡ID
     * 如果已存在则返回,否则生成新的流程卡ID并缓存
     * 前端格式:原始glassId + 两位序号(如"101"、"102"、"201"),通过除以100去掉最后两位提取原始glassId
     *
     * @param glassId 玻璃ID(如"101"、"102"、"201")
     * @param dateStr 日期字符串(yyMMdd格式)
     * @param glassIdFlowCardIdMap 玻璃ID到流程卡ID的映射缓存
     * @return 流程卡ID(格式:NG + yyMMdd + 序号(两位) + A001)
     */
    private String getOrGenerateFlowCardId(String glassId, String dateStr, Map<String, String> glassIdFlowCardIdMap) {
        String flowCardId = glassIdFlowCardIdMap.get(glassId);
        if (flowCardId == null) {
            // 从glassId中提取原始数字:101 -> 1, 102 -> 1, 201 -> 2
            int sequence = 1;
            if (glassId != null && !glassId.trim().isEmpty()) {
                try {
                    String cleaned = glassId.trim().split("[\\r\\n\\t\\s]+")[0];
                    if (cleaned.matches("\\d+")) {
                        int num = Integer.parseInt(cleaned);
                        // 如果长度>=3,除以100去掉最后两位(前端追加的序号)
                        sequence = (cleaned.length() >= 3 && num >= 100) ? num / 100 : (num > 0 ? num : 1);
                    }
                } catch (Exception e) {
                    log.error("从glassId中提取原始数字失败: glassId={}", glassId, e);
                }
            }
            flowCardId = "NG" + dateStr + String.format("%02d", sequence) + "A001";
            glassIdFlowCardIdMap.put(glassId, flowCardId);
            log.info("为玻璃ID {} 生成流程卡ID: flowCardId={}", glassId, flowCardId);
        }
        return flowCardId;
    }
    /**
     * 保留两位小数(四舍五入)
@@ -577,6 +697,13 @@
            return;
        }
        // 如果工程号已存在,先删除该工程号下的旧数据,实现覆盖更新
        List<GlassInfo> existingGlassInfos = getGlassInfosByEngineeringId(engineeringId.trim());
        if (!existingGlassInfos.isEmpty()) {
            log.info("检测到工程号 {} 已存在 {} 条记录,将删除旧数据并更新", engineeringId, existingGlassInfos.size());
            deleteGlassInfosByEngineeringId(engineeringId.trim());
        }
        List<GlassInfo> glassInfos = new ArrayList<>();
        Date now = new Date();
@@ -590,18 +717,18 @@
            if (qty <= 0) qty = 1;
            double width = parseDouble(row.get("width"), 0d);
            double height = parseDouble(row.get("height"), 0d);
            double length = parseDouble(row.get("length"), 0d);
            double thickness = parseDouble(row.get("thickness"), 0d);
            // 与导入规则保持一致:glassId 前加工程号前缀,数量>1时追加序号
            String baseGlassId = engineeringId.trim() + glassId;
            for (int idx = 0; idx < qty; idx++) {
                String finalGlassId = qty > 1 ? baseGlassId + "_" + (idx + 1) : baseGlassId;
                String finalGlassId = qty > 1 ? baseGlassId + (idx + 1) : baseGlassId;
                GlassInfo glassInfo = new GlassInfo();
                glassInfo.setGlassId(finalGlassId);
                glassInfo.setEngineeringId(engineeringId.trim());
                glassInfo.setGlassLength((int) Math.round(height));
                glassInfo.setGlassLength((int) Math.round(length));
                glassInfo.setGlassWidth((int) Math.round(width));
                glassInfo.setGlassThickness(BigDecimal.valueOf(thickness));
                glassInfo.setStatus(GlassInfo.Status.ACTIVE);
@@ -616,8 +743,12 @@
        }
        if (!glassInfos.isEmpty()) {
            batchSaveOrUpdateGlassInfo(glassInfos);
            log.info("已保存 {} 条玻璃信息到本地数据库,工程号: {}", glassInfos.size(), engineeringId);
            boolean success = batchSaveOrUpdateGlassInfo(glassInfos);
            if (success) {
                log.info("已保存 {} 条玻璃信息到本地数据库,工程号: {}", glassInfos.size(), engineeringId);
            } else {
                log.error("保存玻璃信息到本地数据库失败,工程号: {}, 总数: {}", engineeringId, glassInfos.size());
            }
        }
    }
@@ -674,14 +805,12 @@
        try {
            // 先查询要删除的数量(删除前)
            LambdaQueryWrapper<GlassInfo> countWrapper = new LambdaQueryWrapper<>();
            countWrapper.eq(GlassInfo::getEngineeringId, engineeringId.trim())
                       .eq(GlassInfo::getIsDeleted, 0); // 查询未删除的记录
            countWrapper.eq(GlassInfo::getEngineeringId, engineeringId.trim());
            long count = this.count(countWrapper);
            
            // 使用MyBatis-Plus的remove方法,会根据@TableLogic自动进行逻辑删除
            LambdaQueryWrapper<GlassInfo> removeWrapper = new LambdaQueryWrapper<>();
            removeWrapper.eq(GlassInfo::getEngineeringId, engineeringId.trim())
                        .eq(GlassInfo::getIsDeleted, 0); // 只删除未删除的记录
            removeWrapper.eq(GlassInfo::getEngineeringId, engineeringId.trim());
            
            boolean result = this.remove(removeWrapper);
            if (result) {