383 lines
15 KiB
Markdown
383 lines
15 KiB
Markdown
# Prompt First - REQ-011-5 文件上传下载 Controller 和 API 接口
|
||
|
||
## 输入引用
|
||
|
||
引用相关的 docs 文档链接:
|
||
|
||
- [REQ-011.md](../requirements/REQ-011.md) - Salesforce文件上传下载功能总需求
|
||
- [REQ-011-5.md](../requirements/REQ-011-5.md) - 文件上传下载 Controller 和 API 接口需求
|
||
- [0027-file-infrastructure-enums.md](../decisions/adr/0027-file-infrastructure-enums.md) - 文件上传下载基础设施架构决策
|
||
- [0028-attachment-upload-download.md](../decisions/adr/0028-attachment-upload-download.md) - Attachment 文件上传下载架构决策
|
||
- [0029-contentdocument-upload-download.md](../decisions/adr/0029-contentdocument-upload-download.md) - ContentDocument 文件上传下载架构决策
|
||
- [0030-document-upload-download.md](../decisions/adr/0030-document-upload-download.md) - Document 文件上传下载架构决策
|
||
- [0031-file-controller-api.md](../decisions/adr/0031-file-controller-api.md) - 文件上传下载 Controller 和 API 接口架构决策
|
||
|
||
## Context Maps
|
||
|
||
强制列出本次 Prompt 依赖的 Canvas 文件:
|
||
|
||
- [Authentication.canvas](../Authentication.canvas) - 项目架构视觉化展示
|
||
- **相关节点**: [集成核心](node_integration_core) - 提供与Salesforce的各种连接方式
|
||
- **相关节点**: [SessionManager](node_session_manager_detail) - 会话管理,提供登录服务
|
||
|
||
## 目标
|
||
|
||
实现 FileController,提供文件上传下载的 RESTful API 接口,支持 Attachment、ContentDocument、Document 三种文件对象类型。用户可以通过 HTTP 请求上传和下载 Salesforce 的三种文件对象,支持指定组织配置 ID。
|
||
|
||
### 具体目标
|
||
|
||
1. 创建 FileController,使用 @RestController 和 @RequestMapping("/files") 注解
|
||
2. 实现文件上传接口 POST /files/upload,支持三种文件对象类型
|
||
3. 实现文件下载接口 GET /files/download,支持三种文件对象类型
|
||
4. 使用 JSR-303 验证注解进行参数验证
|
||
5. 使用统一的响应格式
|
||
6. 使用策略模式根据文件对象类型调用不同的 Service
|
||
7. 使用 Swagger/OpenAPI 注解生成 API 文档
|
||
|
||
## 输出格式
|
||
|
||
### 代码文件
|
||
|
||
**文件路径**: `d:\idea_demo\datai\datai-scenes\datai-scene-salesforce\datai-salesforce-integration\src\main\java\com\datai\integration\controller\FileController.java`
|
||
|
||
**代码语言**: Java 17
|
||
|
||
**代码框架**: Spring Boot 3.5.7
|
||
|
||
**依赖注解**:
|
||
- @RestController
|
||
- @RequestMapping("/files")
|
||
- @Slf4j
|
||
- @Tag(name = "文件管理", description = "文件上传下载 API")
|
||
- @RequiredArgsConstructor
|
||
- @Autowired
|
||
|
||
**必需导入**:
|
||
```java
|
||
import com.datai.integration.dto.*;
|
||
import com.datai.integration.enums.FileObjectType;
|
||
import com.datai.integration.service.*;
|
||
import io.swagger.v3.oas.annotations.*;
|
||
import io.swagger.v3.oas.annotations.parameters.*;
|
||
import io.swagger.v3.oas.annotations.responses.*;
|
||
import io.swagger.v3.oas.annotations.media.*;
|
||
import lombok.RequiredArgsConstructor;
|
||
import lombok.extern.slf4j.Slf4j;
|
||
import org.springframework.http.*;
|
||
import org.springframework.validation.annotation.Validated;
|
||
import org.springframework.web.bind.annotation.*;
|
||
import org.springframework.web.multipart.MultipartFile;
|
||
import jakarta.validation.Valid;
|
||
import jakarta.validation.constraints.*;
|
||
```
|
||
|
||
### API 接口定义
|
||
|
||
#### 1. 文件上传接口
|
||
|
||
**接口路径**: POST /files/upload
|
||
|
||
**请求参数**:
|
||
```java
|
||
@Parameter(description = "文件对象类型", required = true, example = "ATTACHMENT")
|
||
@RequestParam("fileObjectType") FileObjectType fileObjectType,
|
||
|
||
@Parameter(description = "组织配置 ID", required = true, example = "org001")
|
||
@RequestParam("orgConfigId") @NotBlank String orgConfigId,
|
||
|
||
@Parameter(description = "关联记录 ID", required = false, example = "001xx000003DGp2AAG")
|
||
@RequestParam(value = "relatedRecordId", required = false) String relatedRecordId,
|
||
|
||
@Parameter(description = "文件", required = true)
|
||
@RequestPart("file") MultipartFile file,
|
||
|
||
@Parameter(description = "文件描述", required = false)
|
||
@RequestParam(value = "description", required = false) String description
|
||
```
|
||
|
||
**响应格式**:
|
||
```java
|
||
@ApiResponse(responseCode = "200", description = "上传成功",
|
||
content = @Content(schema = @Schema(implementation = ApiResponse.class)))
|
||
@ApiResponse(responseCode = "400", description = "请求参数错误",
|
||
content = @Content(schema = @Schema(implementation = ApiResponse.class)))
|
||
@ApiResponse(responseCode = "500", description = "服务器内部错误",
|
||
content = @Content(schema = @Schema(implementation = ApiResponse.class)))
|
||
```
|
||
|
||
**实现逻辑**:
|
||
```java
|
||
@PostMapping("/upload")
|
||
public ResponseEntity<ApiResponse<FileUploadResponse>> uploadFile(
|
||
@RequestParam("fileObjectType") FileObjectType fileObjectType,
|
||
@RequestParam("orgConfigId") @NotBlank String orgConfigId,
|
||
@RequestParam(value = "relatedRecordId", required = false) String relatedRecordId,
|
||
@RequestPart("file") MultipartFile file,
|
||
@RequestParam(value = "description", required = false) String description) {
|
||
|
||
log.info("收到文件上传请求: fileObjectType={}, orgConfigId={}, fileName={}",
|
||
fileObjectType, orgConfigId, file.getOriginalFilename());
|
||
|
||
FileUploadRequest request = FileUploadRequest.builder()
|
||
.fileObjectType(fileObjectType)
|
||
.orgConfigId(orgConfigId)
|
||
.relatedRecordId(relatedRecordId)
|
||
.file(file)
|
||
.description(description)
|
||
.build();
|
||
|
||
FileUploadResponse response = switch (fileObjectType) {
|
||
case ATTACHMENT -> attachmentFileService.upload(request);
|
||
case CONTENT_DOCUMENT -> contentVersionFileService.upload(request);
|
||
case DOCUMENT -> documentFileService.upload(request);
|
||
};
|
||
|
||
log.info("文件上传成功: fileId={}, fileName={}", response.getFileId(), response.getFileName());
|
||
|
||
return ResponseEntity.ok(ApiResponse.success(response));
|
||
}
|
||
```
|
||
|
||
#### 2. 文件下载接口
|
||
|
||
**接口路径**: GET /files/download
|
||
|
||
**请求参数**:
|
||
```java
|
||
@Parameter(description = "文件对象类型", required = true, example = "ATTACHMENT")
|
||
@RequestParam("fileObjectType") FileObjectType fileObjectType,
|
||
|
||
@Parameter(description = "组织配置 ID", required = true, example = "org001")
|
||
@RequestParam("orgConfigId") @NotBlank String orgConfigId,
|
||
|
||
@Parameter(description = "文件 ID", required = true, example = "00Pxx0000016u0HEAQ")
|
||
@RequestParam("fileId") @NotBlank String fileId
|
||
```
|
||
|
||
**响应格式**:
|
||
```java
|
||
@ApiResponse(responseCode = "200", description = "下载成功",
|
||
content = @Content(schema = @Schema(implementation = ApiResponse.class)))
|
||
@ApiResponse(responseCode = "400", description = "请求参数错误",
|
||
content = @Content(schema = @Schema(implementation = ApiResponse.class)))
|
||
@ApiResponse(responseCode = "404", description = "文件不存在",
|
||
content = @Content(schema = @Schema(implementation = ApiResponse.class)))
|
||
@ApiResponse(responseCode = "500", description = "服务器内部错误",
|
||
content = @Content(schema = @Schema(implementation = ApiResponse.class)))
|
||
```
|
||
|
||
**实现逻辑**:
|
||
```java
|
||
@GetMapping("/download")
|
||
public ResponseEntity<ApiResponse<FileDownloadResponse>> downloadFile(
|
||
@RequestParam("fileObjectType") FileObjectType fileObjectType,
|
||
@RequestParam("orgConfigId") @NotBlank String orgConfigId,
|
||
@RequestParam("fileId") @NotBlank String fileId) {
|
||
|
||
log.info("收到文件下载请求: fileObjectType={}, orgConfigId={}, fileId={}",
|
||
fileObjectType, orgConfigId, fileId);
|
||
|
||
FileDownloadRequest request = FileDownloadRequest.builder()
|
||
.fileObjectType(fileObjectType)
|
||
.orgConfigId(orgConfigId)
|
||
.fileId(fileId)
|
||
.build();
|
||
|
||
FileDownloadResponse response = switch (fileObjectType) {
|
||
case ATTACHMENT -> attachmentFileService.download(request);
|
||
case CONTENT_DOCUMENT -> contentVersionFileService.download(request);
|
||
case DOCUMENT -> documentFileService.download(request);
|
||
};
|
||
|
||
log.info("文件下载成功: fileId={}, fileName={}", response.getFileId(), response.getFileName());
|
||
|
||
return ResponseEntity.ok(ApiResponse.success(response));
|
||
}
|
||
```
|
||
|
||
### 统一响应格式
|
||
|
||
**响应类定义**:
|
||
```java
|
||
@Data
|
||
@Builder
|
||
@AllArgsConstructor
|
||
@NoArgsConstructor
|
||
public class ApiResponse<T> {
|
||
private Integer code;
|
||
private String message;
|
||
private T data;
|
||
|
||
public static <T> ApiResponse<T> success(T data) {
|
||
return ApiResponse.<T>builder()
|
||
.code(200)
|
||
.message("success")
|
||
.data(data)
|
||
.build();
|
||
}
|
||
|
||
public static <T> ApiResponse<T> error(Integer code, String message) {
|
||
return ApiResponse.<T>builder()
|
||
.code(code)
|
||
.message(message)
|
||
.data(null)
|
||
.build();
|
||
}
|
||
}
|
||
```
|
||
|
||
## 约束
|
||
|
||
### 技术栈约束
|
||
|
||
1. **Java 版本**: 必须使用 Java 17
|
||
2. **Spring Boot 版本**: 必须使用 Spring Boot 3.5.7
|
||
3. **Lombok**: 必须使用 Lombok 注解简化代码
|
||
4. **Swagger**: 必须使用 Swagger/OpenAPI 3.0 注解生成 API 文档
|
||
5. **JSR-303**: 必须使用 JSR-303 验证注解进行参数验证
|
||
|
||
### 架构约束
|
||
|
||
1. **模块约束**: 必须在 datai-salesforce-integration 模块下实现
|
||
2. **包路径**: 必须在 com.datai.integration.controller 包下
|
||
3. **依赖注入**: 必须使用 @Autowired 或构造函数注入
|
||
4. **策略模式**: 必须使用策略模式根据文件对象类型调用不同的 Service
|
||
5. **统一响应**: 必须使用统一的响应格式
|
||
|
||
### 认证约束
|
||
|
||
1. **会话管理**: 必须使用 SessionManager 进行会话管理
|
||
2. **自动重新登录**: 必须支持自动重新登录机制
|
||
3. **组织配置**: 必须支持指定组织配置 ID
|
||
|
||
### API 设计约束
|
||
|
||
1. **RESTful 规范**: 必须遵循 RESTful 设计规范
|
||
2. **HTTP 方法**: 必须使用正确的 HTTP 方法(POST 用于上传,GET 用于下载)
|
||
3. **参数传递**: 必须使用 @RequestParam 和 @RequestPart 注解接收参数
|
||
4. **响应状态码**: 必须返回正确的 HTTP 状态码
|
||
|
||
### 文档约束
|
||
|
||
1. **Swagger 注解**: 必须使用 Swagger/OpenAPI 注解生成 API 文档
|
||
2. **参数描述**: 必须为每个参数提供详细的描述
|
||
3. **响应描述**: 必须为每个响应提供详细的描述
|
||
4. **示例数据**: 必须为每个参数和响应提供示例数据
|
||
|
||
### 代码质量约束
|
||
|
||
1. **代码规范**: 必须遵循项目编码规范
|
||
2. **日志记录**: 必须使用 @Slf4j 注解记录日志
|
||
3. **异常处理**: 必须正确处理异常,提供详细的错误信息
|
||
4. **单元测试**: 必须编写单元测试,确保测试覆盖率 ≥ 90%
|
||
|
||
## Rule Set
|
||
|
||
"请严格参考 @Authentication.canvas 中的状态机转移逻辑,不要自行发挥。"
|
||
|
||
**具体规则**:
|
||
- 必须使用 Canvas 中定义的类名和方法名
|
||
- 必须遵循 Canvas 中定义的调用关系
|
||
- 必须参考 Canvas 中的流程图逻辑
|
||
|
||
**代码规范规则**:
|
||
- 必须使用 Lombok 注解(@Data、@Builder、@Slf4j、@RequiredArgsConstructor 等)
|
||
- 必须使用 JSR-303 验证注解(@NotNull、@NotBlank、@Size 等)
|
||
- 必须使用 Swagger/OpenAPI 注解(@Tag、@Operation、@Parameter、@ApiResponse 等)
|
||
- 必须使用策略模式(switch-case 语句)根据文件对象类型调用不同的 Service
|
||
- 必须使用统一的响应格式(ApiResponse)
|
||
- 必须使用构造函数注入(@RequiredArgsConstructor)
|
||
- 必须使用 @Valid 注解进行参数验证
|
||
|
||
**API 设计规则**:
|
||
- 必须遵循 RESTful 设计规范
|
||
- 必须使用正确的 HTTP 方法(POST 用于上传,GET 用于下载)
|
||
- 必须使用 @RequestParam 和 @RequestPart 注解接收参数
|
||
- 必须返回正确的 HTTP 状态码
|
||
- 必须提供详细的参数描述和示例数据
|
||
- 必须提供详细的响应描述和示例数据
|
||
|
||
**异常处理规则**:
|
||
- 必须正确处理异常,提供详细的错误信息
|
||
- 必须使用 try-catch 块捕获异常
|
||
- 必须记录详细的日志,便于问题排查
|
||
- 必须返回统一的错误响应格式
|
||
|
||
**日志记录规则**:
|
||
- 必须使用 @Slf4j 注解记录日志
|
||
- 必须记录关键操作的开始和结束
|
||
- 必须记录异常信息
|
||
- 必须记录请求参数和响应结果
|
||
|
||
## 验收标准
|
||
|
||
### 功能完整性验收标准
|
||
|
||
1. **上传功能**: 能够成功上传本地文件到 Salesforce Attachment、ContentDocument、Document 对象
|
||
2. **下载功能**: 能够成功从 Salesforce Attachment、ContentDocument、Document 对象下载文件到本地
|
||
3. **参数验证**: 能够正确验证参数,提供详细的错误信息
|
||
4. **异常处理**: 能够正确处理异常,提供详细的错误信息
|
||
5. **日志记录**: 能够记录详细的日志,便于问题排查
|
||
6. **API 文档**: 能够生成清晰的 API 文档,便于前端调用和集成
|
||
|
||
### 代码质量验收标准
|
||
|
||
1. **代码规范**: 代码符合项目编码规范,有清晰的注释
|
||
2. **设计模式**: 使用策略模式封装三种文件对象的上传下载逻辑
|
||
3. **单一职责**: 遵循单一职责原则和开闭原则
|
||
4. **测试覆盖**: 测试覆盖率 ≥ 90%
|
||
|
||
### API 设计验收标准
|
||
|
||
1. **RESTful 规范**: 遵循 RESTful 设计规范
|
||
2. **HTTP 方法**: 使用正确的 HTTP 方法(POST 用于上传,GET 用于下载)
|
||
3. **参数传递**: 使用 @RequestParam 和 @RequestPart 注解接收参数
|
||
4. **响应状态码**: 返回正确的 HTTP 状态码
|
||
5. **统一响应**: 使用统一的响应格式
|
||
|
||
### 文档质量验收标准
|
||
|
||
1. **Swagger 注解**: 使用 Swagger/OpenAPI 注解生成 API 文档
|
||
2. **参数描述**: 为每个参数提供详细的描述
|
||
3. **响应描述**: 为每个响应提供详细的描述
|
||
4. **示例数据**: 为每个参数和响应提供示例数据
|
||
|
||
### 性能验收标准
|
||
|
||
1. **上传性能**: 文件上传性能良好,不影响系统响应
|
||
2. **下载性能**: 文件下载性能良好,不影响系统响应
|
||
3. **并发性能**: 并发上传下载性能良好,不出现资源竞争
|
||
|
||
## 风险
|
||
|
||
### 输出质量风险
|
||
|
||
1. **API 设计风险**: API 接口设计可能不够完善
|
||
- **缓解措施**: 提前进行 API 设计评审,参考业界最佳实践
|
||
2. **参数验证风险**: 参数验证可能不够严格
|
||
- **缓解措施**: 使用 JSR-303 验证注解,提供详细的错误信息
|
||
3. **响应封装风险**: 响应封装可能不够统一
|
||
- **缓解措施**: 定义统一的响应格式,使用统一的响应封装逻辑
|
||
|
||
### 技术实现风险
|
||
|
||
1. **并发风险**: 并发上传下载可能导致资源竞争
|
||
- **缓解措施**: 使用线程池,限制并发数,监控并发性能
|
||
2. **性能风险**: API 接口性能可能不达标
|
||
- **缓解措施**: 提前进行性能测试,优化代码性能
|
||
3. **兼容性风险**: 前端可能不兼容新的 API 接口
|
||
- **缓解措施**: 提供详细的 API 文档,提供前端集成指南
|
||
|
||
### 时间成本风险
|
||
|
||
1. **开发时间风险**: 开发时间可能超出预期
|
||
- **缓解措施**: 提前进行技术调研,参考官方文档和示例代码
|
||
2. **测试时间风险**: 测试时间可能超出预期
|
||
- **缓解措施**: 提前进行性能测试,优化代码性能
|
||
|
||
## 使用记录
|
||
|
||
| 日期 | 使用场景 | 输入参数 | 输出结果 | 反馈 | 改进措施 |
|
||
|------|---------|---------|---------|------|----------|
|
||
| 2026-01-19 | REQ-011-5 阶段 4 执行 | REQ-011-5 需求文档、架构决策文档、提示词文档 | FileController 代码实现 | 待评估 | 待改进 |
|