huang
3 天以前 22e17b6db03ca58bc477a35ca067e55a09cffce7
添加工程表及相关逻辑,防止重复导入Excel表序号重复生成
3个文件已修改
5个文件已添加
301 ■■■■ 已修改文件
mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/EngineeringSequence.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
mes-processes/mes-plcSend/src/main/java/com/mes/device/mapper/EngineeringSequenceMapper.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
mes-processes/mes-plcSend/src/main/java/com/mes/device/service/EngineeringSequenceService.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/EngineeringSequenceServiceImpl.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/GlassInfoServiceImpl.java 56 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
mes-processes/mes-plcSend/src/main/resources/db/migration/V20241121__create_engineering_table.sql 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
mes-web/public/config.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
mes-web/src/utils/constants.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
mes-processes/mes-plcSend/src/main/java/com/mes/device/entity/EngineeringSequence.java
New file
@@ -0,0 +1,63 @@
package com.mes.device.entity;
import com.baomidou.mybatisplus.annotation.*;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.Date;
/**
 * 工程序号实体类
 * 对应数据库表:engineering_sequence
 * 用于管理工程号生成,避免重复
 */
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("engineering_sequence")
@ApiModel(value = "EngineeringSequence", description = "工程序号信息")
public class EngineeringSequence {
    @ApiModelProperty(value = "主键ID", example = "1")
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    @ApiModelProperty(value = "工程号(唯一标识)", example = "P25010801")
    @TableField("engineering_id")
    private String engineeringId;
    @ApiModelProperty(value = "日期(yyMMdd格式对应的日期)")
    @TableField("date")
    @JsonFormat(pattern = "yyyy-MM-dd")
    private Date date;
    @ApiModelProperty(value = "当天的序号", example = "1")
    @TableField("sequence")
    private Integer sequence;
    @ApiModelProperty(value = "创建时间")
    @TableField(value = "created_time", fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createdTime;
    @ApiModelProperty(value = "更新时间")
    @TableField(value = "updated_time", fill = FieldFill.INSERT_UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date updatedTime;
    @ApiModelProperty(value = "创建人", example = "system")
    @TableField(value = "created_by", fill = FieldFill.INSERT)
    private String createdBy;
    @ApiModelProperty(value = "更新人", example = "system")
    @TableField(value = "updated_by", fill = FieldFill.INSERT_UPDATE)
    private String updatedBy;
    @ApiModelProperty(value = "是否删除:0-否,1-是", example = "0")
    @TableField("is_deleted")
    @TableLogic
    private Integer isDeleted;
}
mes-processes/mes-plcSend/src/main/java/com/mes/device/mapper/EngineeringSequenceMapper.java
New file
@@ -0,0 +1,47 @@
package com.mes.device.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mes.device.entity.EngineeringSequence;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.Date;
/**
 * 工程序号Mapper接口
 *
 * @author mes
 * @since 2024-11-20
 */
@Mapper
public interface EngineeringSequenceMapper extends BaseMapper<EngineeringSequence> {
    /**
     * 查询指定日期的最大序号
     *
     * @param date 日期
     * @return 最大序号,如果没有记录返回0
     */
    @Select("SELECT COALESCE(MAX(sequence), 0) FROM engineering_sequence WHERE date = #{date} AND is_deleted = 0")
    Integer selectMaxSequenceByDate(@Param("date") Date date);
    /**
     * 查询指定日期的最大序号并加行锁,避免并发生成重复序号
     *
     * @param date 日期
     * @return 最大序号,如果没有记录返回0
     */
    @Select("SELECT COALESCE(MAX(sequence), 0) FROM engineering_sequence WHERE date = #{date} AND is_deleted = 0 FOR UPDATE")
    Integer selectMaxSequenceByDateForUpdate(@Param("date") Date date);
    /**
     * 根据工程号查询工程序号信息
     *
     * @param engineeringId 工程号
     * @return 工程序号信息
     */
    @Select("SELECT * FROM engineering_sequence WHERE engineering_id = #{engineeringId} AND is_deleted = 0 LIMIT 1")
    EngineeringSequence selectByEngineeringId(@Param("engineeringId") String engineeringId);
}
mes-processes/mes-plcSend/src/main/java/com/mes/device/service/EngineeringSequenceService.java
New file
@@ -0,0 +1,25 @@
package com.mes.device.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.mes.device.entity.EngineeringSequence;
import java.util.Date;
/**
 * 工程序号信息服务接口
 *
 * @author mes
 * @since 2024-11-20
 */
public interface EngineeringSequenceService extends IService<EngineeringSequence> {
    /**
     * 生成并保存新的工程号
     * 根据日期获取最大序号,然后自增1生成新的工程号
     *
     * @param date 日期
     * @return 生成的工程号(格式:P + yyMMdd + 两位序号)
     */
    String generateAndSaveEngineeringId(Date date);
}
mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/EngineeringSequenceServiceImpl.java
New file
@@ -0,0 +1,81 @@
package com.mes.device.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mes.device.entity.EngineeringSequence;
import com.mes.device.mapper.EngineeringSequenceMapper;
import com.mes.device.service.EngineeringSequenceService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
/**
 * 工程序号信息服务实现类
 *
 * @author mes
 * @since 2024-11-20
 */
@Slf4j
@Service
public class EngineeringSequenceServiceImpl extends ServiceImpl<EngineeringSequenceMapper, EngineeringSequence> implements EngineeringSequenceService {
    // 日期格式化器(线程不安全,使用ThreadLocal保证线程安全)
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyMMdd");
    @Override
    @Transactional(rollbackFor = Exception.class)
    public String generateAndSaveEngineeringId(Date date) {
        // 乐观重试,防止并发写入造成重复键
        int retry = 0;
        final int maxRetry = 5;
        while (true) {
            try {
                // 1. 查询当天最大序号,并加行锁避免并发重复
                Integer maxSequence = baseMapper.selectMaxSequenceByDateForUpdate(date);
                if (maxSequence == null) {
                    maxSequence = 0;
                }
                // 2. 序号自增1
                int newSequence = maxSequence + 1;
                // 3. 生成工程号:P + yyMMdd + 两位序号
                LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
                String dateStr = localDate.format(DATE_FORMATTER);
                String engineeringId = "P" + dateStr + String.format("%02d", newSequence);
                // 4. 保存到数据库
                EngineeringSequence engineeringSequence = new EngineeringSequence();
                engineeringSequence.setEngineeringId(engineeringId);
                engineeringSequence.setDate(date);
                engineeringSequence.setSequence(newSequence);
                engineeringSequence.setCreatedTime(new Date());
                engineeringSequence.setUpdatedTime(new Date());
                engineeringSequence.setCreatedBy("system");
                engineeringSequence.setUpdatedBy("system");
                save(engineeringSequence);
                log.info("生成工程号成功: engineeringId={}, date={}, sequence={}", engineeringId, date, newSequence);
                return engineeringId;
            } catch (DuplicateKeyException dup) {
                // 并发导致的唯一键冲突,重试
                if (++retry > maxRetry) {
                    log.error("生成工程号重试超过上限, date={}", date, dup);
                    throw new RuntimeException("生成工程号失败", dup);
                }
                log.warn("工程号生成发生并发冲突,准备重试,第{}次,date={}", retry, date);
            } catch (Exception e) {
                log.error("生成工程号失败, date={}", date, e);
                throw new RuntimeException("生成工程号失败", e);
            }
        }
    }
}
mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/GlassInfoServiceImpl.java
@@ -5,19 +5,16 @@
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mes.device.entity.GlassInfo;
import com.mes.device.mapper.DeviceGlassInfoMapper;
import com.mes.device.service.EngineeringSequenceService;
import com.mes.device.service.GlassInfoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static java.util.stream.IntStream.range;
@@ -32,6 +29,9 @@
@RefreshScope
@Service("deviceGlassInfoService")
public class GlassInfoServiceImpl extends ServiceImpl<DeviceGlassInfoMapper, GlassInfo> implements GlassInfoService {
    @Autowired
    private EngineeringSequenceService engineeringSequenceService;
    @Override
    public GlassInfo getGlassInfo(String glassId) {
@@ -230,9 +230,8 @@
            return result;
        }
        // 工程号生成:P + yyMMdd + 序号(2位)
        AtomicInteger seq = new AtomicInteger(1);
        final String engineerId = generateEngineerId(firstValue(excelRows, "glassId"), seq.getAndIncrement());
        // 工程号生成:使用数据库自增序号,避免重复
        final String engineerId = engineeringSequenceService.generateAndSaveEngineeringId(new Date());
        final String filmsIdDefault = firstValue(excelRows, "filmsId", "白玻");
        final double thicknessDefault = parseDouble(firstValue(excelRows, "thickness"), 0d);
@@ -389,47 +388,6 @@
        return result;
    }
    // 日期格式化器(线程不安全,使用ThreadLocal保证线程安全)
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyMMdd");
    // 数字匹配正则(预编译提升性能)
    private static final Pattern DIGIT_PATTERN = Pattern.compile("\\d+");
    /**
     * 生成工程师ID
     * 格式规则:P + 年月日(yyMMdd) + 两位序号
     * 序号优先从glassId中提取末尾两位数字,否则使用传入的index补零
     *
     * @param glassId 玻璃ID(可为null,用于提取数字序号)
     * @param index   备用序号(当glassId无有效数字时使用)
     * @return 格式化的工程师ID(如:P25010801)
     */
    private String generateEngineerId(Object glassId, int index) {
        // 1. 生成日期前缀(yyMMdd)
        String base = LocalDate.now().format(DATE_FORMATTER);
        // 2. 初始化序号(两位补零)
        String seq = String.format("%02d", index);
        // 3. 从glassId中提取末尾两位数字(覆盖默认序号)
        if (glassId != null) {
            String glassIdStr = glassId.toString();
            Matcher matcher = DIGIT_PATTERN.matcher(glassIdStr);
            String lastDigitStr = null;
            // 遍历匹配所有数字段,取最后一个
            while (matcher.find()) {
                lastDigitStr = matcher.group();
            }
            // 若数字段长度≥2,取最后两位;否则保留原序号
            if (lastDigitStr != null && lastDigitStr.length() >= 2) {
                seq = lastDigitStr.substring(lastDigitStr.length() - 2);
            }
        }
        return "P" + base + seq;
    }
    /**
     * 提取List中第一个Map的指定key值(默认空字符串)
mes-processes/mes-plcSend/src/main/resources/db/migration/V20241121__create_engineering_table.sql
New file
@@ -0,0 +1,21 @@
-- 工程序号表
-- 用于管理工程号生成,避免重复
-- 每次导入Excel时,会根据日期自动生成递增的工程号
CREATE TABLE IF NOT EXISTS engineering_sequence (
    id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键ID',
    engineering_id VARCHAR(50) NOT NULL UNIQUE COMMENT '工程号(唯一标识,格式:P + yyMMdd + 两位序号)',
    date DATE NOT NULL COMMENT '日期(用于按日期分组生成序号)',
    sequence INT NOT NULL COMMENT '当天的序号(从1开始递增)',
    created_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    updated_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    created_by VARCHAR(50) DEFAULT 'system' COMMENT '创建人',
    updated_by VARCHAR(50) DEFAULT 'system' COMMENT '更新人',
    is_deleted TINYINT DEFAULT 0 COMMENT '是否删除:0-否,1-是',
    INDEX idx_engineering_id (engineering_id),
    INDEX idx_date (date),
    INDEX idx_date_sequence (date, sequence),
    INDEX idx_created_time (created_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='工程序号表';
mes-web/public/config.js
@@ -1,7 +1,7 @@
// const ip = '127.0.0.1'
const ip = '10.153.19.192'
const ip = '10.153.19.182'
// const ip = '10.100.0.183'
// const ip = '192.168.2.8'
window.ipConfig = {
    serverUrl: `${ip}:88`,
    serverUrl: `${ip}:89`,
}
mes-web/src/utils/constants.js
@@ -1,7 +1,7 @@
// export const WebSocketHost = "10.153.19.150";
// export const WebSocketHost = "172.17.2.7";
export const WebSocketHost = "10.153.19.166";//hxl
export const WebSocketHost = "10.153.19.192";//hxl
// export const WebSocketHost = "10.153.19.2";//zt
//export const WebSocketHost = "10.153.19.20";//wsx
// export const WebSocketHost = "127.0.0.1";
export const host = "88";
export const host = "89";