# 历史项目导入新项目实施计划记录

## 本次改动

- 新增后端实施计划 `docs/plans/backend/2026-03-29-project-import-history-backend-implementation.md`
- 新增前端实施计划 `docs/plans/frontend/2026-03-29-project-import-history-frontend-implementation.md`
- 后端计划拆分为表结构与 Mapper、接口契约、任务提交流程、流水复制、删除拦截与回归验证五个任务
- 前端计划拆分为 API 与跳转、弹窗重构、上传记录只读展示、校验与回归验证四个任务

## 计划结论

- 后端按“同步创建项目 + 异步历史导入任务”主线推进
- 前端按“弹窗真实提交 + 详情页承接状态 + 历史导入记录只读展示”主线推进
- 执行阶段优先遵循最短路径实现，不引入额外任务中心或兼容性分支

## 审阅说明

- 按仓库约束，本次未启用 subagent
- 已人工检查计划与设计文档的一致性，确认未引入未被批准的新方案

## 执行记录

### 2026-03-29 Task 1 后端数据结构预置

- 新增迁移脚本 `sql/migration/2026-03-29-add-file-upload-history-import-source-fields.sql`，为 `ccdi_file_upload_record` 增加 `source_type`、`source_project_id`、`source_project_name`
- 扩展 `CcdiFileUploadRecord` 实体与 `CcdiFileUploadRecordMapper.xml`，补齐来源字段映射、查询列和批量插入列
- 调整 `CcdiBankStatementMapper.xml` 详情查询，补充 `fur.source_project_name` 查询痕迹，为后续历史导入来源展示预留字段
- 新增 `CcdiBankStatementMapperXmlTest` 断言，先验证缺少来源字段时失败，再验证补齐后通过
- 验证命令：`mvn -pl ccdi-project -am -Dtest=CcdiBankStatementMapperXmlTest -Dsurefire.failIfNoSpecifiedTests=false test`

### 2026-03-29 Task 2 后端接口契约落地

- 新增 `CcdiProjectImportHistoryDTO` 与 `CcdiProjectHistoryListItemVO`，明确历史导入入参与历史项目列表返回结构
- 扩展 `ICcdiProjectService`、`CcdiProjectController` 与 `CcdiProjectServiceImpl`，补齐 `/ccdi/project/history` 和 `/ccdi/project/import` 最小接口骨架
- 新增控制器契约测试与导入返回结构测试，锁定真实接口路径和“项目创建成功”返回数据
- 验证命令：`mvn -pl ccdi-project -am -Dtest=CcdiProjectControllerContractTest,CcdiProjectControllerTest -Dsurefire.failIfNoSpecifiedTests=false test`

### 2026-03-29 Task 3 后端提交流程接通

- 新增 `ICcdiProjectHistoryImportService` 与 `CcdiProjectHistoryImportServiceImpl`，作为历史项目导入的异步编排入口
- 为 `CcdiProjectMapper`/XML 增加 `selectHistoryProjects`，固定查询状态 `1/2` 并支持项目名模糊搜索
- 将 `CcdiProjectServiceImpl#importFromHistory` 改为“创建项目后注册 `afterCommit` 回调”，事务提交后再调用 `historyImportService.submitImport(...)`
- 在 `CcdiProjectServiceImplTest` 中新增历史项目列表查询与事务后提交流程测试
- 验证命令：`mvn -pl ccdi-project -am -Dtest=CcdiProjectServiceImplTest,CcdiProjectControllerTest -Dsurefire.failIfNoSpecifiedTests=false test`

### 2026-03-29 Task 4 后端流水复制与来源记录生成

