huang
4 天以前 9571229a2013472dc701ecf5767f2873b36d8f90
mes-processes/mes-plcSend/src/main/java/com/mes/device/service/impl/EngineeringSequenceServiceImpl.java
@@ -16,66 +16,54 @@
/**
 * 工程序号信息服务实现类
 *
 *
 * @author mes
 * @since 2024-11-20
 * @since 2025-11-20
 */
@Slf4j
@Service
public class EngineeringSequenceServiceImpl extends ServiceImpl<EngineeringSequenceMapper, EngineeringSequence> implements EngineeringSequenceService {
    // 日期格式化器(线程不安全,使用ThreadLocal保证线程安全)
    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyMMdd");
    // 修复:使用ThreadLocal保证DateTimeFormatter的线程安全
    private static final ThreadLocal<DateTimeFormatter> DATE_FORMATTER_THREAD_LOCAL = ThreadLocal.withInitial(
            () -> DateTimeFormatter.ofPattern("yyMMdd")
    );
    // 重试间隔(毫秒),使用随机数避免并发请求同时重试
    private static final int RETRY_INTERVAL_MIN = 50;
    private static final int RETRY_INTERVAL_MAX = 200;
    @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;
                }
        try {
            Integer maxSequence = baseMapper.selectMaxSequenceByDate(date);
            maxSequence = (maxSequence == null) ? 0 : maxSequence;
            int newSequence = maxSequence + 1;
                // 2. 序号自增1
                int newSequence = maxSequence + 1;
            LocalDate localDate = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
            String dateStr = DATE_FORMATTER_THREAD_LOCAL.get().format(localDate);
            String engineeringId = "P" + dateStr + String.format("%02d", newSequence);
                // 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);
            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");
                // 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);
                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);
            }
            log.info("生成工程号成功: engineeringId={}, date={}, sequence={}", engineeringId, date, newSequence);
            return engineeringId;
        } catch (DuplicateKeyException dup) {
            log.error("生成工程号唯一键冲突: date={}", date, dup);
            throw new RuntimeException("生成工程号失败", dup);
        } catch (Exception e) {
            log.error("生成工程号失败, date={}", date, e);
            throw new RuntimeException("生成工程号失败", e);
        }
    }
}
}