面向新加入的开发者:从零把后端
ai-server和前端ai-frontend跑起来。 编码约定、分层落位、Agent 子系统等改代码前必读的内容见 AI_AGENT.md,本文只讲「怎么跑起来」。
zsjz-ai/
├─ pom.xml # 父 POM(packaging=pom,Spring Boot 3.5.16),只聚合 ai-server
├─ ai-server/ # 后端:Spring Boot 3 + MyBatis-Plus + AgentScope
├─ ai-frontend/ # 前端:Vue3 + Vite7 + TS + Ant Design Vue(JeeSite 血统)
├─ sql/ # 建表 / 初始化脚本(手工执行,项目无 Flyway / Liquibase)
├─ QingJian/ # 运行期工作空间资产(conf / db / license / rocksdb / 基站库)
└─ data/files/ # 本地文件存储根目录(storage.local.root-path)
业务闭环:多源数据导入 → 清洗治理(dm)→ 建模分析(话单 / 资金 / 轨迹 / 画像 / 图谱)→ 可视化 → AI 对话式研判。
| 项 | 版本 | 校验命令 |
|---|---|---|
| JDK | 25(ai-server/pom.xml 的 java.version) |
java -version |
| Maven | 3.9.x+ | mvn -v |
| Node | >=20.19 \|\| >=22.12(package.json engines) |
node -v |
| pnpm | 10.x(只能用 pnpm,preinstall: only-allow pnpm) |
pnpm -v |
| PostgreSQL | 14+,必须装 pgvector 扩展 | — |
| Redis | 任意近期版本 | — |
| Elasticsearch | 8.18.x(须与 Spring Boot 3.5 托管的客户端版本匹配) | — |
| DuckDB | 无需安装,JDBC 驱动内嵌(案件分析库) | — |
| Neo4j | 可选,仅在配置图谱能力时需要 | — |
依赖服务的硬/软关系:
- Redis 是登录的硬依赖 ——
sa-token-redis-jackson已装配,Redis 不可用则所有StpUtil.*抛异常,无降级实现。- PostgreSQL(master) 存平台元数据 / Agent 会话 / 模型配置 / 向量,不可用则起不来。
- Elasticsearch 供
module/search全文检索(索引zsjz_search)。- DuckDB(slave) 是按案件/工作空间切换的文件库,不是常驻服务。
CREATE DATABASE "zsjz-ai";
\c zsjz-ai
CREATE EXTENSION IF NOT EXISTS vector; -- pgvector,RAG 向量检索必需
连接信息写在 ai-server/src/main/resources/application-dev.yaml:
spring.datasource.dynamic.datasource.master:
url: jdbc:postgresql://192.168.0.109:5432/zsjz-ai
username: postgres
password: postgres
spring.data.redis: host 192.168.0.109 / port 6379
spring.elasticsearch: uris http://192.168.0.109:9200
换成自己的本机地址即可(改这一个文件,application.yaml 是公共配置,不要动它的 context-path)。
⚠️ 该文件里存的是内网 IP + 明文口令,只为开发便利。不要把真实凭证提交到公开仓库,也不要在生产 profile 复用 dev 配置。
单机开发用默认 9200 即可。索引 settings 见 ai-server/src/main/resources/es/search-settings.json(单分片 0 副本 + \x1F 分隔符自定义分析器),文档映射见 module/search/domain/SearchDoc.java(索引名 zsjz_search)。
com.zsjz.ai.common.constants.PathConst 以 JVM 工作目录(user.dir)为根,拼出 <user.dir>/QingJian/...,用到:
| 路径 | 用途 |
|---|---|
QingJian/conf/*.json |
系统配置 / AI 配置(SystemConfManager 读写) |
QingJian/d5e03ad1/ |
内置银行卡 BIN、手机号运营商 RocksDB |
QingJian/uy76tkp8/data.db |
RogueMap 数据 |
QingJian/db/5w8mf4kf |
基础案件库模板;db/temp 临时案件库 |
QingJian/w6df89sk/ |
基站库 cellinfo.bin + libCell.dll/.so(JNI) |
QingJian/license/ |
授权文件 license.xlts 与 regInfo 工具 |
QingJian/agent/skills/ |
Agent 技能文件 |
.gitignore 里 /QingJian/ 被忽略,这些资产不在版本库中,新环境需找同事单独拷贝;缺了它们启动后置任务(AppLoadEndEventListener 初始化 RocksDB / 基站库)会失败。
项目没有自动建表机制,sql/ 下只有部分脚本,需手工执行:
| 脚本 | 目标库 | 内容 |
|---|---|---|
sql/user.sql |
PostgreSQL(master) | sys_user + 案件归属迁移,幂等可重复执行 |
sql/insight_agent_tables.sql |
PostgreSQL(master) | agent_insight_session / agent_insight_message |
sql/insight_agent_tables_align.sql |
PostgreSQL(master) | 上表的约束/索引补丁 |
sql/plat.sql |
平台库 | 平台元数据表(table_info / table_field / case_info / bank_card_issuer_info …),含 Solon 时代的 chat_* 旧表,与当前 agent_* 实体不完全对应 |
sql/external_tables.sql / external_table_field.sql |
案件库(DuckDB) + 元数据 | 13 张外部数据表建表 + table_info/table_field 登记 |
sql/case_table*.sql、create_table_2.sql、table_data_1.sql |
案件库(DuckDB) | 案件业务表与初始配置 |
# 平台库(PostgreSQL)
psql -h <host> -U postgres -d zsjz-ai -f sql/user.sql
psql -h <host> -U postgres -d zsjz-ai -f sql/insight_agent_tables.sql
psql -h <host> -U postgres -d zsjz-ai -f sql/insight_agent_tables_align.sql
初始账号(sql/user.sql 内置,BCrypt 哈希):admin / admin123,首次登录后立刻改密。
⚠️ 已知缺口(搭建新环境前必读):
agent_chat_session、agent_message、agent_model、agent_model_provider、rag_index_status等当前 Agent 实体对应的平台表,在sql/下没有完整建表脚本。AI_AGENT.md§11.8 记录:开发库zsjz-ai缺平台库结构(平台表在另一套etl-2库),裸跑会在启动后置任务阶段报PSQLException: 关系 "table_info" 不存在。 新环境请从已有可用环境导出平台库结构(pg_dump --schema-only)后再启动,别指望sql/目录自带全套。 改表结构时务必补sql/脚本,并同步table_info/table_field元数据三表链路。
cd E:/workspace/zsjz-ai # ★ 工作目录必须是仓库根,PathConst 才命中 QingJian/
mvn -pl ai-server -am clean compile # 编译
mvn -pl ai-server spring-boot:run # 运行
mvn -pl ai-server test # 跑测试
mvn -pl ai-server -am clean package -DskipTests # 打包
IDEA 里直接跑 com.zsjz.ai.App,把 Working directory 设为仓库根目录(默认是模块目录,会导致 QingJian 资产与 ./data/files 落点不对)。
上面四条命令都在仓库根执行;
cd ai-server后单模块构建也能解析到父 POM,但打包/运行时的相对路径(QingJian/、./data/files)会跟着工作目录走,所以统一在根目录执行更省心。
启动后:
curl http://localhost:8980/js/a/sys/health # 免登录健康检查
关键地址(server.port=8980,server.servlet.context-path=/js/a):
| 端点 | 说明 |
|---|---|
POST /js/a/auth/login |
登录,返回 token;body: {username, password} 明文 JSON |
GET /js/a/sys/health |
就绪探测(白名单,免登录) |
GET /js/a/mcpsse |
MCP SSE 端点(白名单、无鉴权,见 §7) |
POST /js/a/chat/stream |
Agent 对话 SSE |
生效配置只有
application.yaml+application-dev.yaml(resources下没有别的 yaml)。context-path必须写成/js/a(以/开头、不能以/结尾),原 Solon 写法!/js/a/会让 Tomcat 直接抛IllegalArgumentException起不来。
Agent 用的模型与厂商信息存平台库 agent_model / agent_model_provider,通过前端「模型管理」界面(后端 module/agent 的 /models/**)维护,不是写在 yaml 里。想跑通 AI 研判,先在这个界面上配好一个可用的 Provider(DashScope / OpenAI 兼容 / Ollama,三套 AgentScope 扩展都已在 pom 引入)。
cd ai-frontend
pnpm install # = pnpm bootstrap
pnpm dev # http://localhost:3100
.env 与 .env.development 关键项(默认值已可用):
VITE_PORT = 3100 # dev server 端口
VITE_PROXY = [["/js","http://127.0.0.1:8980/js",false]] # 代理到本地后端
VITE_GLOB_API_URL_PREFIX = /js
VITE_GLOB_ADMIN_PATH = /a
后端换地址时只改 VITE_PROXY 一行(不能换行)。浏览器打开 http://localhost:3100,用 admin / admin123 登录。
日常命令:
pnpm type:check # vue-tsc --noEmit
pnpm lint:eslint # 自动修复
pnpm lint:all # 全量(含 install)
pnpm build # 生产构建(--mode production)
pnpm build:tomcat # tomcat 模式,输出到 .env.tomcat 的 VITE_OUTPUT_DIR
pnpm preview:dist # 预览构建产物 :3100
.npmrc里配了@jeesite私有源;装不动私有包时先确认网络/镜像可达。
/js/a/... = urlPrefix(/js) + adminPath(/a) + 后端 context-path。前端 defHttp 自动补 /js,代码里写 url: adminPath + '/xxx';不走 defHttp(fetch / <img src>)时手动拼 glob.apiUrl + glob.urlPrefix + glob.adminPath + path。这三处必须同步改。token-name: x-token、is-read-cookie: false(仅 Header)。defHttp 自动注入;fetch 场景要手动带 'x-token': getToken()。原生 EventSource 无法自定义请求头,SSE 一律用 fetch + body.getReader() 读增量。body.code = 401(不是 HTTP 401),前端据此跳登录页。module/agent 的 /chat/**(含 /chat/results/**)是裸返回,调它们必须传 { isTransformResponse: false };/models/** 是 Result<T> 包装,用默认 transform。is-concurrent: false),后登录顶掉前登录 —— 两个浏览器窗口同时登录,前一个会 401,这是设计行为不是 bug。/mcpsse、/mcp/message、/mcpstreamable 在 sa-token 白名单里(外部 MCP 客户端不带 x-token)。代价是能访问 8980 端口的人即可读取全部案件数据,非可信网络部署必须在外层(反代/防火墙)限来源。LicenseFilter 当前是透传实现,授权校验下沉在 SystemService 的 /sys/info、/sys/checkAuth。它被 Spring 自动注册为 /* 全局过滤器,doFilter 里的 chain.doFilter 绝不能删 —— 删了表现为所有接口返回 200 + Content-Length: 0 空白,连 404 都没有,日志无异常。uno.config.ts 的 content.pipeline.include 只扫 **/*.{vue,tsx,ts},menu.json 不在扫描范围,而 Icon 组件是运行时拼 i-<collection>:<name> 类名的 → 新图标要把完整 i- 类名加进 uno.config.ts 的 safelist。src/core/router/helper/routeHelper.ts 的 explicitDynamicViewMap 加显式映射,默认的 import.meta.glob('**/views/**') 会命中 node_modules 里的同名 views。com.zsjz.ai.*。历史上包名迁移漏改 XML(com.qingjian.*)会让 MyBatis 启动即炸。src/<domain>/api/types/{dto,query}.ts。@Tool(description=...) / @ToolParam,并同步更新 AgentToolRegistryTest#TOTAL_BUSINESS_TOOLS 的数量断言。# 后端
mvn -pl ai-server -am clean compile
# 前端
cd ai-frontend && pnpm type:check && pnpm lint:eslint
sql/ 补脚本 + table_info / table_field 元数据同步StpUtil.getLoginIdAsLong() 归属校验System.out.println 残留| 现象 | 原因 / 处置 |
|---|---|
启动即 ContextPath must start with '/'... |
application.yaml 的 context-path 被改成了 !/js/a/ 或带尾斜杠 → 改回 /js/a |
Failed to parse mapping resource: mappers/... |
XML 的 namespace/resultType 仍是 com.qingjian.* → 改 com.zsjz.ai.* |
PSQLException: 关系 "table_info" 不存在 |
平台库结构未导入(见 §4 已知缺口) |
登录必失败 / 所有 StpUtil 抛异常 |
Redis 不可达(硬依赖,无降级) |
| 所有接口返回空白 200 | LicenseFilter.doFilter 漏了 chain.doFilter |
| 前端请求 404 | /js 代理指向错、或后端 context-path 与前端 urlPrefix+adminPath 不一致 |
| SSE 收不到 / 401 | 用了原生 EventSource(带不了 header),或漏传 isTransformResponse:false |
| MCP 客户端连不上 | spring.ai.mcp.server.base-url 未与 context-path(/js/a)对齐,/mcp/message 会 404 |
| 菜单图标缺失 | 图标类名未进 uno.config.ts 的 safelist |
npm install 被拒 |
项目 preinstall: only-allow pnpm,只能用 pnpm |
| 上传大文件失败 | max-file-size: 200MB / max-request-size: 500MB(application.yaml) |
| 文档 | 说明 |
|---|---|
| AI_AGENT.md | 仓库级编码约定:分层落位、命名规范、Agent 工具清单与硬约束、前后端渲染契约 |
ai-server/CODE_WIKI.md |
模块级说明,⚠️ 部分章节仍是 Solon / com.qingjian 时代内容(app.yml、contextPath !/js/a/、启动类 Solon.start()),以代码和本文配置为准 |
continuous-insight-plan.md、tool-call-detail-plan.md 等 |
各特性设计稿(根目录 *-plan.md / *.md) |
readme.md |
离线栅格地图(pmtiles 转换)与 regInfo 授权工具的零散说明 |