cisd/docs/plans/2026-03-10-file-upload-fileid-design.md
2026-03-11 14:23:11 +08:00

5.0 KiB
Raw Blame History

File Upload FileId Design

Date: 2026-03-10

Goal

为 TMS 提供一个通用文件上传能力,前端在初始化页面选择文件后先上传,后端返回 fileId,初始化任务创建和执行阶段继续只引用 fileId,不直接传输文件流。

Scope

本设计只覆盖:

  • 通用文件上传接口
  • 文件元信息记录
  • 与 CISD 初始化的 fileId 约定
  • 前端调用时序

本设计不覆盖:

  • 文件下载
  • 文件删除回收
  • 秒传/去重
  • 文件内容级业务校验
  • 初始化主流程改造

Current State

当前初始化接口:

  • /api/v1/init/preview
  • /api/v1/init/tasks

都只接收 JSON InitPreviewRequest,不接收 multipart/form-data

当前初始化模块已经完整依赖以下 fileId 字段:

  • licenses.receiverLicenseFileId
  • licenses.cfgZipFileId
  • mq.tlqLicenseFileId
  • mq.cfmqConfigFileId

当前执行器通过 upload-base-dir/<fileId>upload-base-dir/<fileId>/<preferredFileName> 解析文件。

Design Choice

采用:通用上传接口 + 初始化继续引用 fileId

不采用:初始化任务接口直接接收 MultipartFile

原因:

  • 初始化主流程已经基于 fileId,继续沿用改动最小
  • 上传与初始化创建失败语义不同,拆分更清晰
  • 通用上传能力后续可被证书、升级、备份等模块复用

API Design

1. Upload API

POST /api/v1/files/upload

Content-Typemultipart/form-data

表单字段:

  • file: 必填,文件本体
  • bizType: 可选,默认 GENERIC

返回示例:

{
  "success": true,
  "code": 200,
  "msg": "success",
  "data": {
    "fileId": "f_20260310_ab12cd34",
    "bizType": "INIT",
    "originalFilename": "cmep.license",
    "contentType": "application/octet-stream",
    "size": 2048,
    "storagePath": "/home/tms/uploads/f_20260310_ab12cd34/cmep.license"
  },
  "timestamp": 1741600000000,
  "traceId": "xxx"
}

2. File Detail API

GET /api/v1/files/{fileId}

用于页面回显和排障,不参与初始化主链路。

Storage Design

Storage root

  • /home/tms/uploads

Directory layout

  • /home/tms/uploads/<fileId>/
  • /home/tms/uploads/<fileId>/<originalFilename>

这样做的原因:

  • 与当前执行器解析逻辑兼容
  • 保留原始文件名,便于排查
  • 每个 fileId 单独目录,避免同名覆盖

Database Design

新增表:sys_file_record

字段建议:

  • id bigint pk
  • file_id varchar(128) unique not null
  • biz_type varchar(32) not null
  • original_filename varchar(256) not null
  • stored_filename varchar(256) not null
  • storage_path varchar(512) not null
  • content_type varchar(128) null
  • file_size bigint not null
  • sha256 varchar(64) null
  • status varchar(16) not null
  • create_time datetime not null
  • update_time datetime not null

状态值当前只需要:

  • ACTIVE
  • DELETED

Backend Module Layout

新增模块:

src/main/java/com/cisd/tms/modules/file
├── controller/internal
│   └── FileInternalController.java
├── dto
│   ├── FileUploadResponse.java
│   └── FileDetailResponse.java
├── entity
│   └── FileRecordEntity.java
├── repository
│   └── FileRecordRepository.java
└── service
    └── FileService.java

资源文件:

src/main/resources/mapper/file/FileRecordMapper.xml
src/main/resources/db/migration/V3__create_sys_file_record.sql

Validation Boundary

Upload API validates

  • 文件非空
  • 原始文件名非空
  • 文件大小不超过配置上限
  • bizType 枚举合法

Init API continues to validate

  • fileId 格式
  • 按版本和 mqType 的必填规则
  • 执行阶段按 fileId 是否能找到对应文件

Frontend Flow

  1. 用户在初始化页面选择文件
  2. 前端立即调用上传接口
  3. 上传成功后拿到 fileId
  4. 前端将 fileId 保存到表单状态
  5. 用户点击“预检”或“创建任务”时,仅提交 JSON

字段映射:

  • 收发器 license -> licenses.receiverLicenseFileId
  • TLQ 配置/许可证 -> mq.tlqLicenseFileId
  • CFMQ 配置 -> mq.cfmqConfigFileId
  • 直参 cfg 包 -> licenses.cfgZipFileId

Configuration

建议新增:

tms:
  file:
    upload-base-dir: /home/tms/uploads
    max-file-size-mb: 50

后续可考虑让初始化执行器复用同一配置根,而不是再单独维护一份路径配置。

Risks

  1. 当前初始化执行器已有 tms.init.executor.upload-base-dir,如果新增 tms.file.upload-base-dir 而两者不一致,会导致上传成功但执行阶段找不到文件。
  2. 如果上传接口只保留原始文件名而不做额外清洗,需要限制危险文件名字符。
  3. 目前不做业务文件类型校验,上传成功不代表该文件一定适合初始化使用。

Recommendation

先实现最小闭环:

  • 上传接口
  • 查询接口
  • 文件元信息表
  • 与初始化 fileId 流程对齐

不在第一版加入:

  • 下载
  • 删除
  • 清理任务
  • 哈希去重
  • 扩展名白名单