252 lines
9.7 KiB
Markdown
252 lines
9.7 KiB
Markdown
# 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 `<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**
|
|
|
|
```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.
|