feat(enum): 统一结构化枚举值传输与同步
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -2,12 +2,14 @@ package com.bruce.common.enumconfig;
|
||||
|
||||
import com.bruce.common.enums.CommonStatusEnum;
|
||||
import com.bruce.common.enums.EnableStatusEnum;
|
||||
import com.bruce.rag.enums.RagIndexStatusEnum;
|
||||
import com.bruce.common.enums.PersistableSysEnumDefinition;
|
||||
import com.bruce.rag.enums.RagChunkStrategyEnum;
|
||||
import com.bruce.rag.enums.RagIndexStatusEnum;
|
||||
import com.bruce.rag.enums.RagParseStatusEnum;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
class EnumDefinitionTests {
|
||||
|
||||
@@ -46,4 +48,33 @@ class EnumDefinitionTests {
|
||||
assertEquals("按分隔符切片", RagChunkStrategyEnum.DELIMITER.getLabel());
|
||||
assertEquals("语义切片", RagChunkStrategyEnum.SEMANTIC.getLabel());
|
||||
}
|
||||
|
||||
@Test
|
||||
void enumsShouldExposeStableSysEnumMetadata() {
|
||||
PersistableSysEnumDefinition chunkStrategy = RagChunkStrategyEnum.DELIMITER;
|
||||
PersistableSysEnumDefinition parseStatus = RagParseStatusEnum.PARSED;
|
||||
PersistableSysEnumDefinition enableStatus = EnableStatusEnum.ENABLED;
|
||||
|
||||
assertEquals("rag", chunkStrategy.getCatalog());
|
||||
assertEquals("chunk_strategy", chunkStrategy.getType());
|
||||
assertEquals("按分隔符切片", chunkStrategy.getName());
|
||||
assertEquals(5, chunkStrategy.getValue());
|
||||
assertEquals(5, chunkStrategy.getSort());
|
||||
assertEquals("RAG文档切片方式", chunkStrategy.getRemark());
|
||||
|
||||
assertEquals("rag", parseStatus.getCatalog());
|
||||
assertEquals("parse_status", parseStatus.getType());
|
||||
assertEquals("已解析", parseStatus.getName());
|
||||
|
||||
assertEquals("common", enableStatus.getCatalog());
|
||||
assertEquals("enable_status", enableStatus.getType());
|
||||
assertEquals("启用", enableStatus.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void ragChunkStrategyShouldResolveByIntegerValue() {
|
||||
assertEquals(RagChunkStrategyEnum.FIXED_LENGTH, RagChunkStrategyEnum.fromValue(1));
|
||||
assertEquals(RagChunkStrategyEnum.DELIMITER, RagChunkStrategyEnum.fromValue(5));
|
||||
assertThrows(IllegalArgumentException.class, () -> RagChunkStrategyEnum.fromValue(999));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,4 +104,12 @@ class SysEnumComponentStructureTests {
|
||||
|
||||
assertNotNull(initMethod);
|
||||
}
|
||||
|
||||
@Test
|
||||
void sysEnumServiceShouldExposeReplaceByCatalogAndType() throws NoSuchMethodException {
|
||||
Method replaceMethod = ISysEnumService.class.getMethod("replaceByCatalogAndType", String.class, String.class, List.class);
|
||||
|
||||
assertNotNull(replaceMethod);
|
||||
assertEquals(boolean.class, replaceMethod.getReturnType());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.bruce.common.enumconfig;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.bruce.common.domain.entity.SysEnum;
|
||||
import com.bruce.common.enums.PersistableSysEnumDefinition;
|
||||
import com.bruce.common.enums.CommonStatusEnum;
|
||||
import com.bruce.common.enums.EnableStatusEnum;
|
||||
import com.bruce.common.service.ISysEnumService;
|
||||
@@ -13,6 +14,8 @@ import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@SpringBootTest
|
||||
@EnabledIfSystemProperty(named = "runEnumInit", matches = "true")
|
||||
class SysEnumDataInitTests {
|
||||
@@ -22,49 +25,22 @@ class SysEnumDataInitTests {
|
||||
|
||||
@Test
|
||||
public void initDefaultEnums() {
|
||||
saveOrUpdate("common", "enable_status", EnableStatusEnum.DISABLED.getLabel(), EnableStatusEnum.DISABLED.getValue(), 0, "通用启用状态");
|
||||
saveOrUpdate("common", "enable_status", EnableStatusEnum.ENABLED.getLabel(), EnableStatusEnum.ENABLED.getValue(), 1, "通用启用状态");
|
||||
List<SysEnumDefinitionSyncSupport.EnumGroup> groups = List.of(
|
||||
buildGroup(EnableStatusEnum.values()),
|
||||
buildGroup(CommonStatusEnum.values()),
|
||||
buildGroup(RagParseStatusEnum.values()),
|
||||
buildGroup(RagIndexStatusEnum.values()),
|
||||
buildGroup(RagChunkStrategyEnum.values())
|
||||
);
|
||||
SysEnumDefinitionSyncSupport.validateUniqueGroupKeys(groups);
|
||||
|
||||
saveOrUpdate("common", "common_status", CommonStatusEnum.DISABLED.getLabel(), CommonStatusEnum.DISABLED.getValue(), 0, "通用状态");
|
||||
saveOrUpdate("common", "common_status", CommonStatusEnum.ENABLED.getLabel(), CommonStatusEnum.ENABLED.getValue(), 1, "通用状态");
|
||||
saveOrUpdate("common", "common_status", CommonStatusEnum.DRAFT.getLabel(), CommonStatusEnum.DRAFT.getValue(), 2, "通用状态");
|
||||
saveOrUpdate("common", "common_status", CommonStatusEnum.PROCESSING.getLabel(), CommonStatusEnum.PROCESSING.getValue(), 3, "通用状态");
|
||||
saveOrUpdate("common", "common_status", CommonStatusEnum.COMPLETED.getLabel(), CommonStatusEnum.COMPLETED.getValue(), 4, "通用状态");
|
||||
saveOrUpdate("common", "common_status", CommonStatusEnum.FAILED.getLabel(), CommonStatusEnum.FAILED.getValue(), 5, "通用状态");
|
||||
|
||||
saveOrUpdate("rag", "parse_status", RagParseStatusEnum.UPLOADED.getLabel(), RagParseStatusEnum.UPLOADED.getValue(), 1, "RAG文档解析状态");
|
||||
saveOrUpdate("rag", "parse_status", RagParseStatusEnum.PARSING.getLabel(), RagParseStatusEnum.PARSING.getValue(), 2, "RAG文档解析状态");
|
||||
saveOrUpdate("rag", "parse_status", RagParseStatusEnum.PARSED.getLabel(), RagParseStatusEnum.PARSED.getValue(), 3, "RAG文档解析状态");
|
||||
saveOrUpdate("rag", "parse_status", RagParseStatusEnum.FAILED.getLabel(), RagParseStatusEnum.FAILED.getValue(), 4, "RAG文档解析状态");
|
||||
|
||||
saveOrUpdate("rag", "index_status", RagIndexStatusEnum.PENDING.getLabel(), RagIndexStatusEnum.PENDING.getValue(), 1, "RAG文档索引状态");
|
||||
saveOrUpdate("rag", "index_status", RagIndexStatusEnum.INDEXING.getLabel(), RagIndexStatusEnum.INDEXING.getValue(), 2, "RAG文档索引状态");
|
||||
saveOrUpdate("rag", "index_status", RagIndexStatusEnum.INDEXED.getLabel(), RagIndexStatusEnum.INDEXED.getValue(), 3, "RAG文档索引状态");
|
||||
saveOrUpdate("rag", "index_status", RagIndexStatusEnum.FAILED.getLabel(), RagIndexStatusEnum.FAILED.getValue(), 4, "RAG文档索引状态");
|
||||
|
||||
saveOrUpdate("rag", "chunk_strategy", RagChunkStrategyEnum.FIXED_LENGTH.getLabel(), RagChunkStrategyEnum.FIXED_LENGTH.getValue(), 1, "RAG文档切片方式");
|
||||
saveOrUpdate("rag", "chunk_strategy", RagChunkStrategyEnum.PARAGRAPH.getLabel(), RagChunkStrategyEnum.PARAGRAPH.getValue(), 2, "RAG文档切片方式");
|
||||
saveOrUpdate("rag", "chunk_strategy", RagChunkStrategyEnum.HEADING.getLabel(), RagChunkStrategyEnum.HEADING.getValue(), 3, "RAG文档切片方式");
|
||||
saveOrUpdate("rag", "chunk_strategy", RagChunkStrategyEnum.TABLE_ROW.getLabel(), RagChunkStrategyEnum.TABLE_ROW.getValue(), 4, "RAG文档切片方式");
|
||||
saveOrUpdate("rag", "chunk_strategy", RagChunkStrategyEnum.DELIMITER.getLabel(), RagChunkStrategyEnum.DELIMITER.getValue(), 5, "RAG文档切片方式");
|
||||
saveOrUpdate("rag", "chunk_strategy", RagChunkStrategyEnum.SEMANTIC.getLabel(), RagChunkStrategyEnum.SEMANTIC.getValue(), 6, "RAG文档切片方式");
|
||||
for (SysEnumDefinitionSyncSupport.EnumGroup group : groups) {
|
||||
List<SysEnum> rows = SysEnumDefinitionSyncSupport.toEntities(group);
|
||||
sysEnumService.replaceByCatalogAndType(group.catalog(), group.type(), rows);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveOrUpdate(String catalog, String type, String name, Integer value, Integer sort, String remark) {
|
||||
SysEnum sysEnum = sysEnumService.getOne(new LambdaQueryWrapper<SysEnum>()
|
||||
.eq(SysEnum::getCatalog, catalog)
|
||||
.eq(SysEnum::getType, type)
|
||||
.eq(SysEnum::getName, name));
|
||||
if (sysEnum == null) {
|
||||
sysEnum = new SysEnum();
|
||||
}
|
||||
sysEnum.setCatalog(catalog);
|
||||
sysEnum.setType(type);
|
||||
sysEnum.setName(name);
|
||||
sysEnum.setValue(value);
|
||||
sysEnum.setStrvalue(null);
|
||||
sysEnum.setSort(sort);
|
||||
sysEnum.setRemark(remark);
|
||||
sysEnumService.saveOrUpdate(sysEnum);
|
||||
private SysEnumDefinitionSyncSupport.EnumGroup buildGroup(PersistableSysEnumDefinition[] definitions) {
|
||||
return SysEnumDefinitionSyncSupport.groupOf(List.of(definitions));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
package com.bruce.common.enumconfig;
|
||||
|
||||
import com.bruce.common.domain.entity.SysEnum;
|
||||
import com.bruce.common.enums.PersistableSysEnumDefinition;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* sys_enum 初始化测试的辅助工具。
|
||||
* <p>
|
||||
* 该类只服务于测试入口,用于把代码里的枚举定义组装成可落库的数据结构,
|
||||
* 并在真正写库前完成组级唯一性校验。
|
||||
*/
|
||||
final class SysEnumDefinitionSyncSupport {
|
||||
|
||||
private SysEnumDefinitionSyncSupport() {
|
||||
}
|
||||
|
||||
static EnumGroup groupOf(List<? extends PersistableSysEnumDefinition> definitions) {
|
||||
if (definitions == null || definitions.isEmpty()) {
|
||||
throw new IllegalArgumentException("枚举定义不能为空");
|
||||
}
|
||||
PersistableSysEnumDefinition first = definitions.getFirst();
|
||||
validateGroupMembers(first, definitions);
|
||||
validateUniqueValuesAndSorts(first, definitions);
|
||||
return new EnumGroup(first.getCatalog(), first.getType(), List.copyOf(definitions));
|
||||
}
|
||||
|
||||
static void validateUniqueGroupKeys(List<EnumGroup> groups) {
|
||||
Set<String> keys = new HashSet<>();
|
||||
for (EnumGroup group : groups) {
|
||||
String key = group.catalog() + "/" + group.type();
|
||||
if (!keys.add(key)) {
|
||||
throw new IllegalArgumentException("存在重复的枚举分组: " + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static List<SysEnum> toEntities(EnumGroup group) {
|
||||
return group.definitions().stream()
|
||||
.map(item -> {
|
||||
SysEnum sysEnum = new SysEnum();
|
||||
sysEnum.setCatalog(group.catalog());
|
||||
sysEnum.setType(group.type());
|
||||
sysEnum.setName(item.getName());
|
||||
sysEnum.setValue(item.getValue());
|
||||
sysEnum.setStrvalue(item.getStrvalue());
|
||||
sysEnum.setSort(item.getSort());
|
||||
sysEnum.setRemark(item.getRemark());
|
||||
return sysEnum;
|
||||
})
|
||||
.toList();
|
||||
}
|
||||
|
||||
private static void validateGroupMembers(
|
||||
PersistableSysEnumDefinition first,
|
||||
List<? extends PersistableSysEnumDefinition> definitions
|
||||
) {
|
||||
for (PersistableSysEnumDefinition item : definitions) {
|
||||
if (!first.getCatalog().equals(item.getCatalog()) || !first.getType().equals(item.getType())) {
|
||||
throw new IllegalArgumentException("同一枚举组中的 catalog/type 必须一致");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void validateUniqueValuesAndSorts(
|
||||
PersistableSysEnumDefinition first,
|
||||
List<? extends PersistableSysEnumDefinition> definitions
|
||||
) {
|
||||
Set<Integer> values = new HashSet<>();
|
||||
Set<Integer> sorts = new HashSet<>();
|
||||
for (PersistableSysEnumDefinition item : definitions) {
|
||||
if (!values.add(item.getValue())) {
|
||||
throw new IllegalArgumentException("枚举值重复: " + first.getCatalog() + "/" + first.getType() + "/" + item.getValue());
|
||||
}
|
||||
if (!sorts.add(item.getSort())) {
|
||||
throw new IllegalArgumentException("枚举排序重复: " + first.getCatalog() + "/" + first.getType() + "/" + item.getSort());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
record EnumGroup(
|
||||
String catalog,
|
||||
String type,
|
||||
List<? extends PersistableSysEnumDefinition> definitions
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.bruce.common.enumconfig;
|
||||
|
||||
import com.bruce.common.domain.entity.SysEnum;
|
||||
import com.bruce.common.enums.EnableStatusEnum;
|
||||
import com.bruce.common.enums.PersistableSysEnumDefinition;
|
||||
import com.bruce.rag.enums.RagChunkStrategyEnum;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
class SysEnumDefinitionSyncSupportTests {
|
||||
|
||||
@Test
|
||||
void shouldRejectDuplicateCatalogAndTypeAcrossGroups() {
|
||||
SysEnumDefinitionSyncSupport.EnumGroup left = SysEnumDefinitionSyncSupport.groupOf(
|
||||
List.of(EnableStatusEnum.DISABLED, EnableStatusEnum.ENABLED)
|
||||
);
|
||||
SysEnumDefinitionSyncSupport.EnumGroup right = SysEnumDefinitionSyncSupport.groupOf(
|
||||
List.of(
|
||||
new FakeEnumDefinition("common", "enable_status", "草稿", 2, 2, "通用启用状态")
|
||||
)
|
||||
);
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> SysEnumDefinitionSyncSupport.validateUniqueGroupKeys(List.of(left, right))
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldBuildSysEnumRowsFromDefinitions() {
|
||||
SysEnumDefinitionSyncSupport.EnumGroup group = SysEnumDefinitionSyncSupport.groupOf(
|
||||
List.of(RagChunkStrategyEnum.FIXED_LENGTH, RagChunkStrategyEnum.DELIMITER)
|
||||
);
|
||||
|
||||
List<SysEnum> rows = SysEnumDefinitionSyncSupport.toEntities(group);
|
||||
|
||||
assertEquals(2, rows.size());
|
||||
assertEquals("rag", rows.get(0).getCatalog());
|
||||
assertEquals("chunk_strategy", rows.get(0).getType());
|
||||
assertEquals("固定长度切片", rows.get(0).getName());
|
||||
assertEquals(1, rows.get(0).getValue());
|
||||
assertEquals(5, rows.get(1).getValue());
|
||||
}
|
||||
|
||||
private record FakeEnumDefinition(
|
||||
String catalog,
|
||||
String type,
|
||||
String name,
|
||||
Integer value,
|
||||
Integer sort,
|
||||
String remark
|
||||
) implements PersistableSysEnumDefinition {
|
||||
|
||||
@Override
|
||||
public String getCatalog() {
|
||||
return catalog;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStrvalue() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getSort() {
|
||||
return sort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import com.bruce.common.service.ISysAttachmentService;
|
||||
import com.bruce.rag.dto.request.RagDocumentParseRequest;
|
||||
import com.bruce.rag.dto.response.RagDocumentParseResponse;
|
||||
import com.bruce.rag.entity.RagDocument;
|
||||
import com.bruce.rag.enums.RagChunkStrategyEnum;
|
||||
import com.bruce.rag.enums.RagParseStatusEnum;
|
||||
import com.bruce.rag.service.IRagDocumentService;
|
||||
import com.bruce.rag.service.impl.RagDocumentParseServiceImpl;
|
||||
@@ -26,6 +27,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -121,7 +123,7 @@ class RagDocumentParseServiceImplTests {
|
||||
);
|
||||
RagDocumentParseRequest request = new RagDocumentParseRequest();
|
||||
request.setDocumentIds(List.of(1002L));
|
||||
request.setChunkStrategy("DELIMITER");
|
||||
request.setChunkStrategy(RagChunkStrategyEnum.DELIMITER.getValue());
|
||||
request.setDelimiter("。");
|
||||
|
||||
when(ragDocumentService.getById(1002L)).thenReturn(document);
|
||||
@@ -135,6 +137,23 @@ class RagDocumentParseServiceImplTests {
|
||||
assertEquals(RagParseStatusEnum.PARSED.name(), responses.getFirst().getParseStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
void parseShouldRejectUnknownChunkStrategyValue() {
|
||||
AttachmentProperties attachmentProperties = new AttachmentProperties();
|
||||
attachmentProperties.setBasePath(tempDir.toString());
|
||||
RagDocumentParseServiceImpl service = new RagDocumentParseServiceImpl(
|
||||
ragDocumentService,
|
||||
sysAttachmentService,
|
||||
attachmentProperties,
|
||||
new DocumentParserFactory(List.of(new FixedDocumentParser("batch profiles")))
|
||||
);
|
||||
RagDocumentParseRequest request = new RagDocumentParseRequest();
|
||||
request.setDocumentIds(List.of(1002L));
|
||||
request.setChunkStrategy(999);
|
||||
|
||||
assertThrows(IllegalArgumentException.class, () -> service.parse(request));
|
||||
}
|
||||
|
||||
private static class FixedDocumentParser implements DocumentParser {
|
||||
|
||||
private final String text;
|
||||
|
||||
Reference in New Issue
Block a user