huang
5 天以前 22e17b6db03ca58bc477a35ca067e55a09cffce7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
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);
            }
        }
    }
}