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

9.7 KiB

File Upload FileId Implementation Plan

For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

Goal: Add a reusable file upload module that stores uploaded files under /home/tms/uploads, returns fileId, and keeps CISD initialization flowing through existing fileId fields.

Architecture: Introduce a small modules/file module with internal upload and query APIs, one metadata table, and a service that owns fileId generation plus filesystem persistence. Keep the init module unchanged at the API boundary except for sharing the same upload storage root and documenting the front-end workflow.

Tech Stack: Spring Boot MVC multipart upload, MyBatis-Plus repository pattern, Flyway SQL migration, JUnit 5


Task 1: Add file upload persistence contract

Files:

  • Create: /Users/waner/Work/CISD/文档/tms-framework/src/main/resources/db/migration/V3__create_sys_file_record.sql
  • Create: /Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/file/entity/FileRecordEntity.java
  • Create: /Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/file/repository/FileRecordRepository.java
  • Create: /Users/waner/Work/CISD/文档/tms-framework/src/main/resources/mapper/file/FileRecordMapper.xml
  • Test: /Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/modules/file/FileServiceTest.java

Step 1: Write the failing test

Add a test that expects file metadata to be persisted with:

  • generated fileId
  • original file name
  • absolute storage path
  • ACTIVE status

Step 2: Run test to verify it fails

Run: mvn -q -Dtest=FileServiceTest#shouldPersistUploadedFileMetadata test Expected: FAIL because file module classes and table mapping do not exist.

Step 3: Write minimal implementation

Implement the entity, mapper/repository, and migration with only the fields needed by upload/query.

Step 4: Run test to verify it passes

Run: mvn -q -Dtest=FileServiceTest#shouldPersistUploadedFileMetadata test Expected: PASS

Step 5: Commit

git add src/main/resources/db/migration/V3__create_sys_file_record.sql \
  src/main/java/com/cisd/tms/modules/file/entity/FileRecordEntity.java \
  src/main/java/com/cisd/tms/modules/file/repository/FileRecordRepository.java \
  src/main/resources/mapper/file/FileRecordMapper.xml \
  src/test/java/com/cisd/tms/modules/file/FileServiceTest.java
git commit -m "feat: add file upload persistence contract"

Task 2: Implement upload service

Files:

  • Create: /Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/file/service/FileService.java
  • Create: /Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/file/dto/FileUploadResponse.java
  • Create: /Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/file/dto/FileDetailResponse.java
  • Create: /Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/common/config/properties/FileStorageProperties.java
  • Modify: /Users/waner/Work/CISD/文档/tms-framework/src/main/resources/application.yml
  • Modify: /Users/waner/Work/CISD/文档/tms-framework/config/application.yml.example
  • Test: /Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/modules/file/FileServiceTest.java

Step 1: Write the failing test

Add tests that assert:

  • uploaded file is saved under <upload-base-dir>/<fileId>/<originalFilename>
  • blank file upload is rejected
  • oversize upload is rejected
  • returned DTO contains fileId, size, storagePath

Step 2: Run test to verify it fails

Run: mvn -q -Dtest=FileServiceTest test Expected: FAIL because service and properties do not exist.

Step 3: Write minimal implementation

Implement:

  • fileId generation
  • file name sanitization
  • directory creation
  • file save
  • metadata insert
  • detail query by fileId

Step 4: Run test to verify it passes

Run: mvn -q -Dtest=FileServiceTest test Expected: PASS

Step 5: Commit

git add src/main/java/com/cisd/tms/modules/file/service/FileService.java \
  src/main/java/com/cisd/tms/modules/file/dto/FileUploadResponse.java \
  src/main/java/com/cisd/tms/modules/file/dto/FileDetailResponse.java \
  src/main/java/com/cisd/tms/common/config/properties/FileStorageProperties.java \
  src/main/resources/application.yml config/application.yml.example \
  src/test/java/com/cisd/tms/modules/file/FileServiceTest.java
git commit -m "feat: implement file upload service"

Task 3: Expose internal file APIs

Files:

  • Create: /Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/file/controller/internal/FileInternalController.java
  • Modify: /Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/modules/file/FileServiceTest.java
  • Create: /Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/modules/file/FileInternalControllerTest.java

Step 1: Write the failing test

Add controller tests for:

  • POST /api/v1/files/upload
  • GET /api/v1/files/{fileId}

Verify upload uses multipart, returns ApiResponse, and query returns stored metadata.

Step 2: Run test to verify it fails

Run: mvn -q -Dtest=FileInternalControllerTest test Expected: FAIL because controller does not exist.

Step 3: Write minimal implementation

Expose two endpoints only:

  • upload
  • detail query

