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;
|
||||
|
||||
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.SequenceSegment;
|
||||
import cn.code.nl.module.base.service.codegen.enums.LetterCaseEnum;
|
||||
import cn.code.nl.module.base.service.codegen.enums.SeqStrategyEnum;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
@@ -61,27 +58,25 @@ public class CodeGenServiceImpl implements CodeGenService {
|
||||
* 根据段类型分发处理,生成段值
|
||||
*/
|
||||
private String buildSegment(CodeRuleDO rule, SegmentConfig segment) {
|
||||
if (segment instanceof PrefixSegment) {
|
||||
return buildPrefixSegment((PrefixSegment) segment);
|
||||
} else if (segment instanceof DateSegment) {
|
||||
return buildDateSegment((DateSegment) segment);
|
||||
} else if (segment instanceof SequenceSegment) {
|
||||
return buildSequenceSegment(rule, (SequenceSegment) segment);
|
||||
}
|
||||
throw new IllegalArgumentException("不支持的段类型: " + segment.getClass().getSimpleName());
|
||||
return switch (segment.getType()) {
|
||||
case "PREFIX" -> buildPrefixSegment(segment);
|
||||
case "DATE" -> buildDateSegment(segment);
|
||||
case "SEQUENCE" -> buildSequenceSegment(rule, segment);
|
||||
default -> throw new IllegalArgumentException("不支持的段类型: " + segment.getType());
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成固定前缀段
|
||||
*/
|
||||
private String buildPrefixSegment(PrefixSegment segment) {
|
||||
private String buildPrefixSegment(SegmentConfig segment) {
|
||||
return segment.getValue() != null ? segment.getValue() : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成日期段
|
||||
*/
|
||||
private String buildDateSegment(DateSegment segment) {
|
||||
private String buildDateSegment(SegmentConfig segment) {
|
||||
String format = StrUtil.isNotBlank(segment.getFormat())
|
||||
? segment.getFormat() : "yyyyMMdd";
|
||||
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();
|
||||
if (SeqStrategyEnum.LOCK.getCode().equals(strategy)) {
|
||||
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.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.hutool.core.util.StrUtil;
|
||||
import com.baomidou.lock.annotation.Lock4j;
|
||||
@@ -57,7 +57,7 @@ public class SequenceAllocator {
|
||||
* @param segment 序号段配置
|
||||
* @return 格式化后的序号字符串
|
||||
*/
|
||||
public String allocateBySegment(Long ruleId, SequenceSegment segment) {
|
||||
public String allocateBySegment(Long ruleId, SegmentConfig segment) {
|
||||
String resetKey = buildResetKey(segment.getResetBy());
|
||||
String cacheKey = ruleId + ":" + resetKey;
|
||||
|
||||
@@ -87,7 +87,7 @@ public class SequenceAllocator {
|
||||
* @param segment 序号段配置
|
||||
* @return 格式化后的序号字符串
|
||||
*/
|
||||
public String allocateByLock(Long ruleId, SequenceSegment segment) {
|
||||
public String allocateByLock(Long ruleId, SegmentConfig segment) {
|
||||
String resetKey = buildResetKey(segment.getResetBy());
|
||||
String lockKey = "codegen:seq:lock:" + ruleId + ":" + resetKey;
|
||||
return self.allocateWithLock(lockKey, ruleId, resetKey, segment);
|
||||
@@ -98,7 +98,7 @@ public class SequenceAllocator {
|
||||
*/
|
||||
@Lock4j(keys = "#lockKey", acquireTimeout = 3000, expire = 5000)
|
||||
public long fetchSegmentWithLock(String lockKey, String cacheKey, Long ruleId,
|
||||
String resetKey, SequenceSegment segment) {
|
||||
String resetKey, SegmentConfig segment) {
|
||||
// double-check:其他线程可能已经预取了
|
||||
AtomicLong existing = localSegmentCache.get(cacheKey);
|
||||
if (existing != null) {
|
||||
@@ -136,7 +136,7 @@ public class SequenceAllocator {
|
||||
* Lock4j 保护下实时分配序号
|
||||
*/
|
||||
@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;
|
||||
// step=1 每次只取一个号
|
||||
codeSequenceMapper.updateCurrentValueBySegment(ruleId, resetKey, 1, startAt, segment.getMaxValue());
|
||||
@@ -178,7 +178,7 @@ public class SequenceAllocator {
|
||||
* @param segment 序号段配置
|
||||
* @return 格式化后的字符串
|
||||
*/
|
||||
public String formatSequence(long value, SequenceSegment segment) {
|
||||
public String formatSequence(long value, SegmentConfig segment) {
|
||||
String raw = String.valueOf(value);
|
||||
int paddingLen = segment.getPaddingLen() != null ? segment.getPaddingLen() : 0;
|
||||
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;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 分段配置基类,使用 Jackson 多态序列化/反序列化。
|
||||
* 注意:type 字段由 @JsonTypeInfo 自动管理,不要在子类中重复定义,
|
||||
* 否则会导致序列化时 type 被覆盖为 null,反序列化时找不到子类型。
|
||||
* 分段配置 —— 通用 DTO,不做 Jackson 多态。
|
||||
* type 决定了哪些字段有效:PREFIX→value, DATE→format, SEQUENCE→resetBy/startAt/paddingLen/paddingChar/maxValue
|
||||
*
|
||||
* @author zhouz
|
||||
*/
|
||||
@Data
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type", visible = true)
|
||||
@JsonSubTypes({
|
||||
@JsonSubTypes.Type(value = PrefixSegment.class, name = "PREFIX"),
|
||||
@JsonSubTypes.Type(value = DateSegment.class, name = "DATE"),
|
||||
@JsonSubTypes.Type(value = SequenceSegment.class, name = "SEQUENCE")
|
||||
})
|
||||
public abstract class SegmentConfig {
|
||||
public class SegmentConfig {
|
||||
|
||||
/**
|
||||
* 段类型:PREFIX / DATE / SEQUENCE
|
||||
*/
|
||||
private String type;
|
||||
|
||||
// ========== 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