fix: 放弃Jackson多态,改用普通DTO避免type序列化冲突
- SegmentConfig改为普通POJO,移除@JsonTypeInfo和子类 - CodeGenServiceImpl/SequenceAllocator直接用SegmentConfig - 彻底消除type字段序列化/反序列化的冲突问题
This commit is contained in:
@@ -1,10 +1,7 @@
|
|||||||
package cn.code.nl.module.base.service.codegen;
|
package cn.code.nl.module.base.service.codegen;
|
||||||
|
|
||||||
import cn.code.nl.module.base.dal.dataobject.codegen.CodeRuleDO;
|
import cn.code.nl.module.base.dal.dataobject.codegen.CodeRuleDO;
|
||||||
import cn.code.nl.module.base.service.codegen.dto.DateSegment;
|
|
||||||
import cn.code.nl.module.base.service.codegen.dto.PrefixSegment;
|
|
||||||
import cn.code.nl.module.base.service.codegen.dto.SegmentConfig;
|
import cn.code.nl.module.base.service.codegen.dto.SegmentConfig;
|
||||||
import cn.code.nl.module.base.service.codegen.dto.SequenceSegment;
|
|
||||||
import cn.code.nl.module.base.service.codegen.enums.LetterCaseEnum;
|
import cn.code.nl.module.base.service.codegen.enums.LetterCaseEnum;
|
||||||
import cn.code.nl.module.base.service.codegen.enums.SeqStrategyEnum;
|
import cn.code.nl.module.base.service.codegen.enums.SeqStrategyEnum;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
@@ -61,27 +58,25 @@ public class CodeGenServiceImpl implements CodeGenService {
|
|||||||
* 根据段类型分发处理,生成段值
|
* 根据段类型分发处理,生成段值
|
||||||
*/
|
*/
|
||||||
private String buildSegment(CodeRuleDO rule, SegmentConfig segment) {
|
private String buildSegment(CodeRuleDO rule, SegmentConfig segment) {
|
||||||
if (segment instanceof PrefixSegment) {
|
return switch (segment.getType()) {
|
||||||
return buildPrefixSegment((PrefixSegment) segment);
|
case "PREFIX" -> buildPrefixSegment(segment);
|
||||||
} else if (segment instanceof DateSegment) {
|
case "DATE" -> buildDateSegment(segment);
|
||||||
return buildDateSegment((DateSegment) segment);
|
case "SEQUENCE" -> buildSequenceSegment(rule, segment);
|
||||||
} else if (segment instanceof SequenceSegment) {
|
default -> throw new IllegalArgumentException("不支持的段类型: " + segment.getType());
|
||||||
return buildSequenceSegment(rule, (SequenceSegment) segment);
|
};
|
||||||
}
|
|
||||||
throw new IllegalArgumentException("不支持的段类型: " + segment.getClass().getSimpleName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成固定前缀段
|
* 生成固定前缀段
|
||||||
*/
|
*/
|
||||||
private String buildPrefixSegment(PrefixSegment segment) {
|
private String buildPrefixSegment(SegmentConfig segment) {
|
||||||
return segment.getValue() != null ? segment.getValue() : "";
|
return segment.getValue() != null ? segment.getValue() : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成日期段
|
* 生成日期段
|
||||||
*/
|
*/
|
||||||
private String buildDateSegment(DateSegment segment) {
|
private String buildDateSegment(SegmentConfig segment) {
|
||||||
String format = StrUtil.isNotBlank(segment.getFormat())
|
String format = StrUtil.isNotBlank(segment.getFormat())
|
||||||
? segment.getFormat() : "yyyyMMdd";
|
? segment.getFormat() : "yyyyMMdd";
|
||||||
return LocalDate.now().format(DateTimeFormatter.ofPattern(format));
|
return LocalDate.now().format(DateTimeFormatter.ofPattern(format));
|
||||||
@@ -90,7 +85,7 @@ public class CodeGenServiceImpl implements CodeGenService {
|
|||||||
/**
|
/**
|
||||||
* 生成流水序号段
|
* 生成流水序号段
|
||||||
*/
|
*/
|
||||||
private String buildSequenceSegment(CodeRuleDO rule, SequenceSegment segment) {
|
private String buildSequenceSegment(CodeRuleDO rule, SegmentConfig segment) {
|
||||||
String strategy = rule.getSeqStrategy();
|
String strategy = rule.getSeqStrategy();
|
||||||
if (SeqStrategyEnum.LOCK.getCode().equals(strategy)) {
|
if (SeqStrategyEnum.LOCK.getCode().equals(strategy)) {
|
||||||
return sequenceAllocator.allocateByLock(rule.getId(), segment);
|
return sequenceAllocator.allocateByLock(rule.getId(), segment);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package cn.code.nl.module.base.service.codegen;
|
|||||||
|
|
||||||
import cn.code.nl.module.base.dal.dataobject.codegen.CodeSequenceDO;
|
import cn.code.nl.module.base.dal.dataobject.codegen.CodeSequenceDO;
|
||||||
import cn.code.nl.module.base.dal.mysql.codegen.CodeSequenceMapper;
|
import cn.code.nl.module.base.dal.mysql.codegen.CodeSequenceMapper;
|
||||||
import cn.code.nl.module.base.service.codegen.dto.SequenceSegment;
|
import cn.code.nl.module.base.service.codegen.dto.SegmentConfig;
|
||||||
import cn.code.nl.module.base.service.codegen.enums.ResetByEnum;
|
import cn.code.nl.module.base.service.codegen.enums.ResetByEnum;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.baomidou.lock.annotation.Lock4j;
|
import com.baomidou.lock.annotation.Lock4j;
|
||||||
@@ -57,7 +57,7 @@ public class SequenceAllocator {
|
|||||||
* @param segment 序号段配置
|
* @param segment 序号段配置
|
||||||
* @return 格式化后的序号字符串
|
* @return 格式化后的序号字符串
|
||||||
*/
|
*/
|
||||||
public String allocateBySegment(Long ruleId, SequenceSegment segment) {
|
public String allocateBySegment(Long ruleId, SegmentConfig segment) {
|
||||||
String resetKey = buildResetKey(segment.getResetBy());
|
String resetKey = buildResetKey(segment.getResetBy());
|
||||||
String cacheKey = ruleId + ":" + resetKey;
|
String cacheKey = ruleId + ":" + resetKey;
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ public class SequenceAllocator {
|
|||||||
* @param segment 序号段配置
|
* @param segment 序号段配置
|
||||||
* @return 格式化后的序号字符串
|
* @return 格式化后的序号字符串
|
||||||
*/
|
*/
|
||||||
public String allocateByLock(Long ruleId, SequenceSegment segment) {
|
public String allocateByLock(Long ruleId, SegmentConfig segment) {
|
||||||
String resetKey = buildResetKey(segment.getResetBy());
|
String resetKey = buildResetKey(segment.getResetBy());
|
||||||
String lockKey = "codegen:seq:lock:" + ruleId + ":" + resetKey;
|
String lockKey = "codegen:seq:lock:" + ruleId + ":" + resetKey;
|
||||||
return self.allocateWithLock(lockKey, ruleId, resetKey, segment);
|
return self.allocateWithLock(lockKey, ruleId, resetKey, segment);
|
||||||
@@ -98,7 +98,7 @@ public class SequenceAllocator {
|
|||||||
*/
|
*/
|
||||||
@Lock4j(keys = "#lockKey", acquireTimeout = 3000, expire = 5000)
|
@Lock4j(keys = "#lockKey", acquireTimeout = 3000, expire = 5000)
|
||||||
public long fetchSegmentWithLock(String lockKey, String cacheKey, Long ruleId,
|
public long fetchSegmentWithLock(String lockKey, String cacheKey, Long ruleId,
|
||||||
String resetKey, SequenceSegment segment) {
|
String resetKey, SegmentConfig segment) {
|
||||||
// double-check:其他线程可能已经预取了
|
// double-check:其他线程可能已经预取了
|
||||||
AtomicLong existing = localSegmentCache.get(cacheKey);
|
AtomicLong existing = localSegmentCache.get(cacheKey);
|
||||||
if (existing != null) {
|
if (existing != null) {
|
||||||
@@ -136,7 +136,7 @@ public class SequenceAllocator {
|
|||||||
* Lock4j 保护下实时分配序号
|
* Lock4j 保护下实时分配序号
|
||||||
*/
|
*/
|
||||||
@Lock4j(keys = "#lockKey", acquireTimeout = 3000, expire = 5000)
|
@Lock4j(keys = "#lockKey", acquireTimeout = 3000, expire = 5000)
|
||||||
public String allocateWithLock(String lockKey, Long ruleId, String resetKey, SequenceSegment segment) {
|
public String allocateWithLock(String lockKey, Long ruleId, String resetKey, SegmentConfig segment) {
|
||||||
Long startAt = segment.getStartAt() != null ? segment.getStartAt() : 1L;
|
Long startAt = segment.getStartAt() != null ? segment.getStartAt() : 1L;
|
||||||
// step=1 每次只取一个号
|
// step=1 每次只取一个号
|
||||||
codeSequenceMapper.updateCurrentValueBySegment(ruleId, resetKey, 1, startAt, segment.getMaxValue());
|
codeSequenceMapper.updateCurrentValueBySegment(ruleId, resetKey, 1, startAt, segment.getMaxValue());
|
||||||
@@ -178,7 +178,7 @@ public class SequenceAllocator {
|
|||||||
* @param segment 序号段配置
|
* @param segment 序号段配置
|
||||||
* @return 格式化后的字符串
|
* @return 格式化后的字符串
|
||||||
*/
|
*/
|
||||||
public String formatSequence(long value, SequenceSegment segment) {
|
public String formatSequence(long value, SegmentConfig segment) {
|
||||||
String raw = String.valueOf(value);
|
String raw = String.valueOf(value);
|
||||||
int paddingLen = segment.getPaddingLen() != null ? segment.getPaddingLen() : 0;
|
int paddingLen = segment.getPaddingLen() != null ? segment.getPaddingLen() : 0;
|
||||||
if (paddingLen <= 0 || raw.length() >= paddingLen) {
|
if (paddingLen <= 0 || raw.length() >= paddingLen) {
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
package cn.code.nl.module.base.service.codegen.dto;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 日期段配置
|
|
||||||
*
|
|
||||||
* @author zhouz
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
public class DateSegment extends SegmentConfig {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 日期格式,如 "yyyyMMdd"、"yyyyMM"、"yyyy"
|
|
||||||
*/
|
|
||||||
private String format;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
package cn.code.nl.module.base.service.codegen.dto;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 固定前缀段配置
|
|
||||||
*
|
|
||||||
* @author zhouz
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
public class PrefixSegment extends SegmentConfig {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 固定前缀值,如 "ORD"
|
|
||||||
*/
|
|
||||||
private String value;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,22 +1,60 @@
|
|||||||
package cn.code.nl.module.base.service.codegen.dto;
|
package cn.code.nl.module.base.service.codegen.dto;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
|
||||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 分段配置基类,使用 Jackson 多态序列化/反序列化。
|
* 分段配置 —— 通用 DTO,不做 Jackson 多态。
|
||||||
* 注意:type 字段由 @JsonTypeInfo 自动管理,不要在子类中重复定义,
|
* type 决定了哪些字段有效:PREFIX→value, DATE→format, SEQUENCE→resetBy/startAt/paddingLen/paddingChar/maxValue
|
||||||
* 否则会导致序列化时 type 被覆盖为 null,反序列化时找不到子类型。
|
|
||||||
*
|
*
|
||||||
* @author zhouz
|
* @author zhouz
|
||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", visible = true)
|
public class SegmentConfig {
|
||||||
@JsonSubTypes({
|
|
||||||
@JsonSubTypes.Type(value = PrefixSegment.class, name = "PREFIX"),
|
/**
|
||||||
@JsonSubTypes.Type(value = DateSegment.class, name = "DATE"),
|
* 段类型:PREFIX / DATE / SEQUENCE
|
||||||
@JsonSubTypes.Type(value = SequenceSegment.class, name = "SEQUENCE")
|
*/
|
||||||
})
|
private String type;
|
||||||
public abstract class SegmentConfig {
|
|
||||||
|
// ========== PREFIX 段 ==========
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 固定值,如 "ORD"
|
||||||
|
*/
|
||||||
|
private String value;
|
||||||
|
|
||||||
|
// ========== DATE 段 ==========
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 日期格式,如 "yyyyMMdd"
|
||||||
|
*/
|
||||||
|
private String format;
|
||||||
|
|
||||||
|
// ========== SEQUENCE 段 ==========
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置维度:DAY / MONTH / YEAR / GLOBAL
|
||||||
|
*/
|
||||||
|
private String resetBy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 起始值,默认 1
|
||||||
|
*/
|
||||||
|
private Long startAt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 补位后的总长度,如 4 → 0001
|
||||||
|
*/
|
||||||
|
private Integer paddingLen;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 补位字符,默认 "0"
|
||||||
|
*/
|
||||||
|
private String paddingChar;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 序号最大值上限
|
||||||
|
*/
|
||||||
|
private Long maxValue;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
package cn.code.nl.module.base.service.codegen.dto;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 流水序号段配置
|
|
||||||
*
|
|
||||||
* @author zhouz
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
public class SequenceSegment extends SegmentConfig {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 重置维度:DAY / MONTH / YEAR / GLOBAL
|
|
||||||
*/
|
|
||||||
private String resetBy;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 起始值,默认 1
|
|
||||||
*/
|
|
||||||
private Long startAt;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 补位后的总长度,如 4 → 0001
|
|
||||||
*/
|
|
||||||
private Integer paddingLen;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 补位字符,默认 "0"
|
|
||||||
*/
|
|
||||||
private String paddingChar;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 序号最大值上限
|
|
||||||
*/
|
|
||||||
private Long maxValue;
|
|
||||||
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user