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 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); } } } }