文件上传
This commit is contained in:
552
docs/design/2026-03-04-create-project-integrate-lsfx-design.md
Normal file
552
docs/design/2026-03-04-create-project-integrate-lsfx-design.md
Normal file
@@ -0,0 +1,552 @@
|
||||
# 创建项目时集成流水分析平台设计方案
|
||||
|
||||
**文档版本**: 1.0
|
||||
**创建日期**: 2026-03-04
|
||||
**作者**: Claude Code
|
||||
**状态**: 待实施
|
||||
|
||||
---
|
||||
|
||||
## 1. 需求概述
|
||||
|
||||
### 1.1 背景
|
||||
|
||||
在纪检初核系统中,创建项目时需要同步在流水分析平台创建对应的项目,以便后续进行流水分析操作。
|
||||
|
||||
### 1.2 目标
|
||||
|
||||
- 创建项目时自动调用流水分析平台的 `getToken` 接口
|
||||
- 获取返回的 `projectId` 并保存到项目表
|
||||
- 确保数据一致性,调用失败时项目创建也失败
|
||||
|
||||
### 1.3 参考文档
|
||||
|
||||
- 《兰溪-流水分析对接-新版.md》
|
||||
- 项目现有代码:`ccdi-project` 模块、`ccdi-lsfx` 模块
|
||||
|
||||
---
|
||||
|
||||
## 2. 设计方案
|
||||
|
||||
### 2.1 总体架构
|
||||
|
||||
#### 业务流程
|
||||
|
||||
```
|
||||
用户创建项目
|
||||
↓
|
||||
Controller接收请求
|
||||
↓
|
||||
Service层处理
|
||||
├─→ 生成projectNo (902000_时间戳)
|
||||
├─→ 调用流水分析平台getToken接口
|
||||
│ ├─→ 成功:获取projectId
|
||||
│ └─→ 失败:抛出异常,事务回滚
|
||||
├─→ 保存项目(包含projectId)
|
||||
└─→ 返回项目信息
|
||||
```
|
||||
|
||||
#### 技术方案
|
||||
|
||||
- **调用方式**: 同步调用
|
||||
- **集成位置**: Service层(`CcdiProjectServiceImpl`)
|
||||
- **事务管理**: Spring声明式事务,失败自动回滚
|
||||
- **异常处理**: 依赖 `LsfxAnalysisClient` 的异常处理
|
||||
|
||||
### 2.2 核心设计决策
|
||||
|
||||
| 决策点 | 选择 | 理由 |
|
||||
|-------|------|------|
|
||||
| 调用方式 | 同步调用 | 数据一致性强,用户体验清晰 |
|
||||
| 保存策略 | 仅保存projectId | token 使用一次后失效,无需保存 |
|
||||
| 集成位置 | Service层 | 业务逻辑集中,事务管理简单 |
|
||||
| 异常处理 | 依赖客户端 | 避免重复日志,Service层只做业务校验 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 详细设计
|
||||
|
||||
### 3.1 数据库设计
|
||||
|
||||
#### ccdi_project 表新增字段
|
||||
|
||||
```sql
|
||||
ALTER TABLE `ccdi_project`
|
||||
ADD COLUMN `lsfx_project_id` INT(11) DEFAULT NULL COMMENT '流水分析平台项目ID'
|
||||
AFTER `low_risk_count`;
|
||||
```
|
||||
|
||||
**字段说明:**
|
||||
- 字段名: `lsfx_project_id`
|
||||
- 类型: `INT(11)`(与流水分析平台保持一致)
|
||||
- 允许为空: 是(理论上不会为空,因为失败会回滚)
|
||||
- 索引: 不设置唯一索引(流水分析平台的 projectId 可能重复)
|
||||
|
||||
### 3.2 参数映射规则
|
||||
|
||||
根据《兰溪-流水分析对接-新版.md》文档,GetTokenRequest 参数配置如下:
|
||||
|
||||
#### 必填参数(固定值)
|
||||
|
||||
| 参数名 | 值 | 说明 |
|
||||
|-------|-----|------|
|
||||
| `userId` | `"902001"` | 操作人员编号(固定值) |
|
||||
| `userName` | `"902001"` | 操作人员姓名(固定值) |
|
||||
| `role` | `"VIEWER"` | 人员角色(固定值) |
|
||||
| `orgCode` | `"902000"` | 行社机构号(固定值) |
|
||||
| `analysisType` | `"-1"` | 分析类型(固定值) |
|
||||
| `departmentCode` | `"902000"` | 客户经理所属营业部机构编码(固定值) |
|
||||
|
||||
#### 必填参数(动态生成)
|
||||
|
||||
| 参数名 | 生成规则 | 说明 |
|
||||
|-------|---------|------|
|
||||
| `projectNo` | `"902000_" + System.currentTimeMillis()` | 项目编号 |
|
||||
| `entityName` | 使用前端传入的 `projectName` | 项目名称 |
|
||||
|
||||
#### 自动生成参数
|
||||
|
||||
| 参数名 | 生成位置 | 说明 |
|
||||
|-------|---------|------|
|
||||
| `appId` | `LsfxAnalysisClient` 配置 | 固定值:`remote_app` |
|
||||
| `appSecretCode` | `LsfxAnalysisClient.getToken()` | MD5(projectNo + "_" + entityName + "_" + appSecret) |
|
||||
|
||||
#### 可选参数
|
||||
|
||||
以下参数不传递(暂不需要):
|
||||
- `entityId` (企业统信码或个人身份证号)
|
||||
- `xdRelatedPersons` (信贷关联人信息)
|
||||
- `jzDataDateId` (金综链流水日期ID)
|
||||
- `innerBSStartDateId` (行内流水开始日期)
|
||||
- `innerBSEndDateId` (行内流水结束日期)
|
||||
|
||||
### 3.3 代码实现
|
||||
|
||||
#### 3.3.1 实体类修改
|
||||
|
||||
**CcdiProject.java**
|
||||
|
||||
```java
|
||||
@Data
|
||||
@TableName("ccdi_project")
|
||||
public class CcdiProject implements Serializable {
|
||||
|
||||
// ... 现有字段 ...
|
||||
|
||||
/** 低风险人数 */
|
||||
private Integer lowRiskCount;
|
||||
|
||||
/** 流水分析平台项目ID */
|
||||
private Integer lsfxProjectId; // 新增字段
|
||||
|
||||
/** 删除标志 */
|
||||
@TableLogic
|
||||
private String delFlag;
|
||||
|
||||
// ... 审计字段 ...
|
||||
}
|
||||
```
|
||||
|
||||
**CcdiProjectVO.java**
|
||||
|
||||
```java
|
||||
@Data
|
||||
public class CcdiProjectVO implements Serializable {
|
||||
|
||||
// ... 现有字段 ...
|
||||
|
||||
/** 低风险人数 */
|
||||
private Integer lowRiskCount;
|
||||
|
||||
/** 流水分析平台项目ID */
|
||||
private Integer lsfxProjectId; // 新增字段
|
||||
|
||||
/** 创建时间 */
|
||||
private Date createTime;
|
||||
|
||||
// ... 其他字段 ...
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.3.2 Service实现
|
||||
|
||||
**CcdiProjectServiceImpl.java**
|
||||
|
||||
```java
|
||||
@Service
|
||||
public class CcdiProjectServiceImpl implements ICcdiProjectService {
|
||||
|
||||
@Resource
|
||||
private CcdiProjectMapper projectMapper;
|
||||
|
||||
@Resource
|
||||
private LsfxAnalysisClient lsfxAnalysisClient; // 新增依赖注入
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public CcdiProjectVO createProject(CcdiProjectSaveDTO dto) {
|
||||
// 1. 调用流水分析平台获取projectId
|
||||
Integer lsfxProjectId = callLsfxPlatform(dto.getProjectName());
|
||||
|
||||
// 2. 创建项目实体
|
||||
CcdiProject project = new CcdiProject();
|
||||
BeanUtils.copyProperties(dto, project);
|
||||
|
||||
// 3. 设置默认值和流水分析平台ID
|
||||
project.setStatus("0"); // 进行中
|
||||
project.setIsArchived(0); // 未归档
|
||||
project.setTargetCount(0);
|
||||
project.setHighRiskCount(0);
|
||||
project.setMediumRiskCount(0);
|
||||
project.setLowRiskCount(0);
|
||||
project.setLsfxProjectId(lsfxProjectId); // 设置流水分析平台ID
|
||||
|
||||
// 4. 保存到数据库
|
||||
projectMapper.insert(project);
|
||||
|
||||
// 5. 返回VO
|
||||
CcdiProjectVO vo = new CcdiProjectVO();
|
||||
BeanUtils.copyProperties(project, vo);
|
||||
return vo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用流水分析平台获取projectId
|
||||
*
|
||||
* @param projectName 项目名称
|
||||
* @return 流水分析平台项目ID
|
||||
* @throws ServiceException 调用失败或响应无效时抛出
|
||||
*/
|
||||
private Integer callLsfxPlatform(String projectName) {
|
||||
// 构建请求参数
|
||||
GetTokenRequest request = new GetTokenRequest();
|
||||
request.setProjectNo("902000_" + System.currentTimeMillis());
|
||||
request.setEntityName(projectName);
|
||||
request.setUserId("902001");
|
||||
request.setUserName("902001");
|
||||
request.setRole("VIEWER");
|
||||
request.setOrgCode("902000");
|
||||
request.setAnalysisType("-1");
|
||||
request.setDepartmentCode("902000");
|
||||
|
||||
// 调用流水分析平台(异常处理和日志已在 LsfxAnalysisClient 中完成)
|
||||
GetTokenResponse response = lsfxAnalysisClient.getToken(request);
|
||||
|
||||
// 业务层校验:确保响应有效
|
||||
if (response == null || response.getData() == null) {
|
||||
throw new ServiceException("流水分析平台响应数据为空");
|
||||
}
|
||||
|
||||
if (response.getData().getProjectId() == null) {
|
||||
throw new ServiceException("流水分析平台返回的projectId为空");
|
||||
}
|
||||
|
||||
// 校验返回码
|
||||
if (!"200".equals(response.getCode())) {
|
||||
throw new ServiceException("流水分析平台返回错误: " + response.getMessage());
|
||||
}
|
||||
|
||||
return response.getData().getProjectId();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3.4 异常处理与事务管理
|
||||
|
||||
#### 3.4.1 异常处理策略
|
||||
|
||||
**场景1:流水分析平台调用失败**
|
||||
- `LsfxAnalysisClient` 抛出 `LsfxApiException`
|
||||
- Service 层捕获后转换为 `ServiceException`
|
||||
- Spring 事务自动回滚
|
||||
- 用户收到明确错误提示
|
||||
|
||||
**场景2:网络超时**
|
||||
- `LsfxAnalysisClient` 配置了超时时间(连接30秒,读取60秒)
|
||||
- 超时后抛出异常,事务回滚
|
||||
|
||||
**场景3:响应数据无效**
|
||||
- Service 层进行业务校验
|
||||
- 发现无效响应抛出 `ServiceException`
|
||||
- 事务回滚
|
||||
|
||||
#### 3.4.2 事务管理
|
||||
|
||||
```java
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public CcdiProjectVO createProject(CcdiProjectSaveDTO dto) {
|
||||
// 任何一步失败,整个事务自动回滚
|
||||
Integer lsfxProjectId = callLsfxPlatform(dto.getProjectName());
|
||||
|
||||
// ... 数据库操作 ...
|
||||
|
||||
projectMapper.insert(project);
|
||||
return vo;
|
||||
}
|
||||
```
|
||||
|
||||
**特性:**
|
||||
- 声明式事务管理
|
||||
- 任何异常都触发回滚
|
||||
- 保证数据一致性
|
||||
|
||||
#### 3.4.3 日志记录
|
||||
|
||||
日志记录已在 `LsfxAnalysisClient.getToken()` 中实现:
|
||||
|
||||
```java
|
||||
// LsfxAnalysisClient 中的日志
|
||||
log.info("【流水分析】获取Token请求: projectNo={}, entityName={}", ...);
|
||||
log.info("【流水分析】获取Token成功: projectId={}, 耗时={}ms", ...);
|
||||
log.error("【流水分析】获取Token失败: projectNo={}, error={}", ...);
|
||||
```
|
||||
|
||||
Service 层无需重复记录日志。
|
||||
|
||||
---
|
||||
|
||||
## 4. 实施步骤
|
||||
|
||||
### 4.1 实施顺序
|
||||
|
||||
**步骤1:数据库变更**
|
||||
```bash
|
||||
# 执行SQL脚本
|
||||
mysql -u root -p ccdi < doc/design/2026-03-04-add-lsfx-project-id.sql
|
||||
```
|
||||
|
||||
**步骤2:修改实体类和VO**
|
||||
- `CcdiProject` 实体类添加 `lsfxProjectId` 字段
|
||||
- `CcdiProjectVO` 视图对象添加 `lsfxProjectId` 字段
|
||||
|
||||
**步骤3:修改 Service 实现**
|
||||
- `CcdiProjectServiceImpl` 注入 `LsfxAnalysisClient`
|
||||
- 添加 `callLsfxPlatform()` 私有方法
|
||||
- 修改 `createProject()` 方法
|
||||
|
||||
**步骤4:单元测试**
|
||||
- 测试正常创建项目流程
|
||||
- 测试流水分析平台调用失败场景
|
||||
- 测试网络超时场景
|
||||
|
||||
**步骤5:集成测试**
|
||||
- 使用 Swagger 测试完整流程
|
||||
- 验证数据库中 `lsfx_project_id` 是否正确保存
|
||||
- 验证流水分析平台是否成功创建项目
|
||||
|
||||
**步骤6:前端适配(可选)**
|
||||
- 如果前端需要展示 `lsfxProjectId`,修改前端页面
|
||||
|
||||
### 4.2 数据库迁移脚本
|
||||
|
||||
**文件路径**: `doc/design/2026-03-04-add-lsfx-project-id.sql`
|
||||
|
||||
```sql
|
||||
-- ====================================
|
||||
-- 功能:ccdi_project 表新增流水分析平台项目ID字段
|
||||
-- 日期:2026-03-04
|
||||
-- 作者:Claude Code
|
||||
-- ====================================
|
||||
|
||||
USE ccdi;
|
||||
|
||||
-- 新增字段
|
||||
ALTER TABLE `ccdi_project`
|
||||
ADD COLUMN `lsfx_project_id` INT(11) DEFAULT NULL COMMENT '流水分析平台项目ID'
|
||||
AFTER `low_risk_count`;
|
||||
|
||||
-- 验证
|
||||
SELECT * FROM `ccdi_project` LIMIT 1;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 测试方案
|
||||
|
||||
### 5.1 单元测试
|
||||
|
||||
**测试类**: `CcdiProjectServiceImplTest.java`
|
||||
|
||||
```java
|
||||
@SpringBootTest
|
||||
public class CcdiProjectServiceImplTest {
|
||||
|
||||
@Resource
|
||||
private ICcdiProjectService projectService;
|
||||
|
||||
@Resource
|
||||
private LsfxAnalysisClient lsfxAnalysisClient;
|
||||
|
||||
@Test
|
||||
public void testCreateProject_Success() {
|
||||
// 准备数据
|
||||
CcdiProjectSaveDTO dto = new CcdiProjectSaveDTO();
|
||||
dto.setProjectName("测试项目");
|
||||
dto.setDescription("测试描述");
|
||||
dto.setConfigType("default");
|
||||
|
||||
// 执行
|
||||
CcdiProjectVO result = projectService.createProject(dto);
|
||||
|
||||
// 验证
|
||||
assertNotNull(result);
|
||||
assertNotNull(result.getProjectId());
|
||||
assertNotNull(result.getLsfxProjectId());
|
||||
assertEquals("测试项目", result.getProjectName());
|
||||
}
|
||||
|
||||
@Test(expected = ServiceException.class)
|
||||
public void testCreateProject_LsfxFailed() {
|
||||
// 模拟流水分析平台失败
|
||||
// 需要使用 Mock 或关闭 Mock Server
|
||||
|
||||
CcdiProjectSaveDTO dto = new CcdiProjectSaveDTO();
|
||||
dto.setProjectName("测试项目");
|
||||
|
||||
projectService.createProject(dto);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 5.2 集成测试
|
||||
|
||||
**测试步骤:**
|
||||
|
||||
1. **启动 Mock Server**
|
||||
```bash
|
||||
cd lsfx-mock-server
|
||||
python app.py
|
||||
```
|
||||
|
||||
2. **访问 Swagger**
|
||||
```
|
||||
http://localhost:8080/swagger-ui/index.html
|
||||
```
|
||||
|
||||
3. **测试创建项目接口**
|
||||
- 接口: `POST /ccdi/project`
|
||||
- 请求体:
|
||||
```json
|
||||
{
|
||||
"projectName": "测试项目001",
|
||||
"description": "测试项目描述",
|
||||
"configType": "default"
|
||||
}
|
||||
```
|
||||
|
||||
4. **验证响应**
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"msg": "项目创建成功",
|
||||
"data": {
|
||||
"projectId": 1,
|
||||
"projectName": "测试项目001",
|
||||
"lsfxProjectId": 77, // 应该有值
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
5. **验证数据库**
|
||||
```sql
|
||||
SELECT project_id, project_name, lsfx_project_id
|
||||
FROM ccdi_project
|
||||
WHERE project_id = 1;
|
||||
```
|
||||
|
||||
### 5.3 异常测试
|
||||
|
||||
**测试场景:**
|
||||
|
||||
1. **流水分析平台不可用**
|
||||
- 关闭 Mock Server
|
||||
- 创建项目应该失败
|
||||
- 数据库不应该有新记录
|
||||
|
||||
2. **网络超时**
|
||||
- 修改 Mock Server 延迟超过60秒
|
||||
- 创建项目应该超时失败
|
||||
|
||||
3. **返回错误码**
|
||||
- 修改 Mock Server 返回错误码(如40104)
|
||||
- 创建项目应该失败并显示对应错误信息
|
||||
|
||||
---
|
||||
|
||||
## 6. 风险与注意事项
|
||||
|
||||
### 6.1 风险分析
|
||||
|
||||
| 风险 | 影响 | 缓解措施 |
|
||||
|-----|------|---------|
|
||||
| 流水分析平台不可用 | 无法创建项目 | 提供明确的错误提示,用户可稍后重试 |
|
||||
| 网络延迟 | 创建项目缓慢 | 已配置合理超时时间(60秒) |
|
||||
| projectId 重复 | 无影响 | 不设置唯一索引 |
|
||||
| 事务回滚失败 | 数据不一致 | 依赖 Spring 事务管理,经过充分验证 |
|
||||
|
||||
### 6.2 注意事项
|
||||
|
||||
1. **依赖外部服务**
|
||||
- 流水分析平台必须可用
|
||||
- 建议在生产环境做好监控和告警
|
||||
|
||||
2. **无重试机制**
|
||||
- 失败需要用户手动重试
|
||||
- 可以考虑在 UI 层提供重试按钮
|
||||
|
||||
3. **项目编号唯一性**
|
||||
- 使用时间戳保证唯一性
|
||||
- 理论上不会重复(毫秒级时间戳)
|
||||
|
||||
4. **前端适配**
|
||||
- 当前设计不要求前端传 `lsfxProjectId`
|
||||
- 如果需要展示,修改前端页面即可
|
||||
|
||||
---
|
||||
|
||||
## 7. 变更清单
|
||||
|
||||
| 类型 | 文件 | 变更内容 | 状态 |
|
||||
|-----|------|---------|------|
|
||||
| 数据库 | `ccdi_project` 表 | 新增 `lsfx_project_id` 字段 | 待执行 |
|
||||
| SQL | `2026-03-04-add-lsfx-project-id.sql` | 数据库迁移脚本 | 待创建 |
|
||||
| 实体类 | `CcdiProject.java` | 新增 `lsfxProjectId` 属性 | 待修改 |
|
||||
| VO | `CcdiProjectVO.java` | 新增 `lsfxProjectId` 属性 | 待修改 |
|
||||
| Service | `CcdiProjectServiceImpl.java` | 注入 `LsfxAnalysisClient`,添加调用逻辑 | 待修改 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 后续优化建议
|
||||
|
||||
### 8.1 短期优化
|
||||
|
||||
1. **添加重试机制**
|
||||
- 在 Service 层添加自动重试(最多3次)
|
||||
- 使用 Spring Retry 框架
|
||||
|
||||
2. **异步调用(可选)**
|
||||
- 如果创建速度成为瓶颈,可改为异步调用
|
||||
- 需要增加状态管理和轮询接口
|
||||
|
||||
### 8.2 长期优化
|
||||
|
||||
1. **批量创建支持**
|
||||
- 如果需要批量创建项目,考虑批量调用流水分析平台
|
||||
|
||||
2. **缓存机制**
|
||||
- 如果 token 需要重复使用,考虑缓存机制
|
||||
- 需要处理 token 过期问题
|
||||
|
||||
---
|
||||
|
||||
## 9. 参考资料
|
||||
|
||||
- 《兰溪-流水分析对接-新版.md》
|
||||
- 项目 CLAUDE.md 文档
|
||||
- Spring Boot 事务管理文档
|
||||
- MyBatis Plus 使用指南
|
||||
|
||||
---
|
||||
|
||||
**文档结束**
|
||||
Reference in New Issue
Block a user