# 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** ```bash 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 `//` - 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** ```bash 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** ```bash 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** ```bash 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** ```bash 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.