Files
ccdi/docs/design/2026-03-04-create-project-integrate-lsfx-design.md
wkc d97a34f3b9 docs: 更新设计文档状态并添加实施总结
- 更新设计文档状态为'已实施'
- 添加实施总结文档
- 记录所有变更和测试结果
- 包含Git提交记录和性能分析
2026-03-04 11:15:24 +08:00

14 KiB
Raw Blame History

创建项目时集成流水分析平台设计方案

文档版本: 1.0 创建日期: 2026-03-04 作者: Claude Code 状态: 已实施 实施日期: 2026-03-04 测试状态: 测试通过


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 集成测试

测试步骤:

  1. 启动 Mock Server

    cd lsfx-mock-server
    python app.py
    
  2. 访问 Swagger

    http://localhost:8080/swagger-ui/index.html
    
  3. 测试创建项目接口

    • 接口: POST /ccdi/project
    • 请求体:
      {
        "projectName": "测试项目001",
        "description": "测试项目描述",
        "configType": "default"
      }
      
  4. 验证响应

    {
      "code": 200,
      "msg": "项目创建成功",
      "data": {
        "projectId": 1,
        "projectName": "测试项目001",
        "lsfxProjectId": 77,  // 应该有值
        ...
      }
    }
    
  5. 验证数据库

    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 使用指南

文档结束