14 KiB
14 KiB
创建项目时集成流水分析平台设计方案
文档版本: 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 表新增字段
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
@Data
@TableName("ccdi_project")
public class CcdiProject implements Serializable {
// ... 现有字段 ...
/** 低风险人数 */
private Integer lowRiskCount;
/** 流水分析平台项目ID */
private Integer lsfxProjectId; // 新增字段
/** 删除标志 */
@TableLogic
private String delFlag;
// ... 审计字段 ...
}
CcdiProjectVO.java
@Data
public class CcdiProjectVO implements Serializable {
// ... 现有字段 ...
/** 低风险人数 */
private Integer lowRiskCount;
/** 流水分析平台项目ID */
private Integer lsfxProjectId; // 新增字段
/** 创建时间 */
private Date createTime;
// ... 其他字段 ...
}
3.3.2 Service实现
CcdiProjectServiceImpl.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 事务管理
@Transactional(rollbackFor = Exception.class)
public CcdiProjectVO createProject(CcdiProjectSaveDTO dto) {
// 任何一步失败,整个事务自动回滚
Integer lsfxProjectId = callLsfxPlatform(dto.getProjectName());
// ... 数据库操作 ...
projectMapper.insert(project);
return vo;
}
特性:
- 声明式事务管理
- 任何异常都触发回滚
- 保证数据一致性
3.4.3 日志记录
日志记录已在 LsfxAnalysisClient.getToken() 中实现:
// LsfxAnalysisClient 中的日志
log.info("【流水分析】获取Token请求: projectNo={}, entityName={}", ...);
log.info("【流水分析】获取Token成功: projectId={}, 耗时={}ms", ...);
log.error("【流水分析】获取Token失败: projectNo={}, error={}", ...);
Service 层无需重复记录日志。
4. 实施步骤
4.1 实施顺序
步骤1:数据库变更
# 执行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
-- ====================================
-- 功能: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
@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 集成测试
测试步骤:
-
启动 Mock Server
cd lsfx-mock-server python app.py -
访问 Swagger
http://localhost:8080/swagger-ui/index.html -
测试创建项目接口
- 接口:
POST /ccdi/project - 请求体:
{ "projectName": "测试项目001", "description": "测试项目描述", "configType": "default" }
- 接口:
-
验证响应
{ "code": 200, "msg": "项目创建成功", "data": { "projectId": 1, "projectName": "测试项目001", "lsfxProjectId": 77, // 应该有值 ... } } -
验证数据库
SELECT project_id, project_name, lsfx_project_id FROM ccdi_project WHERE project_id = 1;
5.3 异常测试
测试场景:
-
流水分析平台不可用
- 关闭 Mock Server
- 创建项目应该失败
- 数据库不应该有新记录
-
网络超时
- 修改 Mock Server 延迟超过60秒
- 创建项目应该超时失败
-
返回错误码
- 修改 Mock Server 返回错误码(如40104)
- 创建项目应该失败并显示对应错误信息
6. 风险与注意事项
6.1 风险分析
| 风险 | 影响 | 缓解措施 |
|---|---|---|
| 流水分析平台不可用 | 无法创建项目 | 提供明确的错误提示,用户可稍后重试 |
| 网络延迟 | 创建项目缓慢 | 已配置合理超时时间(60秒) |
| projectId 重复 | 无影响 | 不设置唯一索引 |
| 事务回滚失败 | 数据不一致 | 依赖 Spring 事务管理,经过充分验证 |
6.2 注意事项
-
依赖外部服务
- 流水分析平台必须可用
- 建议在生产环境做好监控和告警
-
无重试机制
- 失败需要用户手动重试
- 可以考虑在 UI 层提供重试按钮
-
项目编号唯一性
- 使用时间戳保证唯一性
- 理论上不会重复(毫秒级时间戳)
-
前端适配
- 当前设计不要求前端传
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 短期优化
-
添加重试机制
- 在 Service 层添加自动重试(最多3次)
- 使用 Spring Retry 框架
-
异步调用(可选)
- 如果创建速度成为瓶颈,可改为异步调用
- 需要增加状态管理和轮询接口
8.2 长期优化
-
批量创建支持
- 如果需要批量创建项目,考虑批量调用流水分析平台
-
缓存机制
- 如果 token 需要重复使用,考虑缓存机制
- 需要处理 token 过期问题
9. 参考资料
- 《兰溪-流水分析对接-新版.md》
- 项目 CLAUDE.md 文档
- Spring Boot 事务管理文档
- MyBatis Plus 使用指南
文档结束