refactor(modules): 拆分多模块工程并收口common基础模块

This commit is contained in:
2026-06-01 03:26:18 +08:00
parent 6fe1209801
commit 07ad8bb36b
231 changed files with 1690 additions and 172 deletions

85
common-agent-boot/pom.xml Normal file
View File

@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.bruce</groupId>
<artifactId>common-agent-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>common-agent-boot</artifactId>
<name>common-agent-boot</name>
<dependencies>
<dependency>
<groupId>com.bruce</groupId>
<artifactId>common-agent-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.bruce</groupId>
<artifactId>common-agent-rag</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.bruce</groupId>
<artifactId>common-agent-modelprovider</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.bruce</groupId>
<artifactId>common-agent-agent</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.bruce</groupId>
<artifactId>common-agent-workflow</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.bruce</groupId>
<artifactId>common-agent-mcp</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.bruce</groupId>
<artifactId>common-agent-skill</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.bruce</groupId>
<artifactId>common-agent-observability</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,26 @@
package com.bruce;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
@MapperScan(basePackages = {
"com.bruce.common.mapper",
"com.bruce.rag.mapper",
"com.bruce.modelprovider.mapper",
"com.bruce.agent.mapper",
"com.bruce.workflow.mapper",
"com.bruce.mcp.mapper",
"com.bruce.skill.mapper",
"com.bruce.observability.mapper"
})
public class CommonAgentApplication {
public static void main(String[] args) {
SpringApplication.run(CommonAgentApplication.class, args);
}
}

View File

@@ -0,0 +1,5 @@
# AI 独立配置文件(建议仅本地/环境覆盖使用,不提交真实密钥)
# 格式ai.secret-refs[<secret_ref>]=<api_key>
ai.secret-refs[SILICONFLOW_API_KEY]=your-key

View File

@@ -0,0 +1,15 @@
spring:
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://110.42.106.130:5431/common_agent?currentSchema=common_agent
username: common_agent
password: common_agent
mybatis-plus:
mapper-locations: classpath*:/mapper/**/*.xml
configuration:
map-underscore-to-camel-case: true
common:
attachment:
base-path: /data/common-agent/attachments

View File

@@ -0,0 +1,17 @@
# Copy this file to application-dev.yaml, application-test.yaml, or application-prod.yaml,
# then change spring.profiles.active in application.yaml to select the environment.
spring:
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://<host>:<port>/<database>
username: <username>
password: <password>
mybatis-plus:
mapper-locations: classpath*:/mapper/**/*.xml
configuration:
map-underscore-to-camel-case: true
common:
attachment:
base-path: /data/common-agent/attachments

View File

@@ -0,0 +1,13 @@
spring:
application:
name: common_agent
profiles:
active: dev
common:
attachment:
base-path: data/attachments
logging:
pattern:
console: "%d{yyyy-MM-dd HH:mm:ss.SSS, GMT+8} %5p ${PID:- } --- [%15.15t] %-40.40logger{39} : %m%n%wEx"

View File

@@ -0,0 +1,13 @@
package com.bruce;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class CommonAgentApplicationTests {
@Test
void contextLoads() {
}
}

View File

@@ -0,0 +1,37 @@
package com.bruce.integration.config;
import com.bruce.common.config.EntityAuditMetaObjectHandler;
import com.bruce.rag.entity.RagStore;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class EntityAuditMetaObjectHandlerTests {
@Test
void insertFillShouldPopulateAuditFields() {
EntityAuditMetaObjectHandler handler = new EntityAuditMetaObjectHandler();
RagStore entity = new RagStore();
handler.insertFill(SystemMetaObject.forObject(entity));
assertNotNull(entity.getCreateTime());
assertNotNull(entity.getUpdateTime());
assertEquals("system", entity.getCreateBy());
assertEquals("system", entity.getUpdateBy());
}
@Test
void updateFillShouldRefreshUpdateAuditFields() {
EntityAuditMetaObjectHandler handler = new EntityAuditMetaObjectHandler();
RagStore entity = new RagStore();
entity.setUpdateBy("oldUser");
handler.updateFill(SystemMetaObject.forObject(entity));
assertNotNull(entity.getUpdateTime());
assertEquals("system", entity.getUpdateBy());
}
}