- 为 `CcdiBankStatementMapper`/XML 增加历史导入只读查询，按 `projectId + batchId + trxDate` 范围读取来源流水
- 为 `CcdiFileUploadRecordMapper`/XML 增加来源项目成功批次查询，并在批量插入时补齐 `log_id`
- 将 `CcdiProjectHistoryImportServiceImpl` 扩展为真实异步复制链路：读取成功批次、按新批次号重建目标流水、按来源字段生成历史导入文件记录，并在导入前做内存去重
- 新增 `CcdiProjectHistoryImportServiceImplTest` 与 XML 断言，验证日期范围传递、跨来源批次去重、新批次号生成及来源标识写入
- 验证命令：`mvn -pl ccdi-project -am -Dtest=CcdiProjectHistoryImportServiceImplTest,CcdiFileUploadServiceImplTest,CcdiBankStatementMapperXmlTest -Dsurefire.failIfNoSpecifiedTests=false test`

### 2026-03-29 Task 5 后端自动打标与删除拦截收口

- 在 `CcdiProjectHistoryImportServiceImpl` 中补齐目标人数刷新与 `bankTagService.submitAutoRebuild(targetProjectId, TriggerType.AUTO_BATCH_UPLOAD)` 调用
- 在 `CcdiFileUploadServiceImpl#validateDeleteRecord` 中优先拦截 `sourceType = HISTORY_IMPORT` 的记录，返回“历史导入文件不支持删除”
- 扩展历史导入服务测试与文件删除测试，锁定自动重算与禁删行为
- 完成后端回归验证：`mvn -pl ccdi-project -am -Dtest=CcdiProjectControllerTest,CcdiProjectControllerContractTest,CcdiProjectServiceImplTest,CcdiProjectHistoryImportServiceImplTest,CcdiFileUploadServiceImplTest,CcdiBankStatementMapperXmlTest -Dsurefire.failIfNoSpecifiedTests=false test`

### 2026-03-29 Task 1 前端提交跳转链路接通

- 保持 `ruoyi-ui/src/api/ccdiProject.js` 中真实 `/ccdi/project/history` 与 `/ccdi/project/import` 接口不变
- 调整 `ruoyi-ui/src/views/ccdiProject/index.vue` 的 `handleSubmitImport`，成功后提示“历史项目导入任务已开始”、关闭弹窗、刷新列表并跳转到新项目详情页
- 新增 `ruoyi-ui/tests/unit/project-import-history-submit-flow.test.js`，锁定真实接口地址、成功提示文案与详情页跳转路径
- 验证命令：`cd ruoyi-ui && node tests/unit/project-import-history-submit-flow.test.js`

### 2026-03-29 Task 2 前端导入弹窗重构

- 将 `ImportHistoryDialog.vue` 从单选 + Mock 数据改为“上方新项目配置、下方历史项目多选列表”的纵向结构
- 接入 `listHistoryProjects` 与 `importFromHistory` 真实接口，提交时按计划组装 `projectName/description/sourceProjectIds/startDate/endDate`
- 新增 `project-import-history-dialog-layout.test.js` 与 `project-import-history-dialog-behavior.test.js`，锁定备注字段、日期范围、多选语义、真实接口调用与 `response.data` 回传
- 验证命令：`cd ruoyi-ui && node tests/unit/project-import-history-dialog-layout.test.js && node tests/unit/project-import-history-dialog-behavior.test.js`

### 2026-03-29 Task 3 前端上传记录来源与只读规则

- 在 `UploadData.vue` 的文件列表中增加“来源”列，历史导入记录显示为“历史导入 · 来源项目名”
- 将 `uploadFileActionRules.js` 改为按整行判断动作，对 `sourceType = HISTORY_IMPORT` 的记录直接返回 `null`，隐藏删除入口
- 新增 `upload-data-history-import-readonly.test.js`，并扩展现有文件列表与删除文案测试，锁定来源字段与历史导入只读规则
- 验证命令：`cd ruoyi-ui && node tests/unit/upload-data-file-list-table.test.js && node tests/unit/upload-data-delete-retag-copy.test.js && node tests/unit/upload-data-history-import-readonly.test.js`

### 2026-03-29 Task 4 前端校验与详情页收口验证