Keep them internal-only under /api/v1/files.

Step 4: Run test to verify it passes

Run: mvn -q -Dtest=FileInternalControllerTest test Expected: PASS

Step 5: Commit

git add src/main/java/com/cisd/tms/modules/file/controller/internal/FileInternalController.java \
  src/test/java/com/cisd/tms/modules/file/FileInternalControllerTest.java \
  src/test/java/com/cisd/tms/modules/file/FileServiceTest.java
git commit -m "feat: expose internal file upload api"

Task 4: Align init module with shared upload root and docs

Files:

  • Modify: /Users/waner/Work/CISD/文档/tms-framework/src/main/java/com/cisd/tms/modules/init/config/InitExecutorProperties.java
  • Modify: /Users/waner/Work/CISD/文档/tms-framework/src/main/resources/application.yml
  • Modify: /Users/waner/Work/CISD/文档/tms-framework/config/application.yml.example
  • Modify: /Users/waner/Work/CISD/文档/tms-framework/docs/openapi/cisd-init.openapi.yaml
  • Modify: /Users/waner/Work/CISD/文档/tms-framework/docs/openapi/cisd-init.openapi.json
  • Modify: /Users/waner/Work/CISD/文档/tms-framework/README.md
  • Test: /Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/modules/init/executor/ConfigurableInitStepExecutorTest.java

Step 1: Write the failing test

Add or update tests to assert:

  • init executor upload root matches the shared upload root config
  • docs clearly state init APIs accept fileId, not multipart files
  • README documents front-end flow: upload first, then create init task

Step 2: Run test to verify it fails

Run: mvn -q -Dtest=ConfigurableInitStepExecutorTest#shouldDocumentInitFileIdFlow test Expected: FAIL because docs/config are not aligned yet.

Step 3: Write minimal implementation

Align configuration and docs without changing init request DTO semantics.

Step 4: Run test to verify it passes

Run: mvn -q -Dtest=ConfigurableInitStepExecutorTest#shouldDocumentInitFileIdFlow test Expected: PASS

Step 5: Commit

git add src/main/java/com/cisd/tms/modules/init/config/InitExecutorProperties.java \
  src/main/resources/application.yml config/application.yml.example \
  docs/openapi/cisd-init.openapi.yaml docs/openapi/cisd-init.openapi.json README.md \
  src/test/java/com/cisd/tms/modules/init/executor/ConfigurableInitStepExecutorTest.java
git commit -m "docs: align init fileId flow with upload api"

Task 5: Add upload API OpenAPI spec

Files:

  • Modify: /Users/waner/Work/CISD/文档/tms-framework/docs/openapi/cisd-init.openapi.yaml
  • Modify: /Users/waner/Work/CISD/文档/tms-framework/docs/openapi/cisd-init.openapi.json
  • Test: /Users/waner/Work/CISD/文档/tms-framework/src/test/java/com/cisd/tms/modules/file/FileInternalControllerTest.java

Step 1: Write the failing test

Add a documentation test or assertion that the upload route and response fields are present in the OpenAPI output.

Step 2: Run test to verify it fails

Run: mvn -q -Dtest=FileInternalControllerTest#shouldDocumentUploadApi test Expected: FAIL because the route is not documented yet.

Step 3: Write minimal implementation

Document:

  • upload API
  • query API
  • multipart request
  • fileId response
  • init API references to upload-first workflow

Step 4: Run test to verify it passes

Run: mvn -q -Dtest=FileInternalControllerTest#shouldDocumentUploadApi test Expected: PASS

Step 5: Commit

git add docs/openapi/cisd-init.openapi.yaml docs/openapi/cisd-init.openapi.json \
  src/test/java/com/cisd/tms/modules/file/FileInternalControllerTest.java
git commit -m "docs: add file upload api spec"

Task 6: Final verification

Files:

  • Verify only

Step 1: Run file module tests

Run: mvn -q -Dtest=FileServiceTest,FileInternalControllerTest test Expected: PASS

Step 2: Run init-related tests

Run: mvn -q -Dtest=InitServiceTest,ConfigurableInitStepExecutorTest test Expected: PASS

Step 3: Run compile

Run: mvn -q -DskipTests compile Expected: PASS

Step 4: Review focused diff

Run: git diff -- src/main/java/com/cisd/tms/modules/file src/main/resources/db/migration/V3__create_sys_file_record.sql src/main/resources/mapper/file src/main/resources/application.yml config/application.yml.example README.md docs/openapi/cisd-init.openapi.yaml docs/openapi/cisd-init.openapi.json Expected: only file upload module, config, README, and OpenAPI/doc alignment changes.