|
|
@@ -0,0 +1,258 @@
|
|
|
+# 清鉴(zsjz-ai)开发快速开始
|
|
|
+
|
|
|
+> 面向新加入的开发者:从零把后端 `ai-server` 和前端 `ai-frontend` 跑起来。
|
|
|
+> 编码约定、分层落位、Agent 子系统等**改代码前必读**的内容见 [AI_AGENT.md](AI_AGENT.md),本文只讲「怎么跑起来」。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 1. 项目构成
|
|
|
+
|
|
|
+```
|
|
|
+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 对话式研判。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 2. 环境要求
|
|
|
+
|
|
|
+| 项 | 版本 | 校验命令 |
|
|
|
+|---|---|---|
|
|
|
+| 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)** 是按案件/工作空间切换的文件库,不是常驻服务。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 3. 准备依赖服务
|
|
|
+
|
|
|
+### 3.1 PostgreSQL
|
|
|
+
|
|
|
+```sql
|
|
|
+CREATE DATABASE "zsjz-ai";
|
|
|
+\c zsjz-ai
|
|
|
+CREATE EXTENSION IF NOT EXISTS vector; -- pgvector,RAG 向量检索必需
|
|
|
+```
|
|
|
+
|
|
|
+连接信息写在 `ai-server/src/main/resources/application-dev.yaml`:
|
|
|
+
|
|
|
+```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 配置。
|
|
|
+
|
|
|
+### 3.2 Elasticsearch
|
|
|
+
|
|
|
+单机开发用默认 9200 即可。索引 settings 见 `ai-server/src/main/resources/es/search-settings.json`(单分片 0 副本 + `\x1F` 分隔符自定义分析器),文档映射见 `module/search/domain/SearchDoc.java`(索引名 `zsjz_search`)。
|
|
|
+
|
|
|
+### 3.3 QingJian 运行期资产
|
|
|
+
|
|
|
+`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 / 基站库)会失败。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 4. 初始化数据库
|
|
|
+
|
|
|
+项目**没有**自动建表机制,`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) | 案件业务表与初始配置 |
|
|
|
+
|
|
|
+```bash
|
|
|
+# 平台库(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` 元数据三表链路。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 5. 启动后端
|
|
|
+
|
|
|
+```bash
|
|
|
+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`)会跟着工作目录走,所以统一在根目录执行更省心。
|
|
|
+
|
|
|
+启动后:
|
|
|
+
|
|
|
+```bash
|
|
|
+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` 起不来。
|
|
|
+
|
|
|
+### 模型 / API Key 配置在哪
|
|
|
+
|
|
|
+Agent 用的模型与厂商信息存平台库 `agent_model` / `agent_model_provider`,通过前端「模型管理」界面(后端 `module/agent` 的 `/models/**`)维护,**不是**写在 yaml 里。想跑通 AI 研判,先在这个界面上配好一个可用的 Provider(DashScope / OpenAI 兼容 / Ollama,三套 AgentScope 扩展都已在 pom 引入)。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 6. 启动前端
|
|
|
+
|
|
|
+```bash
|
|
|
+cd ai-frontend
|
|
|
+pnpm install # = pnpm bootstrap
|
|
|
+pnpm dev # http://localhost:3100
|
|
|
+```
|
|
|
+
|
|
|
+`.env` 与 `.env.development` 关键项(默认值已可用):
|
|
|
+
|
|
|
+```ini
|
|
|
+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` 登录。
|
|
|
+
|
|
|
+日常命令:
|
|
|
+
|
|
|
+```bash
|
|
|
+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` 私有源;装不动私有包时先确认网络/镜像可达。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 7. 联调要点(改接口前必看)
|
|
|
+
|
|
|
+1. **路径三段式**:请求最终落到 `/js/a/...` = `urlPrefix(/js)` + `adminPath(/a)` + 后端 `context-path`。前端 `defHttp` 自动补 `/js`,代码里写 `url: adminPath + '/xxx'`;不走 `defHttp`(`fetch` / `<img src>`)时手动拼 `glob.apiUrl + glob.urlPrefix + glob.adminPath + path`。**这三处必须同步改**。
|
|
|
+2. **鉴权只认请求头**:Sa-Token 配的是 `token-name: x-token`、`is-read-cookie: false`(仅 Header)。`defHttp` 自动注入;`fetch` 场景要手动带 `'x-token': getToken()`。原生 `EventSource` **无法自定义请求头**,SSE 一律用 `fetch` + `body.getReader()` 读增量。
|
|
|
+3. **未登录返回 HTTP 200 + `body.code = 401`**(不是 HTTP 401),前端据此跳登录页。
|
|
|
+4. **两套响应形态**:`module/agent` 的 `/chat/**`(含 `/chat/results/**`)是裸返回,调它们必须传 `{ isTransformResponse: false }`;`/models/**` 是 `Result<T>` 包装,用默认 transform。
|
|
|
+5. **同账号不允许多端登录**(`is-concurrent: false`),后登录顶掉前登录 —— 两个浏览器窗口同时登录,前一个会 401,这是设计行为不是 bug。
|
|
|
+6. **MCP 端点无鉴权**:`/mcpsse`、`/mcp/message`、`/mcpstreamable` 在 sa-token 白名单里(外部 MCP 客户端不带 `x-token`)。代价是**能访问 8980 端口的人即可读取全部案件数据**,非可信网络部署必须在外层(反代/防火墙)限来源。
|
|
|
+7. **`LicenseFilter` 当前是透传实现**,授权校验下沉在 `SystemService` 的 `/sys/info`、`/sys/checkAuth`。它被 Spring 自动注册为 `/*` 全局过滤器,**`doFilter` 里的 `chain.doFilter` 绝不能删** —— 删了表现为所有接口返回 `200 + Content-Length: 0` 空白,连 404 都没有,日志无异常。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 8. 新页面 / 新接口常见坑
|
|
|
+
|
|
|
+- **菜单图标不显示**:`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。
|
|
|
+- **Mapper XML 命名空间**:必须是 `com.zsjz.ai.*`。历史上包名迁移漏改 XML(`com.qingjian.*`)会让 MyBatis 启动即炸。
|
|
|
+- **改后端 DTO 字段 → 同步前端** `src/<domain>/api/types/{dto,query}.ts`。
|
|
|
+- **新增 Agent 工具** → 写清 `@Tool(description=...)` / `@ToolParam`,并同步更新 `AgentToolRegistryTest#TOTAL_BUSINESS_TOOLS` 的数量断言。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 9. 提交前检查清单
|
|
|
+
|
|
|
+```bash
|
|
|
+# 后端
|
|
|
+mvn -pl ai-server -am clean compile
|
|
|
+# 前端
|
|
|
+cd ai-frontend && pnpm type:check && pnpm lint:eslint
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] 接口字段变更 → 后端 DTO/VO 与前端 TS 类型同步
|
|
|
+- [ ] 表结构变更 → `sql/` 补脚本 + `table_info` / `table_field` 元数据同步
|
|
|
+- [ ] 用户级资源接口 → 带 `StpUtil.getLoginIdAsLong()` 归属校验
|
|
|
+- [ ] 无密钥 / 真实库密码 / 内网地址入库
|
|
|
+- [ ] 中文注释齐全,无 `System.out.println` 残留
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 10. 故障排查速查
|
|
|
+
|
|
|
+| 现象 | 原因 / 处置 |
|
|
|
+|---|---|
|
|
|
+| 启动即 `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`) |
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 11. 延伸阅读
|
|
|
+
|
|
|
+| 文档 | 说明 |
|
|
|
+|---|---|
|
|
|
+| [AI_AGENT.md](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` 授权工具的零散说明 |
|