View File

@@ -0,0 +1,120 @@
package com.bruce.integration.enumconfig;
import com.bruce.common.enums.CommonStatusEnum;
import com.bruce.common.enums.EnableStatusEnum;
import com.bruce.common.enums.PersistableSysEnumDefinition;
import com.bruce.modelprovider.enums.ModelCallStatusEnum;
import com.bruce.modelprovider.enums.ModelHealthStatusEnum;
import com.bruce.modelprovider.enums.ModelProtocolTypeEnum;
import com.bruce.modelprovider.enums.ModelProviderTypeEnum;
import com.bruce.modelprovider.enums.ModelRouteStrategyEnum;
import com.bruce.modelprovider.enums.ModelTaskTypeEnum;
import com.bruce.modelprovider.enums.ModelTypeEnum;
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 {
@Test
/**
* 方法 enumValuesShouldBeStable用于执行业务逻辑处理。
*/
void enumValuesShouldBeStable() {
assertEquals(1, EnableStatusEnum.ENABLED.getValue());
assertEquals(0, EnableStatusEnum.DISABLED.getValue());
assertEquals(0, CommonStatusEnum.DISABLED.getValue());
assertEquals(1, CommonStatusEnum.ENABLED.getValue());
assertEquals(2, CommonStatusEnum.DRAFT.getValue());
assertEquals(3, CommonStatusEnum.PROCESSING.getValue());
assertEquals(4, CommonStatusEnum.COMPLETED.getValue());
assertEquals(5, CommonStatusEnum.FAILED.getValue());
assertEquals(1, RagParseStatusEnum.UPLOADED.getValue());
assertEquals(4, RagParseStatusEnum.FAILED.getValue());
assertEquals(1, RagIndexStatusEnum.PENDING.getValue());
assertEquals(3, RagIndexStatusEnum.INDEXED.getValue());
assertEquals(1, RagChunkStrategyEnum.FIXED_LENGTH.getValue());
assertEquals(5, RagChunkStrategyEnum.DELIMITER.getValue());
assertEquals(6, RagChunkStrategyEnum.SEMANTIC.getValue());
assertEquals(1, ModelProviderTypeEnum.OLLAMA.getValue());
assertEquals(1, ModelProtocolTypeEnum.OPENAI_COMPATIBLE.getValue());
assertEquals(2, ModelTypeEnum.EMBEDDING.getValue());
assertEquals(1, ModelTaskTypeEnum.RAG_EMBEDDING.getValue());
assertEquals(4, ModelRouteStrategyEnum.MANUAL.getValue());
assertEquals(1, ModelCallStatusEnum.SUCCESS.getValue());
assertEquals(2, ModelHealthStatusEnum.HEALTHY.getValue());
}
@Test
/**
* 方法 enumNamesShouldBeStable用于执行业务逻辑处理。
*/
void enumNamesShouldBeStable() {
assertEquals("启用", EnableStatusEnum.ENABLED.getLabel());
assertEquals("禁用", EnableStatusEnum.DISABLED.getLabel());
assertEquals("草稿", CommonStatusEnum.DRAFT.getLabel());
assertEquals("处理中", CommonStatusEnum.PROCESSING.getLabel());
assertEquals("已完成", CommonStatusEnum.COMPLETED.getLabel());
assertEquals("失败", CommonStatusEnum.FAILED.getLabel());
assertEquals("已上传", RagParseStatusEnum.UPLOADED.getLabel());
assertEquals("解析失败", RagParseStatusEnum.FAILED.getLabel());
assertEquals("待索引", RagIndexStatusEnum.PENDING.getLabel());
assertEquals("已索引", RagIndexStatusEnum.INDEXED.getLabel());
assertEquals("固定长度切片", RagChunkStrategyEnum.FIXED_LENGTH.getLabel());
assertEquals("按分隔符切片", RagChunkStrategyEnum.DELIMITER.getLabel());
assertEquals("语义切片", RagChunkStrategyEnum.SEMANTIC.getLabel());
assertEquals("Ollama", ModelProviderTypeEnum.OLLAMA.getLabel());
assertEquals("OpenAI兼容协议", ModelProtocolTypeEnum.OPENAI_COMPATIBLE.getLabel());
assertEquals("向量模型", ModelTypeEnum.EMBEDDING.getLabel());
assertEquals("RAG文档向量化", ModelTaskTypeEnum.RAG_EMBEDDING.getLabel());
assertEquals("手工指定", ModelRouteStrategyEnum.MANUAL.getLabel());
assertEquals("成功", ModelCallStatusEnum.SUCCESS.getLabel());
assertEquals("健康", ModelHealthStatusEnum.HEALTHY.getLabel());
}
@Test
/**
* 方法 enumsShouldExposeStableSysEnumMetadata用于执行业务逻辑处理。
*/
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());
PersistableSysEnumDefinition providerType = ModelProviderTypeEnum.OLLAMA;
assertEquals("model_provider", providerType.getCatalog());
assertEquals("provider_type", providerType.getType());
assertEquals("Ollama", providerType.getName());
}
@Test
/**
* 方法 ragChunkStrategyShouldResolveByIntegerValue用于执行业务逻辑处理。
*/
void ragChunkStrategyShouldResolveByIntegerValue() {
assertEquals(RagChunkStrategyEnum.FIXED_LENGTH, RagChunkStrategyEnum.fromValue(1));
assertEquals(RagChunkStrategyEnum.DELIMITER, RagChunkStrategyEnum.fromValue(5));
assertThrows(IllegalArgumentException.class, () -> RagChunkStrategyEnum.fromValue(999));
}
}

