本文引用的文件
本文件聚焦 ai-server 的 Spring Boot 后端服务架构,目标是帮助开发者理解:
com.zsjz.ai.common.model.<module>.entity 下。ai-server 是一个 Spring Boot 应用,采用“公共基础设施 + 业务模块”的组织方式:
common:跨模块配置、上下文、缓存、异常、统一响应、数据源路由、安全拦截等横切能力。module:按领域划分的业务模块,每个模块内部再分 controller、service、mapper、entity、vo、dto、config 等包。resources:Spring Boot 配置文件,包含通用配置、开发环境与生产环境 profile。scripts:部署脚本;sql:数据库初始化与增量脚本。
graph TB
Client["前端或调用方"] --> Web["Spring MVC<br/>Controller 层"]
Web --> Common["横切能力<br/>Sa-Token / CORS / 全局异常"]
Web --> Modules["业务模块<br/>plat / agent / aiclean / knowledge / graph / person / call / trans / track / govern / search / dm / otg / external"]
Modules --> Services["Service 业务逻辑"]
Services --> Mappers["Mapper<br/>MyBatis-Plus"]
Mappers --> DS["数据源路由<br/>CaseRoutingDataSource"]
DS --> PG["PostgreSQL / pgvector"]
DS --> DuckDB["DuckDB 案件库"]
Services --> Redis["Redis"]
Services --> ES["Elasticsearch"]
图示来源
章节来源
这些依赖集中在 ai-server 的 Maven 工程中,并配合 application.yaml 中的环境变量占位符实现可部署化。
章节来源
ai-server 的请求路径如下:
长连接场景使用 SSE 推送清洗、治理、研判进度;定时任务负责资源回收与看门狗。
sequenceDiagram
participant Browser as "浏览器"
participant Cors as "CORS Filter"
participant Sa as "Sa-Token Filter"
participant Auth as "SaInterceptor"
participant Ctrl as "Controller"
participant Svc as "Service"
participant Map as "Mapper"
participant DS as "CaseRoutingDataSource"
participant PG as "PostgreSQL/pgvector"
participant Duck as "DuckDB"
Browser->>Cors: "HTTP 请求"
Cors-->>Browser: "跨域响应头"
Browser->>Sa: "带 x-token 的请求"
Sa-->>Auth: "携带会话上下文"
Auth->>Ctrl: "校验通过后转发"
Ctrl->>Svc: "调用业务方法"
Svc->>Map: "执行 SQL"
Map->>DS: "按 @DS 或 slave key 获取数据源"
alt "主库或普通业务"
DS->>PG: "PostgreSQL"
else "案件数据"
DS->>Duck: "DuckDB 案件库"
end
Duck-->>Map: "结果集"
PG-->>Map: "结果集"
Map-->>Svc: "实体或 VO"
Svc-->>Ctrl: "业务结果"
Ctrl-->>Browser: "Result 包装响应"
图示来源
业务模块按领域划分,典型结构为:
module/<domain>/controller:对外 REST 接口。module/<domain>/service:业务编排与事务边界。module/<domain>/mapper:MyBatis-Plus Mapper 接口。common/model/<domain>/entity:持久化实体。module/<domain>/vo / dto:视图对象与入参对象。各模块主要职责如下:
这种组织方式使每个模块只关注自身领域对象,并通过 Service 暴露业务能力;Controller 保持薄,只做参数校验与 Result 包装。
章节来源
ai-server 使用 MyBatis-Plus 与 Dynamic Datasource 组合,实现主库 PostgreSQL/pgvector 与案件级 DuckDB 的动态切换。
关键设计点:
spring.datasource.dynamic.primary 默认指向 master,即 PostgreSQL。spring.datasource.dynamic.strict=true 保证写错数据源 key 时直接报错,而不是静默回退到主库。@DS(StrConsts.DS_KEY_SLAVE),表示走“从库分支”。CaseRoutingDataSource 替换 Dynamic Datasource 自动装配的 DynamicRoutingDataSource,把逻辑 key slave 映射到物理 key case{caseId}。DuckDB 作为案件库,由 CaseDataSourceRegistry 管理生命周期,并由 CaseDataSourceReaper 兜底回收。
flowchart TD
Start["业务调用 Mapper"] --> CheckDS{"是否标注 slave?"}
CheckDS -->|否| UseMaster["使用 master:PostgreSQL/pgvector"]
CheckDS -->|是| GetCase["从 CaseContextHolder 取 caseId"]
GetCase --> HasCase{"caseId 是否存在?"}
HasCase -->|否| Error400["返回 400:请先打开案件"]
HasCase -->|是| PhysicalKey["生成物理 key:case{caseId}"]
PhysicalKey --> Exists{"DuckDB 数据源已注册?"}
Exists -->|否| Error400b["返回 400:数据源未打开或已释放"]
Exists -->|是| ReturnDuck["返回 DuckDB 连接"]
图示来源
章节来源
Sa-Token 在项目中承担“登录态校验”职责,不负责复杂角色权限:
StpUtil.checkLogin()。/auth/login、/sys/health、/sys/checkAuth、/sys/info、/sys/code、/sys/noNet、/sys/map/**、/error。前端通过 x-token 请求头传递令牌,需与后端 Sa-Token 配置一致。
sequenceDiagram
participant Client as "前端"
participant Sys as "SystemController"
participant Auth as "AuthController"
participant Sa as "Sa-Token"
participant Redis as "Redis"
Client->>Sys: "GET /sys/health"
Sys-->>Client: "健康检查成功"
Client->>Auth: "POST /auth/login"
Auth->>Sa: "创建会话"
Sa->>Redis: "写入 token 与登录映射"
Auth-->>Client: "返回 x-token"
Client->>Client: "后续请求携带 x-token"
Client->>Sys: "业务请求"
Sa->>Redis: "校验 token"
Sys-->>Client: "返回业务数据"
图示来源
章节来源
SSE 用于向前端推送清洗、治理、研判等任务的实时进度:
SseEmitter,避免多人使用时互相覆盖。推送内容会补齐当前 caseId,让前端能按案件丢弃上一个任务的残留进度。
sequenceDiagram
participant Frontend as "前端"
participant Sse as "SseService"
participant Scheduler as "调度器"
Frontend->>Sse: "建立 SSE 连接"
Sse-->>Frontend: "返回 SseEmitter"
Sse->>Frontend: "推送连接成功消息"
loop "每 5 秒"
Scheduler->>Sse: "触发 fixedSee()"
Sse->>Frontend: "推送心跳消息"
end
Frontend->>Sse: "断开连接"
Sse-->>Frontend: "移除连接引用"
图示来源
章节来源
定时任务运行在 Spring 调度线程池中,默认大小为 4,避免单个批量任务拖住 SSE 心跳:
CaseDataSourceReaper 的核心策略是“保守回收”:
查询会话状态失败时,宁可多占内存也不误关正在使用的案件数据源。
flowchart TD
Start["定时触发回收"] --> Empty{"是否有已打开案件?"}
Empty -->|否| End["结束本轮"]
Empty -->|是| Iterate["遍历每个 caseId"]
Iterate --> Owner["获取归属 userId 与 token"]
Owner --> Alive{"归属会话仍有效?"}
Alive -->|是| Next["下一个案件"]
Alive -->|否| Busy{"是否有在途任务?"}
Busy -->|是| Skip["跳过回收"]
Busy -->|否| Close["关闭 DuckDB 数据源并清理缓存"]
Close --> Next
Next --> Iterate
图示来源
章节来源
ai-server 的依赖可分为四层:
外部依赖:PostgreSQL、pgvector、DuckDB、Redis、Elasticsearch、Neo4j、外部大模型与推理引擎。
graph LR
Controller["Controller"] --> Service["Service"]
Service --> Mapper["Mapper"]
Mapper --> DataSource["CaseRoutingDataSource"]
DataSource --> Postgres["PostgreSQL / pgvector"]
DataSource --> Duck["DuckDB"]
Service --> Cache["Redis"]
Service --> Search["Elasticsearch"]
Service --> Graph["Neo4j"]
图示来源
章节来源
新增业务模块应遵循以下流程:
新建 module 包
com.zsjz.ai.module.<new-domain> 下创建模块根包。controller、service、mapper、vo、dto。com.zsjz.ai.common.model.<new-domain>.entity。定义实体与 Mapper
@DS;如果读案件数据,加 @DS(StrConsts.DS_KEY_SLAVE)。实现 Service
注册 Controller 路由
@RestController 与 @RequestMapping。Result 包装。维护数据库脚本
sql 目录下新增或更新建表、索引、初始数据脚本。验证多数据源行为
@DS 注解正确。测试 token 过期后数据源被回收。
flowchart TD
NewModule["新建模块包"] --> Entity["定义 entity"]
Entity --> MapperDef["定义 mapper"]
MapperDef --> ServiceImpl["实现 service"]
ServiceImpl --> ControllerDef["实现 controller"]
ControllerDef --> Route["注册路由"]
Route --> Sql["维护数据库脚本"]
Sql --> Test["验证主库与案件库"]
Test --> Done["完成"]
[此图为概念流程,不对应具体源码文件]
zsjz.duckdb.max-open-cases 限制同时打开的案件数,避免内存耗尽。memory-ratio 与 threads-per-instance 控制每个案件实例的资源占用。zsjz.file-ai.max-concurrent 与 max-pending 防止一次性上传打爆队列。eager-load=false 懒加载,并结合 intra-op-threads、opt-level 调整。allowed-origin-patterns,减少预检请求。/sys/health 外,可增加 /sys/detailedHealth,暴露各依赖的健康状态。章节来源
file:// 必须允许跨域。x-token。@DS(slave)。strict=true 是否生效。max-open-cases 是否已满。CaseDataSourceReaper 是否正常回收。/sys/health 与 /sys/serviceHealth,确认后端与依赖可用性。slave 是否路由到 case{caseId}。章节来源
ai-server 的后端架构以 Spring Boot 为基础,通过模块化拆分业务领域,通过 MyBatis-Plus 与 Dynamic Datasource 实现 PostgreSQL/pgvector 与 DuckDB 的多数据源切换,通过 Sa-Token 实现轻量认证,通过 SSE 与定时任务实现实时推送与资源回收。新增模块时应严格遵循 controller-service-mapper 分层,并在数据库脚本中维护表结构。未来可继续补充指标采集、链路追踪与健康检查增强,以提升系统的可观测性与稳定性。