- 扩展 `project-import-history-dialog-behavior.test.js`，补齐“请选择历史项目”“请输入新项目名称”两条关键校验文案断言，锁定导入弹窗最小交互约束
- 扩展 `project-detail-tagging-polling.test.js`，补齐 `this.$route.params.projectId` 与 `projectStatusPolling` 代码痕迹断言，确认新项目详情页继续以路由参数承接并保留状态轮询能力
- 完成前端回归验证：`cd ruoyi-ui && node tests/unit/project-import-history-submit-flow.test.js && node tests/unit/project-import-history-dialog-layout.test.js && node tests/unit/project-import-history-dialog-behavior.test.js && node tests/unit/upload-data-file-list-table.test.js && node tests/unit/upload-data-delete-retag-copy.test.js && node tests/unit/upload-data-history-import-readonly.test.js && node tests/unit/project-detail-tagging-polling.test.js`

### 2026-03-29 Task 6 后端历史导入解环修复

- 根据联调时出现的 `CcdiBankTagServiceImpl -> CcdiProjectServiceImpl -> CcdiProjectHistoryImportServiceImpl` 循环依赖，确认根因是 `CcdiProjectServiceImpl#importFromHistory` 直接依赖历史导入服务，而历史导入服务又会回调打标服务
- 新增 `CcdiProjectHistoryImportSubmittedEvent` 与 `CcdiProjectHistoryImportEventListener`，将“创建项目后提交历史导入任务”改为事务提交后发布事件，再由监听器委托 `ICcdiProjectHistoryImportService`
- 调整 `CcdiProjectServiceImpl` 与 `CcdiProjectServiceImplTest`，锁定“导入历史项目在 `afterCommit` 阶段发布事件”的行为，移除对 `ICcdiProjectHistoryImportService` 的直接注入
- 验证命令：`mvn -pl ccdi-project -am -Dtest=CcdiProjectServiceImplTest -Dsurefire.failIfNoSpecifiedTests=false test`
- 启动验证：`mvn -pl ruoyi-admin -am -DskipTests package` 后执行 `java -jar ruoyi-admin/target/ruoyi-admin.jar`，确认默认启动不再报循环依赖且应用正常启动

### 2026-03-29 Task 7 后端历史导入联调修复

- 针对“通过导入创建的项目一直进行中，上传文件列表也不正确”做联调排查，先执行 `bin/mysql_utf8_exec.sh sql/migration/2026-03-29-add-file-upload-history-import-source-fields.sql`，将 `ccdi_file_upload_record` 缺失的 `source_type`、`source_project_id`、`source_project_name` 字段补齐到联调数据库
- 继续排查异步导入日志，定位 `CcdiBankStatementMapper.xml` 中 `selectStatementsForHistoryImport` 存在两处 SQL 组装问题：一处重复拼出 `SELECT select ... FROM ccdi_bank_statement FROM ...`，另一处在日期过滤场景下与动态条件相邻，导致联调日志出现 `?AND`/`17094AND` 形式的拼接结果
- 调整 `selectCcdiBankStatementVo` 为纯列片段，并将历史导入查询的基础条件包裹为 `(bs.project_id = #{projectId}) AND (bs.batch_id = #{batchId})`，避免动态日期条件继续与前置参数直接粘连
- 扩展 `CcdiBankStatementMapperXmlTest`，新增历史导入查询 SQL 渲染断言，锁定“不重复生成 `select` 关键字”与“不生成 `?AND`”两个回归点
- 验证命令：`mvn -pl ccdi-project -am -Dtest=CcdiBankStatementMapperXmlTest -Dsurefire.failIfNoSpecifiedTests=false test`
- 联调验证：`mvn -pl ruoyi-admin -am -DskipTests package` 后启动 `java -jar ruoyi-admin/target/ruoyi-admin.jar`，重新调用 `/ccdi/project/import` 创建项目 `61`；接口轮询确认项目状态从 `0` 进入 `3` 再落到 `1`，上传记录列表返回 `14` 条历史导入文件；数据库确认 `ccdi_project.status = 1`、`target_count = 12`、`ccdi_file_upload_record` 共 `14` 条且 `source_type = HISTORY_IMPORT`、`ccdi_bank_statement` 共 `20` 条