View File

@@ -0,0 +1,68 @@
package com.bruce.integration.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.modelprovider.enums.ModelCallStatusEnum;
import com.bruce.modelprovider.enums.ModelHealthStatusEnum;
import com.bruce.modelprovider.enums.ModelProtocolTypeEnum;
import com.bruce.modelprovider.enums.ModelProviderTypeEnum;
import com.bruce.modelprovider.enums.ModelRouteStrategyEnum;
import com.bruce.modelprovider.enums.ModelTaskTypeEnum;
import com.bruce.modelprovider.enums.ModelTypeEnum;
import com.bruce.common.service.ISysEnumService;
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 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 {
@Autowired
private ISysEnumService sysEnumService;
@Test
/**
* 方法 initDefaultEnums用于执行业务逻辑处理。
*/
public void initDefaultEnums() {
List<SysEnumDefinitionSyncSupport.EnumGroup> groups = List.of(
buildGroup(EnableStatusEnum.values()),
buildGroup(CommonStatusEnum.values()),
buildGroup(RagParseStatusEnum.values()),
buildGroup(RagIndexStatusEnum.values()),
buildGroup(RagChunkStrategyEnum.values()),
buildGroup(ModelProviderTypeEnum.values()),
buildGroup(ModelProtocolTypeEnum.values()),
buildGroup(ModelTypeEnum.values()),
buildGroup(ModelTaskTypeEnum.values()),
buildGroup(ModelRouteStrategyEnum.values()),
buildGroup(ModelCallStatusEnum.values()),
buildGroup(ModelHealthStatusEnum.values())
);
SysEnumDefinitionSyncSupport.validateUniqueGroupKeys(groups);
for (SysEnumDefinitionSyncSupport.EnumGroup group : groups) {
List<SysEnum> rows = SysEnumDefinitionSyncSupport.toEntities(group);
sysEnumService.replaceByCatalogAndType(group.catalog(), group.type(), rows);
}
}
/**
* 方法 buildGroup用于执行业务逻辑处理。
*/
private SysEnumDefinitionSyncSupport.EnumGroup buildGroup(PersistableSysEnumDefinition[] definitions) {
return SysEnumDefinitionSyncSupport.groupOf(List.of(definitions));
}
}

View File

@@ -0,0 +1,90 @@
package com.bruce.integration.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
) {
}
}

View File

@@ -0,0 +1,98 @@
package com.bruce.integration.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;
}
}
}