From 4988ab5944ab3e27e4adb261707b611064f94f41 Mon Sep 17 00:00:00 2001 From: wkc <978997012@qq.com> Date: Wed, 6 May 2026 14:22:05 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=BE=E8=AE=A1:=20=E4=BF=9D=E6=8C=81?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=B5=81=E6=B0=B4=E5=8E=9F=E5=A7=8B=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...06-bank-upload-original-filename-design.md | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 docs/superpowers/specs/2026-05-06-bank-upload-original-filename-design.md diff --git a/docs/superpowers/specs/2026-05-06-bank-upload-original-filename-design.md b/docs/superpowers/specs/2026-05-06-bank-upload-original-filename-design.md new file mode 100644 index 00000000..c3280458 --- /dev/null +++ b/docs/superpowers/specs/2026-05-06-bank-upload-original-filename-design.md @@ -0,0 +1,88 @@ +# 上传流水文件原始文件名保持设计 + +## 背景 + +项目详情的“上传数据”页支持批量上传流水文件。当前前端提交文件时保留了浏览器选择文件的原始文件名,但后端为了异步处理,会先把文件保存为带有批次号、序号和时间戳的临时文件名。异步任务再把这个临时文件对象转传给流水分析平台,导致流水分析平台上传接口收到的 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`:无需源码改动,仅做真实页面验证。 +- 数据库:无需结构变更,历史数据不回改。