Files
ccdi/docs/design/2026-03-29-project-import-history-design.md

360 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 历史项目导入新项目功能设计文档
## 背景
当前项目管理页已经存在“导入历史项目”前端弹窗壳子,但能力仍停留在演示阶段:
- 仅支持前端 Mock 历史项目数据
- 仅支持单选历史项目
- 没有真实创建新项目与复制流水的后端逻辑
- 没有接通现有打标、结果表生成、统计刷新链路
与此同时,系统已经具备以下基础能力:
- [`CcdiProjectServiceImpl#createProject`](/Users/wkc/Desktop/ccdi/ccdi/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectServiceImpl.java#L44) 可创建本地项目,并在创建时调用流水分析平台 `getToken` 接口获取并绑定 `lsfxProjectId`
- [`CcdiFileUploadServiceImpl`](/Users/wkc/Desktop/ccdi/ccdi/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImpl.java) 已打通文件上传后自动触发打标的异步链路
- [`CcdiBankTagServiceImpl`](/Users/wkc/Desktop/ccdi/ccdi/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiBankTagServiceImpl.java) 已具备项目状态切换、标签重算、结果表刷新能力
- [`detail.vue`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/src/views/ccdiProject/detail.vue) 已具备项目状态轮询能力
因此本次需要补齐“从一个或多个历史项目复制流水到新项目”的真实业务闭环。
## 目标
- 在“导入历史项目”弹窗中支持填写新项目名称、备注、交易日期范围,并多选历史项目
- 点击提交后先创建新项目,再异步复制历史流水到新项目
- 若填写时间范围,仅复制 `trxDate` 命中的流水
- 若多个来源项目存在重复流水,按现有流水唯一性口径去重后写入新项目
- 同步复制真正贡献了被导入流水的成功文件上传记录
- 历史导入完成后自动触发打标、结果表生成和统计刷新
- 提交成功后关闭弹窗并跳转到新项目详情页,由详情页承接进度展示
- 历史导入生成的文件记录在新项目中只读展示,不允许删除
## 非目标
- 不支持继承来源项目的参数配置
- 不支持合并多个来源项目的参数规则
- 不支持导入“进行中”项目
- 不支持复制失败或进行中的文件上传记录
- 不支持删除历史导入生成的文件记录
- 不新增独立的“导入任务管理页面”
- 不新增额外复杂导入状态体系或独立进度中心
## 已确认业务口径
- 历史项目可选范围:仅“已完成”“已归档”项目
- 新项目参数来源:固定走现有新建项目默认参数逻辑
- 时间过滤字段:固定按 `trxDate`
- 重复流水处理:按现有唯一性口径去重
- 提交后页面行为:关闭弹窗并跳转到新项目详情页
- 文件记录复制范围:仅复制 `parsed_success` 且确实贡献了被复制流水的记录
- 文件记录展示:保留原文件名,并标记“历史导入来源”
- 删除规则:历史导入生成的文件记录不允许删除
## 方案对比
### 方案一:创建项目后启动独立历史导入异步任务
- 做法:提交接口先调用现有 `createProject`,再在事务提交后启动独立历史导入服务,完成流水复制、文件记录复制、自动打标和结果刷新
- 优点:业务语义清晰,和“上传文件”链路解耦,适合历史复制场景
- 缺点:需要新增历史导入 DTO、接口和异步编排服务
### 方案二:将历史导入伪装成“虚拟上传文件”
- 做法:把来源项目批次伪装为上传任务,强行复用文件上传服务全链路
- 优点:看似能复用更多现有代码
- 缺点:历史导入并不是真实上传,会导致批次、错误信息、来源展示语义混乱
### 方案三:提交接口同步完成流水复制,末尾再触发打标
- 做法:导入接口直接完成所有复制逻辑,再异步触发打标
- 优点:表面上实现路径更短
- 缺点:来源项目多、流水量大时接口耗时过长,和“先创建项目,再后台处理”的需求不符
## 最终方案
采用方案一。
导入接口只负责同步创建新项目和提交异步历史导入任务。历史导入任务在后台完成以下步骤:
1. 校验来源项目和导入条件
2.`trxDate` 过滤并复制流水到新项目
3. 按现有唯一性口径去重
4. 为真正贡献流水的来源批次生成新的文件上传记录
5. 刷新新项目 `targetCount`
6. 自动触发打标、结果表生成和统计刷新
前端提交成功后直接跳转到新项目详情页,复用现有项目状态轮询和上传记录列表。
## 现状分析
## 1. 新建项目现状
[`CcdiProjectServiceImpl#createProject`](/Users/wkc/Desktop/ccdi/ccdi/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectServiceImpl.java#L44) 会先调用 `callLsfxPlatform(projectName)` 获取流水分析平台项目 ID再将其写入本地项目表的 `lsfxProjectId` 字段。
[`callLsfxPlatform`](/Users/wkc/Desktop/ccdi/ccdi/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiProjectServiceImpl.java#L247) 当前通过 `lsfxAnalysisClient.getToken(request)` 调用流水分析平台接口,并校验返回的 `projectId` 非空。
这意味着历史项目导入时创建新项目,可以直接复用现有创建逻辑,不需要额外新增“平台建项目”调用路径。
## 2. 文件上传记录现状
[`ccdi_file_upload_record`](/Users/wkc/Desktop/ccdi/ccdi/sql/ccdi_file_upload_record.sql) 当前存储字段包括:
- `project_id`
- `lsfx_project_id`
- `log_id`
- `file_name`
- `file_status`
- `enterprise_names`
- `account_nos`
- `upload_time`
- `upload_user`
[`CcdiBankStatementMapper.xml`](/Users/wkc/Desktop/ccdi/ccdi/ccdi-project/src/main/resources/mapper/ccdi/project/CcdiBankStatementMapper.xml#L330) 会按 `project_id + log_id(batch_id)` 将流水和文件记录关联起来,用于详情页展示原始文件名。
因此历史导入不能直接复用来源项目原始 `logId`,否则会在新项目维度造成批次语义混乱。新项目内必须对导入批次重新分配新的批次号,并同步写入新项目的文件记录与流水数据。
## 3. 详情页现状
[`UploadData.vue`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/src/views/ccdiProject/components/detail/UploadData.vue) 已具备:
- 上传记录分页列表
- 项目打标中的只读锁定
- 轮询刷新文件记录与统计
- 删除文件记录入口
[`detail.vue`](/Users/wkc/Desktop/ccdi/ccdi/ruoyi-ui/src/views/ccdiProject/detail.vue) 已具备项目状态轮询。
因此本次只需要让历史导入记录进入现有上传记录列表,并在行操作层和删除接口层增加“历史导入不可删除”的约束。
## 详细设计
## 1. 后端接口设计
### 1.1 查询可导入历史项目
- URL`GET /ccdi/project/history`
- 权限:沿用项目列表查询权限
- 入参:
- `projectName`:可选,按项目名模糊搜索
- 返回:
- 仅返回状态为“已完成”“已归档”的项目
- 返回字段包含项目 ID、项目名称、状态、创建时间、目标人数等弹窗展示所需字段
### 1.2 提交历史导入
- URL`POST /ccdi/project/import`
- 权限:沿用项目新增权限
- 入参建议:
- `projectName`
- `description`
- `sourceProjectIds`
- `startDate`
- `endDate`
- 返回:
- 新项目 `projectId`
- 新项目基础信息
接口执行顺序:
1. 校验入参
2. 调用现有 `createProject`
3. 在事务提交后启动历史导入异步任务
4. 立即返回新项目信息给前端
## 2. 新增导入 DTO 与 VO
新增独立 DTO避免和普通新建项目 DTO 混用:
- `CcdiProjectImportHistoryDTO`
- `projectName`
- `description`
- `List<Long> sourceProjectIds`
- `String startDate`
- `String endDate`
新增历史项目列表 VO
- `CcdiProjectHistoryListItemVO`
- `projectId`
- `projectName`
- `projectStatus`
- `createTime`
- `targetCount`
- `warningCount`
## 3. 历史导入异步服务设计
新增独立历史导入服务,职责单一为“将历史项目流水导入到新项目”。
建议命名示例:
- `ICcdiProjectHistoryImportService`
- `CcdiProjectHistoryImportServiceImpl`
核心步骤:
1. 校验新项目存在且未归档
2. 校验来源项目均为“已完成 / 已归档”
3. 查询来源项目成功文件记录与流水数据
4.`trxDate` 过滤流水
5. 按现有唯一性口径去重
6. 批量写入新项目流水
7. 根据实际贡献流水的批次生成新项目文件记录
8. 刷新 `targetCount`
9. 调用 `bankTagService.submitAutoRebuild(projectId, triggerType)`
## 4. 流水复制规则
### 4.1 复制范围
- 仅处理所选来源项目
- 若未填写时间范围,则复制全部流水
- 若填写时间范围,则仅复制 `trxDate` 位于区间内的流水
### 4.2 去重规则
- 沿用当前 `ccdi_bank_statement` 入库唯一性口径
- 若多个来源项目存在重复流水,新项目中仅保留一份
### 4.3 空导入处理
- 若所有来源项目在过滤后均无可导入流水,则导入任务判定为业务失败
- 新项目保留,但不触发后续打标
- 前端详情页可看到失败结果,不回滚项目
## 5. 文件上传记录复制规则
### 5.1 可复制记录范围
- 仅复制 `parsed_success`
- 仅复制那些“至少贡献了一条被导入流水”的来源批次
- 失败记录、进行中记录一律不复制
### 5.2 批次号重映射
新项目内每个来源批次必须生成新的批次标识,并同步写入:
- 新项目文件上传记录的 `logId`
- 新项目流水表的 `batchId`
这样可以确保:
- 与新项目后续真实上传文件的批次不冲突
- 详情页仍能正确通过 `project_id + log_id(batch_id)` 关联文件与流水
### 5.3 来源标识
为支持前端展示“历史导入来源”,建议在 `ccdi_file_upload_record` 增加最小必要字段:
- `source_type``UPLOAD` / `PULL_BANK_INFO` / `HISTORY_IMPORT`
- `source_project_id`
- `source_project_name`
其中:
- 当前已有上传/拉取记录可回填默认 `source_type`
- 历史导入记录固定写入 `HISTORY_IMPORT`
## 6. 删除约束设计
### 6.1 前端删除约束
历史导入记录在上传列表中只读展示:
- 保留文件名
- 展示“历史导入 · 来源项目名”
- 不显示“删除”按钮
### 6.2 后端删除约束
[`deleteFileUploadRecord`](/Users/wkc/Desktop/ccdi/ccdi/ccdi-project/src/main/java/com/ruoyi/ccdi/project/service/impl/CcdiFileUploadServiceImpl.java) 需增加后端兜底校验:
- 若记录属于 `HISTORY_IMPORT`,直接拒绝删除
- 返回明确错误信息,例如“历史导入文件不支持删除”
这样可以避免前端绕过行操作约束后误删导入数据。
## 7. 打标与结果刷新衔接
历史导入完成后,不新增单独导入状态机,直接复用现有打标链路:
1. 复制流水成功
2. 刷新新项目 `targetCount`
3. 调用 `bankTagService.submitAutoRebuild(...)`
4. 项目状态进入“打标中”
5. 打标完成后刷新结果表、风险统计和总览数据
6. 项目状态回到“已完成”
导入失败时:
- 项目保留
- 不进入打标
- 通过上传记录或错误提示体现失败结果
## 8. 前端弹窗设计
弹窗采用纵向布局:
### 8.1 新项目配置区
位于弹窗上方,包含:
- 新项目名称
- 备注
- 交易日期范围
### 8.2 历史项目选择区
位于弹窗下方,包含:
- 项目名称搜索
- 多选历史项目列表
- 固定高度滚动区域
列表只显示“已完成”“已归档”项目。
## 9. 提交后交互设计
提交成功后:
1. 关闭弹窗
2. 提示“历史项目导入任务已开始”
3. 跳转到新项目详情页
详情页承接方式:
- 复用现有项目状态轮询
- 复用上传记录列表轮询
- 用户在详情页中观察历史导入记录、项目状态和后续打标完成情况
## 10. 测试关注点
后续实施时需重点验证:
- 多历史项目多选导入
- `trxDate` 时间范围过滤
- 跨项目重复流水去重
- 历史导入记录来源展示
- 历史导入记录删除拦截
- 导入完成后自动打标与结果表刷新
- 无可导入流水时的失败呈现
## 风险与约束
- 现有文件记录与流水关联依赖 `project_id + log_id(batch_id)`,批次号重映射必须保证稳定且唯一
- 历史导入记录一旦允许删除,会破坏“复制数据来源可追溯”的约束,因此本次明确禁止删除
- 本次不引入额外任务中心,导入失败的展示能力依赖现有详情页与记录列表,需要实施时保持错误信息可读
## 结论
本次采用“先创建新项目,再异步导入历史流水并触发现有打标链路”的方案,保持最短路径实现。
该方案满足以下要求:
- 贴合现有项目创建和详情页状态轮询结构
- 不混淆“上传文件”和“复制历史流水”两类业务语义
- 能复用现有打标、结果表和统计刷新能力
- 能明确保证历史导入文件记录不可删除
后续进入实施计划阶段时,需要按仓库约定分别输出前端、后端两份实施计划。