|
|
@@ -0,0 +1,180 @@
|
|
|
+package com.skyversation.xjcy.service;
|
|
|
+
|
|
|
+import lombok.Getter;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import javax.annotation.PostConstruct;
|
|
|
+import javax.annotation.Resource;
|
|
|
+import java.time.LocalDate;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
+import java.util.EnumMap;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.concurrent.ConcurrentHashMap;
|
|
|
+import java.util.concurrent.atomic.AtomicInteger;
|
|
|
+import java.util.concurrent.locks.ReentrantLock;
|
|
|
+
|
|
|
+@Service
|
|
|
+public class SerialNumberGenerator {
|
|
|
+ @Resource
|
|
|
+ DMSService dmsService;
|
|
|
+
|
|
|
+ // 前缀枚举定义(可根据需要扩展)
|
|
|
+ @Getter
|
|
|
+ public enum Prefix {
|
|
|
+ XS("XS", "1576", "c_clue_code"),
|
|
|
+ //PT("PT","" ,"" ) ,
|
|
|
+ ED("ED", "1587", "c_work_order_code"),
|
|
|
+ EP("EP", "1592", "c_product_code"),
|
|
|
+ PR("PR", "1591", "c_requirement_code");
|
|
|
+ private final String code;
|
|
|
+ private final String columId;
|
|
|
+ private final String codeColumn;
|
|
|
+
|
|
|
+ Prefix(String code, String columId, String codeColumn) {
|
|
|
+ this.code = code;
|
|
|
+ this.columId = columId;
|
|
|
+ this.codeColumn = codeColumn;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // 存储每个前缀的当前状态
|
|
|
+ private final Map<Prefix, SerialState> stateMap = new ConcurrentHashMap<>();
|
|
|
+ // 存储每个前缀的初始化状态
|
|
|
+ private final Map<Prefix, Boolean> initializedMap = new ConcurrentHashMap<>();
|
|
|
+ // 用于初始化时的同步锁
|
|
|
+ private final Map<Prefix, ReentrantLock> initLocks = new EnumMap<>(Prefix.class);
|
|
|
+
|
|
|
+ // 日期格式化器(线程安全)
|
|
|
+ private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.BASIC_ISO_DATE;
|
|
|
+
|
|
|
+ public SerialNumberGenerator() {
|
|
|
+ // 为每个前缀初始化锁
|
|
|
+ for (Prefix prefix : Prefix.values()) {
|
|
|
+ initLocks.put(prefix, new ReentrantLock());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @PostConstruct
|
|
|
+ public void autoInit() {
|
|
|
+ String token;
|
|
|
+ try {
|
|
|
+ token = dmsService.loginForToken();
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException(e);
|
|
|
+ }
|
|
|
+ for (Prefix prefix : Prefix.values()) {
|
|
|
+ try {
|
|
|
+ String today = currentDate();
|
|
|
+ int max = dmsService.getLargestCode(token, prefix.getColumId(), prefix.getCodeColumn(), today);
|
|
|
+ initPrefix(prefix, max+1 , today);
|
|
|
+ } catch (Exception e) {
|
|
|
+ System.out.println(prefix+"流水号初始化失败");
|
|
|
+ e.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 生成流水号
|
|
|
+ *
|
|
|
+ * @param prefix 前缀枚举
|
|
|
+ * @return 生成的流水号字符串
|
|
|
+ * @throws IllegalStateException 如果前缀未初始化
|
|
|
+ */
|
|
|
+ public String generate(Prefix prefix) {
|
|
|
+ // 检查是否已初始化
|
|
|
+ if (!isInitialized(prefix)) {
|
|
|
+ throw new IllegalStateException("Prefix " + prefix + " not initialized. Call initPrefix() first.");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取或创建当前状态
|
|
|
+ SerialState currentState = getOrCreateState(prefix);
|
|
|
+
|
|
|
+ // 原子递增并格式化
|
|
|
+ int nextSerial = currentState.counter.incrementAndGet();
|
|
|
+ if (nextSerial > 999) {
|
|
|
+ throw new IllegalStateException("Serial number overflow for prefix: " + prefix);
|
|
|
+ }
|
|
|
+
|
|
|
+ return prefix.getCode() + currentState.date + String.format("%03d", nextSerial);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 初始化指定前缀的流水号
|
|
|
+ *
|
|
|
+ * @param prefix 前缀枚举
|
|
|
+ * @param initialValue 初始值(1-999)
|
|
|
+ * @param today 初始化数据的时间
|
|
|
+ * @throws IllegalArgumentException 初始值不在有效范围
|
|
|
+ * @throws IllegalStateException 如果前缀已初始化
|
|
|
+ */
|
|
|
+ public void initPrefix(Prefix prefix, int initialValue, String today) {
|
|
|
+ if (initialValue < 1 || initialValue > 999) {
|
|
|
+ throw new IllegalArgumentException("Initial value must be between 1 and 999");
|
|
|
+ }
|
|
|
+
|
|
|
+ ReentrantLock lock = initLocks.get(prefix);
|
|
|
+ lock.lock();
|
|
|
+ try {
|
|
|
+ if (isInitialized(prefix)) {
|
|
|
+ throw new IllegalStateException("Prefix " + prefix + " already initialized");
|
|
|
+ }
|
|
|
+
|
|
|
+ stateMap.put(prefix, new SerialState(today, new AtomicInteger(initialValue - 1)));
|
|
|
+ initializedMap.put(prefix, true);
|
|
|
+ } finally {
|
|
|
+ lock.unlock();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 检查前缀是否已初始化
|
|
|
+ */
|
|
|
+ public boolean isInitialized(Prefix prefix) {
|
|
|
+ return initializedMap.getOrDefault(prefix, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取或创建当前状态(处理日期切换)
|
|
|
+ private SerialState getOrCreateState(Prefix prefix) {
|
|
|
+ String today = currentDate();
|
|
|
+ SerialState currentState = stateMap.get(prefix);
|
|
|
+
|
|
|
+ // 如果状态存在且日期匹配,直接返回
|
|
|
+ if (currentState != null && currentState.date.equals(today)) {
|
|
|
+ return currentState;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 处理日期切换或状态不存在的情况
|
|
|
+ ReentrantLock lock = initLocks.get(prefix);
|
|
|
+ lock.lock();
|
|
|
+ try {
|
|
|
+ // 双重检查锁定模式
|
|
|
+ currentState = stateMap.get(prefix);
|
|
|
+ if (currentState == null || !currentState.date.equals(today)) {
|
|
|
+ // 创建新的状态(计数器从0开始)
|
|
|
+ currentState = new SerialState(today, new AtomicInteger(0));
|
|
|
+ stateMap.put(prefix, currentState);
|
|
|
+ }
|
|
|
+ return currentState;
|
|
|
+ } finally {
|
|
|
+ lock.unlock();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取当前日期字符串(yyyyMMdd)
|
|
|
+ private String currentDate() {
|
|
|
+ return LocalDate.now().format(DATE_FORMATTER);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 状态存储内部类
|
|
|
+ private static class SerialState {
|
|
|
+ final String date; // 当前日期(yyyyMMdd)
|
|
|
+ final AtomicInteger counter; // 当前计数器值
|
|
|
+
|
|
|
+ SerialState(String date, AtomicInteger counter) {
|
|
|
+ this.date = date;
|
|
|
+ this.counter = counter;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|