From 301fa6c85c43c9347d8cdf2258ec873ec91f152e Mon Sep 17 00:00:00 2001 From: wkc <978997012@qq.com> Date: Wed, 4 Mar 2026 09:47:42 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=87=E4=BB=B6=E4=B8=8A=E4=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CLAUDE.md | 163 ++++++++++++++++-- assets/对接流水分析/ccdi_bank_statement.docx | Bin 0 -> 18569 bytes .../ruoyi/lsfx/client/LsfxAnalysisClient.java | 3 +- .../design/2026-03-04-add-lsfx-project-id.sql | 0 ...04-create-project-integrate-lsfx-design.md | 0 5 files changed, 153 insertions(+), 13 deletions(-) create mode 100644 assets/对接流水分析/ccdi_bank_statement.docx rename {doc => docs}/design/2026-03-04-add-lsfx-project-id.sql (100%) rename {doc => docs}/design/2026-03-04-create-project-integrate-lsfx-design.md (100%) diff --git a/CLAUDE.md b/CLAUDE.md index 4cd78f6..4ef7318 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -34,7 +34,7 @@ POST http://localhost:8080/login/test?username=admin&password=admin123 | 后端技术 | 版本 | 前端技术 | 版本 | |-----------------------------|--------|------------|---------| | Spring Boot | 3.5.8 | Vue.js | 2.6.12 | -| Java | 17 | Element UI | 2.15.14 | +| Java | 21 | Element UI | 2.15.14 | | MyBatis Spring Boot Starter | 3.0.5 | Vuex | 3.6.0 | | MySQL Connector | 8.2.0 | Vue Router | 3.4.9 | | SpringDoc OpenAPI | 2.8.14 | Axios | 0.28.1 | @@ -114,7 +114,10 @@ ccdi/ ├── ruoyi-common/ # 通用工具 (annotations, utils, constants) ├── ruoyi-quartz/ # 定时任务 ├── ruoyi-generator/ # 代码生成器 -├── ruoyi-info-collection/ # 【核心业务模块】信息采集 +├── ccdi-info-collection/ # 【核心业务模块】信息采集 +├── ccdi-project/ # 【核心业务模块】项目管理 +├── ccdi-lsfx/ # 【核心业务模块】流水分析对接 +├── lsfx-mock-server/ # 流水分析模拟服务器 (Python) ├── ruoyi-ui/ # 前端 Vue 应用 ├── sql/ # 数据库脚本 ├── bin/ # 启动脚本 @@ -130,7 +133,11 @@ ruoyi-admin (启动模块) ├── ruoyi-common (共享工具) ├── ruoyi-quartz (定时任务) ├── ruoyi-generator (代码生成) - └── ruoyi-info-collection (信息采集模块) + ├── ccdi-info-collection (信息采集模块) + │ └── 依赖 ruoyi-common + ├── ccdi-project (项目管理模块) + │ └── 依赖 ruoyi-common + └── ccdi-lsfx (流水分析对接模块) └── 依赖 ruoyi-common ``` @@ -140,7 +147,7 @@ ruoyi-admin (启动模块) 3. 在 `ruoyi-admin/pom.xml` 中添加对新模块的依赖 4. 在新模块中按照分层规范创建 controller/service/mapper/domain 包 -### ruoyi-info-collection 业务模块 (核心) +### ccdi-info-collection 业务模块 (核心) 自定义业务模块,包含以下核心功能: @@ -158,14 +165,87 @@ ruoyi-admin (启动模块) **分层结构:** -- Controller: `ruoyi-info-collection/src/main/java/com/ruoyi/info/collection/controller/` -- Service: `ruoyi-info-collection/src/main/java/com/ruoyi/info/collection/service/` -- Mapper: `ruoyi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/` -- Domain: `ruoyi-info-collection/src/main/java/com/ruoyi/info/collection/domain/` +- Controller: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/` +- Service: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/service/` +- Mapper: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/mapper/` +- Domain: `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/domain/` - dto/: 数据传输对象 - vo/: 视图对象 - excel/: Excel导入导出实体 -- XML映射: `ruoyi-info-collection/src/main/resources/mapper/info/collection/` +- XML映射: `ccdi-info-collection/src/main/resources/mapper/info/collection/` + +### ccdi-project 业务模块 (核心) + +项目管理模块,用于管理纪检初核项目的全生命周期: + +**核心功能:** +- 项目创建、更新、删除、查询 +- 项目状态管理 (进行中、已完成、已归档) +- 项目统计(按状态统计数量) +- 模型参数配置管理 + +**主要 Controller:** +- CcdiProjectController: 项目管理 +- CcdiModelParamController: 模型参数配置 + +**分层结构:** +- Controller: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/` +- Service: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/` +- Mapper: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/mapper/` +- Domain: `ccdi-project/src/main/java/com/ruoyi/ccdi/project/domain/` +- XML映射: `ccdi-project/src/main/resources/mapper/ccdi/project/` + +### ccdi-lsfx 业务模块 (核心) + +流水分析平台对接模块,用于与外部流水分析系统交互: + +**核心功能:** +- 获取访问令牌 (Token) +- 上传流水文件并解析 +- 拉取行内流水数据 +- 查询解析状态和结果 +- 获取银行流水明细 + +**主要组件:** +- LsfxAnalysisClient: 流水分析平台客户端 +- LsfxTestController: 测试接口 + +**配置项 (application-dev.yml):** +```yaml +lsfx: + api: + base-url: http://localhost:8000 # 流水分析平台地址 + app-id: your-app-id + app-secret: your-app-secret + client-id: your-client-id + endpoints: + get-token: /api/auth/token + upload-file: /api/files/upload + fetch-inner-flow: /api/flow/inner +``` + +**分层结构:** +- Client: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/client/` +- Controller: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/controller/` +- Domain: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/` + - request/: 请求对象 + - response/: 响应对象 +- Config: `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/config/` + +### lsfx-mock-server (开发测试工具) + +Python 实现的流水分析平台模拟服务器,用于本地开发和测试: + +**用途:** +- 模拟流水分析平台的 API 接口 +- 提供测试数据和模拟响应 +- 支持错误场景模拟 + +**启动方式:** +```bash +cd lsfx-mock-server +python app.py # 默认监听 http://localhost:8000 +``` --- @@ -389,6 +469,55 @@ POST /login/test?username=admin&password=admin123 - **数据库索引**: 0 - **连接超时**: 10s +### 流水分析平台配置 + +项目集成了外部流水分析平台,配置项位于 `application-dev.yml`: + +```yaml +lsfx: + api: + base-url: http://localhost:8000 # 流水分析平台基础地址 + app-id: ccdi-app # 应用ID + app-secret: ccdi-secret-2024 # 应用密钥 + client-id: ccdi-client # 客户端ID + endpoints: + get-token: /api/auth/token # 获取令牌接口 + upload-file: /api/files/upload # 文件上传接口 + fetch-inner-flow: /api/flow/inner # 拉取行内流水接口 +``` + +**开发环境使用 Mock 服务器:** +- 本地开发时,将 `base-url` 设置为 `http://localhost:8000` +- 启动 `lsfx-mock-server` 提供模拟接口 +- 生产环境替换为真实的流水分析平台地址 + +### MCP 配置 + +项目使用 MCP (Model Context Protocol) 连接数据库,配置文件: `.mcp.json` + +```json +{ + "mcpServers": { + "mysql": { + "command": "npx", + "args": ["-y", "@fhuang/mcp-mysql-server"], + "env": { + "MYSQL_HOST": "116.62.17.81", + "MYSQL_PORT": "3306", + "MYSQL_USER": "root", + "MYSQL_PASSWORD": "Kfcx@1234", + "MYSQL_DATABASE": "ccdi" + } + } + } +} +``` + +**使用场景:** +- 通过 MCP 工具直接查询和操作数据库 +- 在开发过程中快速验证数据 +- 生成测试数据和调试 SQL + ### Druid 监控台 访问地址: `http://localhost:8080/druid/` @@ -405,8 +534,11 @@ POST /login/test?username=admin&password=admin123 |---------------|--------------------------------------------------------------------------------| | 应用入口 | `ruoyi-admin/src/main/java/com/ruoyi/RuoYiApplication.java` | | 安全配置 | `ruoyi-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java` | -| 业务 Controller | `ruoyi-info-collection/src/main/java/com/ruoyi/info/collection/controller/` | -| 业务 Mapper XML | `ruoyi-info-collection/src/main/resources/mapper/info/collection/` | +| 信息采集 Controller | `ccdi-info-collection/src/main/java/com/ruoyi/info/collection/controller/` | +| 信息采集 Mapper XML | `ccdi-info-collection/src/main/resources/mapper/info/collection/` | +| 项目管理 Controller | `ccdi-project/src/main/java/com/ruoyi/ccdi/project/controller/` | +| 项目管理 Mapper XML | `ccdi-project/src/main/resources/mapper/ccdi/project/` | +| 流水分析 Client | `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/client/LsfxAnalysisClient.java` | | Vue 路由 | `ruoyi-ui/src/router/index.js` | | Vuex Store | `ruoyi-ui/src/store/` | | 前端 API | `ruoyi-ui/src/api/` | @@ -499,6 +631,15 @@ doc/ 3. 确认 Excel 模板格式正确 4. 检查必填字段是否为空 +### 流水分析平台连接失败 + +**检查项:** +1. 确认 `lsfx-mock-server` 已启动(开发环境) +2. 检查 `application-dev.yml` 中的 `lsfx.api.base-url` 配置 +3. 验证 app-id、app-secret、client-id 是否正确 +4. 检查网络连接和防火墙设置 +5. 查看后端日志中的 HTTP 请求错误信息 + --- ## MyBatis Plus 分页使用 diff --git a/assets/对接流水分析/ccdi_bank_statement.docx b/assets/对接流水分析/ccdi_bank_statement.docx new file mode 100644 index 0000000000000000000000000000000000000000..70090b1bbab6db2ee0ae586c822642d6547e6305 GIT binary patch literal 18569 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 VU?2Jd0AN4