feat(enum): 统一结构化枚举值传输与同步

This commit is contained in:
2026-05-24 21:12:14 +08:00
parent bd8bfeb607
commit e37e8dfca6
23 changed files with 793 additions and 78 deletions

View File

@@ -5,7 +5,7 @@ import lombok.Getter;
@Getter
@AllArgsConstructor
public enum CommonStatusEnum {
public enum CommonStatusEnum implements PersistableSysEnumDefinition {
DISABLED(0, "禁用"),
ENABLED(1, "启用"),
@@ -14,7 +14,28 @@ public enum CommonStatusEnum {
COMPLETED(4, "已完成"),
FAILED(5, "失败");
private static final String CATALOG = "common";
private static final String TYPE = "common_status";
private static final String REMARK = "通用状态";
private final Integer value;
private final String label;
@Override
public String getCatalog() {
return CATALOG;
}
@Override
public String getType() {
return TYPE;
}
@Override
public String getRemark() {
return REMARK;
}
}

View File

@@ -5,12 +5,33 @@ import lombok.Getter;
@Getter
@AllArgsConstructor
public enum EnableStatusEnum {
public enum EnableStatusEnum implements PersistableSysEnumDefinition {
DISABLED(0, "禁用"),
ENABLED(1, "启用");
private static final String CATALOG = "common";
private static final String TYPE = "enable_status";
private static final String REMARK = "通用启用状态";
private final Integer value;
private final String label;
@Override
public String getCatalog() {
return CATALOG;
}
@Override
public String getType() {
return TYPE;
}
@Override
public String getRemark() {
return REMARK;
}
}

View File

@@ -0,0 +1,56 @@
package com.bruce.common.enums;
/**
* 可同步到 sys_enum 的枚举定义契约。
* <p>
* 长期固定的结构化文本字段统一通过该契约描述,
* 便于前后端传值、数据库初始化和后续协作保持同一事实来源。
*/
public interface PersistableSysEnumDefinition {
/**
* 枚举所属模块目录,例如 common、rag。
*/
String getCatalog();
/**
* 枚举所属类型,同一 catalog/type 视为同一个枚举组。
*/
String getType();
/**
* 落库到 sys_enum.name 的展示名称。
*/
default String getName() {
return getLabel();
}
/**
* 枚举整型值,前后端协议统一传该值。
*/
Integer getValue();
/**
* 可选的字符串值,当前大多数业务枚举不使用。
*/
default String getStrvalue() {
return null;
}
/**
* 排序值,默认与枚举值保持一致。
*/
default Integer getSort() {
return getValue();
}
/**
* sys_enum.remark 中的说明文本。
*/
String getRemark();
/**
* 枚举显示文案,通常与 name 保持一致。
*/
String getLabel();
}

View File

@@ -25,4 +25,6 @@ public interface ISysEnumService extends IService<SysEnum> {
boolean saveOrUpdate(SysEnumSaveRequest request);
boolean batchSave(SysEnumBatchSaveRequest request);
boolean replaceByCatalogAndType(String catalog, String type, List<SysEnum> items);
}

View File

@@ -10,6 +10,7 @@ import com.bruce.common.dto.response.SysEnumResponse;
import com.bruce.common.mapper.SysEnumMapper;
import com.bruce.common.service.ISysEnumService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
@@ -137,6 +138,24 @@ public class SysEnumServiceImpl extends ServiceImpl<SysEnumMapper, SysEnum> impl
return result;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean replaceByCatalogAndType(String catalog, String type, List<SysEnum> items) {
log.info("SysEnumServiceImpl.replaceByCatalogAndType start, catalog={}, type={}, itemCount={}",
catalog, type, items == null ? 0 : items.size());
validateReplaceByCatalogAndTypeRequest(catalog, type, items);
boolean removed = lambdaUpdate()
.eq(SysEnum::getCatalog, catalog)
.eq(SysEnum::getType, type)
.remove();
boolean saved = saveBatch(items);
boolean result = removed || saved;
log.info("SysEnumServiceImpl.replaceByCatalogAndType success, catalog={}, type={}, removed={}, saved={}, result={}",
catalog, type, removed, saved, result);
return result;
}
public void validateBatchSaveRequest(SysEnumBatchSaveRequest request, List<SysEnum> existingEnums) {
log.info("SysEnumServiceImpl.validateBatchSaveRequest start");
if (request == null) {
@@ -183,6 +202,44 @@ public class SysEnumServiceImpl extends ServiceImpl<SysEnumMapper, SysEnum> impl
request.getCatalog(), request.getType(), requestValues.size(), existingValues.size());
}
private void validateReplaceByCatalogAndTypeRequest(String catalog, String type, List<SysEnum> items) {
if (!StringUtils.hasText(catalog)) {
throw new IllegalArgumentException("模块目录不能为空");
}
if (!StringUtils.hasText(type)) {
throw new IllegalArgumentException("枚举类型不能为空");
}
if (items == null || items.isEmpty()) {
throw new IllegalArgumentException("枚举项不能为空");
}
Set<Integer> values = new HashSet<>();
Set<Integer> sorts = new HashSet<>();
for (SysEnum item : items) {
if (item == null) {
throw new IllegalArgumentException("枚举项不能为空");
}
if (!catalog.equals(item.getCatalog()) || !type.equals(item.getType())) {
throw new IllegalArgumentException("替换的枚举项 catalog/type 必须与目标分组一致");
}
if (!StringUtils.hasText(item.getName())) {
throw new IllegalArgumentException("枚举名称不能为空");
}
if (item.getValue() == null) {
throw new IllegalArgumentException("枚举整型值不能为空");
}
if (!values.add(item.getValue())) {
throw new IllegalArgumentException("枚举值重复: " + item.getValue());
}
if (item.getSort() == null) {
throw new IllegalArgumentException("枚举排序不能为空");
}
if (!sorts.add(item.getSort())) {
throw new IllegalArgumentException("枚举排序重复: " + item.getSort());
}
}
}
private List<SysEnumResponse> toResponses(List<SysEnum> enums) {
return enums.stream()
.map(SysEnumResponse::fromEntity)

View File

@@ -12,8 +12,8 @@ public class RagDocumentParseRequest {
@Schema(description = "文档ID列表")
private List<Long> documentIds;
@Schema(description = "切片方式")
private String chunkStrategy;
@Schema(description = "切片方式枚举值")
private Integer chunkStrategy;
@Schema(description = "切片长度")
private Integer chunkSize;

View File

@@ -1,11 +1,14 @@
package com.bruce.rag.enums;
import com.bruce.common.enums.PersistableSysEnumDefinition;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
@Getter
@AllArgsConstructor
public enum RagChunkStrategyEnum {
public enum RagChunkStrategyEnum implements PersistableSysEnumDefinition {
FIXED_LENGTH(1, "固定长度切片"),
PARAGRAPH(2, "按段落切片"),
@@ -14,7 +17,35 @@ public enum RagChunkStrategyEnum {
DELIMITER(5, "按分隔符切片"),
SEMANTIC(6, "语义切片");
private static final String CATALOG = "rag";
private static final String TYPE = "chunk_strategy";
private static final String REMARK = "RAG文档切片方式";
private final Integer value;
private final String label;
public static RagChunkStrategyEnum fromValue(Integer value) {
return Arrays.stream(values())
.filter(item -> item.getValue().equals(value))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("不支持的切片方式: " + value));
}
@Override
public String getCatalog() {
return CATALOG;
}
@Override
public String getType() {
return TYPE;
}
@Override
public String getRemark() {
return REMARK;
}
}

View File

@@ -1,18 +1,40 @@
package com.bruce.rag.enums;
import com.bruce.common.enums.PersistableSysEnumDefinition;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum RagIndexStatusEnum {
public enum RagIndexStatusEnum implements PersistableSysEnumDefinition {
PENDING(1, "待索引"),
INDEXING(2, "索引中"),
INDEXED(3, "已索引"),
FAILED(4, "索引失败");
private static final String CATALOG = "rag";
private static final String TYPE = "index_status";
private static final String REMARK = "RAG文档索引状态";
private final Integer value;
private final String label;
@Override
public String getCatalog() {
return CATALOG;
}
@Override
public String getType() {
return TYPE;
}
@Override
public String getRemark() {
return REMARK;
}
}

View File

@@ -1,18 +1,40 @@
package com.bruce.rag.enums;
import com.bruce.common.enums.PersistableSysEnumDefinition;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum RagParseStatusEnum {
public enum RagParseStatusEnum implements PersistableSysEnumDefinition {
UPLOADED(1, "已上传"),
PARSING(2, "解析中"),
PARSED(3, "已解析"),
FAILED(4, "解析失败");
private static final String CATALOG = "rag";
private static final String TYPE = "parse_status";
private static final String REMARK = "RAG文档解析状态";
private final Integer value;
private final String label;
@Override
public String getCatalog() {
return CATALOG;
}
@Override
public String getType() {
return TYPE;
}
@Override
public String getRemark() {
return REMARK;
}
}

View File

@@ -22,10 +22,7 @@ import org.springframework.util.StringUtils;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@Slf4j
@Service
@@ -95,12 +92,7 @@ public class RagDocumentParseServiceImpl implements IRagDocumentParseService {
if (request.getDocumentIds() == null || request.getDocumentIds().isEmpty()) {
throw new IllegalArgumentException("文档ID列表不能为空");
}
Set<String> strategies = Arrays.stream(RagChunkStrategyEnum.values())
.map(Enum::name)
.collect(Collectors.toSet());
if (request.getChunkStrategy() == null || !strategies.contains(request.getChunkStrategy())) {
throw new IllegalArgumentException("不支持的切片方式: " + request.getChunkStrategy());
}
RagChunkStrategyEnum.fromValue(request.getChunkStrategy());
}
private DocumentParseContext buildParseContext(RagDocument document, SysAttachment attachment) {