Files
ccdi/docs/superpowers/specs/2026-05-06-bank-upload-original-filename-design.md

89 lines
6.0 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.
# 上传流水文件原始文件名保持设计
## 背景
项目详情的“上传数据”页支持批量上传流水文件。当前前端提交文件时保留了浏览器选择文件的原始文件名,但后端为了异步处理,会先把文件保存为带有批次号、序号和时间戳的临时文件名。异步任务再把这个临时文件对象转传给流水分析平台,导致流水分析平台上传接口收到的 multipart 文件名不是用户初始上传的文件名。
同时,后端在查询流水分析平台文件状态后,会用平台返回的 `uploadFileName``downloadFileName` 覆盖本系统上传记录 `ccdi_file_upload_record.file_name`,页面列表读取该字段展示文件名,因此页面展示名也可能与初始上传文件名不一致。
## 目标
- 新上传流水文件时,页面展示的文件名必须保持用户初始上传文件名。
- 调用流水分析平台上传文件接口时multipart 文件 part 的 filename 必须保持用户初始上传文件名。
- 临时文件仍需保持唯一命名,避免批量上传、同名文件或并发上传互相覆盖。
- 本次只处理“上传本地流水文件”链路,不改变“拉取本行信息”链路的现有文件名展示与状态处理行为。
- 历史已上传记录不做批量回改。
## 非目标
- 不修改历史上传记录的文件名。
- 不调整上传入口、文件格式限制、文件大小限制、异步任务调度和解析轮询规则。
- 不新增数据库字段。
- 不改变流水分析平台接口地址、请求参数名和响应解析口径。
- 不改变“拉取本行信息”生成的上传记录文件名规则。
## 现状链路
1. 前端 `UploadData.vue``selectedFiles.map((f) => f.raw)` 传给 `batchUploadFiles`
2. `ccdiProjectUpload.js` 使用 `FormData.append("files", file)` 上传,浏览器侧仍保留原始文件名。
3. `CcdiFileUploadController.batchUpload` 通过 `MultipartFile.getOriginalFilename()` 校验格式。
4. `CcdiFileUploadServiceImpl.batchUploadFiles` 保存记录时写入原始文件名,但临时文件路径使用 `batchId_index_timestamp_originalFilename`
5. `processFileAsync` 读取临时文件并调用 `lsfxClient.uploadFile(lsfxProjectId, file)`
6. `LsfxAnalysisClient.uploadFile``HttpUtil.uploadFile` 只接收 `File`,最终用 `FileSystemResource` 发送文件multipart filename 来自临时文件名。
7. 查询文件上传状态后,`processRecordAfterLogIdReady` 使用平台返回文件名覆盖 `record.fileName`
## 设计方案
### 后端上传转发
保留当前临时文件唯一命名方式,避免同名文件覆盖。异步处理时以 `CcdiFileUploadRecord.fileName` 作为原始文件名来源,将“临时文件内容”和“原始文件名”一起传给流水分析客户端。
`LsfxAnalysisClient.uploadFile` 增加可指定上传文件名的能力。对项目上传流水链路,调用新签名并传入原始文件名;测试 Controller 或其他调用方如果不传原始文件名,可继续使用现有文件名语义。
`HttpUtil.uploadFile` 增加对“文件内容 + 指定 filename”的 multipart 资源包装。发送时仍使用参数名 `files`,但文件 part 的 filename 使用原始文件名,而不是临时文件名。
### 上传记录文件名
“上传本地流水文件”链路中的 `ccdi_file_upload_record.file_name` 只记录本系统初始上传文件名。查询流水分析平台状态后,该链路不再用 `uploadFileName``downloadFileName` 覆盖该字段。
平台返回的文件大小仍可继续更新到 `file_size`,解析状态、主体名称、账号、错误信息等字段保持现有处理方式。
当前状态后处理逻辑会被本地上传和“拉取本行信息”复用。实现时需要在调用或方法参数上区分来源:本地上传链路保留初始文件名;拉取本行信息链路保持现有行为,继续按当前规则处理平台返回文件名。
### 前端展示
前端无需改造。上传记录列表继续展示后端返回的 `fileName`。由于本地上传链路后端不再覆盖该字段,新上传记录从创建、处理中、成功或失败状态都会展示初始上传文件名。
## 数据流
```text
用户选择文件: 银行流水A.xlsx
-> 前端 FormData files: filename=银行流水A.xlsx
-> 后端 MultipartFile originalFilename=银行流水A.xlsx
-> 本地临时文件: batchId_0_timestamp_银行流水A.xlsx
-> 上传记录 file_name=银行流水A.xlsx
-> 流水分析平台 multipart files: filename=银行流水A.xlsx
-> 页面上传记录 fileName=银行流水A.xlsx
```
## 错误处理
- 原始文件名为空时,沿用 Controller 现有“文件名不能为空”校验。
- 临时文件不存在、上传失败、解析失败时,沿用现有失败状态和错误信息记录方式。
- 指定原始文件名只影响 multipart filename不影响临时文件读取和异常处理。
## 测试计划
1. 后端单测或轻量验证覆盖 multipart 资源 filename传入临时文件和原始文件名后上传请求中的文件名应为原始文件名。
2. 后端链路测试覆盖 `CcdiFileUploadServiceImpl`:创建上传记录后,异步上传调用使用 `record.fileName` 作为原始文件名。
3. 状态处理测试覆盖平台返回 `uploadFileName/downloadFileName` 与初始文件名不一致时,本系统记录仍保持初始文件名。
4. 拉取本行信息链路回归:本次改动不改变其现有文件名展示与状态处理行为。
5. 真实页面测试:在项目详情“上传数据”页上传一个文件名带中文的流水文件,核对页面上传记录展示初始文件名,并通过日志或 mock 接收端确认流水分析平台上传接口收到的 filename 为初始文件名。
## 影响范围
- `ccdi-project`:上传流水异步处理链路。
- `ccdi-lsfx`:流水分析平台上传客户端和 multipart 工具。
- `ruoyi-ui`:无需源码改动,仅做真实页面验证。
- 数据库:无需结构变更,历史数据不回改。