15 KiB
15 KiB
Prompt First - REQ-011-5 文件上传下载 Controller 和 API 接口
输入引用
引用相关的 docs 文档链接:
- REQ-011.md - Salesforce文件上传下载功能总需求
- REQ-011-5.md - 文件上传下载 Controller 和 API 接口需求
- 0027-file-infrastructure-enums.md - 文件上传下载基础设施架构决策
- 0028-attachment-upload-download.md - Attachment 文件上传下载架构决策
- 0029-contentdocument-upload-download.md - ContentDocument 文件上传下载架构决策
- 0030-document-upload-download.md - Document 文件上传下载架构决策
- 0031-file-controller-api.md - 文件上传下载 Controller 和 API 接口架构决策
Context Maps
强制列出本次 Prompt 依赖的 Canvas 文件:
- Authentication.canvas - 项目架构视觉化展示
- 相关节点: 集成核心 - 提供与Salesforce的各种连接方式
- 相关节点: SessionManager - 会话管理,提供登录服务
目标
实现 FileController,提供文件上传下载的 RESTful API 接口,支持 Attachment、ContentDocument、Document 三种文件对象类型。用户可以通过 HTTP 请求上传和下载 Salesforce 的三种文件对象,支持指定组织配置 ID。
具体目标
- 创建 FileController,使用 @RestController 和 @RequestMapping("/files") 注解
- 实现文件上传接口 POST /files/upload,支持三种文件对象类型
- 实现文件下载接口 GET /files/download,支持三种文件对象类型
- 使用 JSR-303 验证注解进行参数验证
- 使用统一的响应格式
- 使用策略模式根据文件对象类型调用不同的 Service
- 使用 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
必需导入:
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
请求参数:
@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
响应格式:
@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)))
实现逻辑:
@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
请求参数:
@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
响应格式:
@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)))
实现逻辑:
@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));
}
统一响应格式
响应类定义:
@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();
}
}
约束
技术栈约束
- Java 版本: 必须使用 Java 17
- Spring Boot 版本: 必须使用 Spring Boot 3.5.7
- Lombok: 必须使用 Lombok 注解简化代码
- Swagger: 必须使用 Swagger/OpenAPI 3.0 注解生成 API 文档
- JSR-303: 必须使用 JSR-303 验证注解进行参数验证
架构约束
- 模块约束: 必须在 datai-salesforce-integration 模块下实现
- 包路径: 必须在 com.datai.integration.controller 包下
- 依赖注入: 必须使用 @Autowired 或构造函数注入
- 策略模式: 必须使用策略模式根据文件对象类型调用不同的 Service
- 统一响应: 必须使用统一的响应格式
认证约束
- 会话管理: 必须使用 SessionManager 进行会话管理
- 自动重新登录: 必须支持自动重新登录机制
- 组织配置: 必须支持指定组织配置 ID
API 设计约束
- RESTful 规范: 必须遵循 RESTful 设计规范
- HTTP 方法: 必须使用正确的 HTTP 方法(POST 用于上传,GET 用于下载)
- 参数传递: 必须使用 @RequestParam 和 @RequestPart 注解接收参数
- 响应状态码: 必须返回正确的 HTTP 状态码
文档约束
- Swagger 注解: 必须使用 Swagger/OpenAPI 注解生成 API 文档
- 参数描述: 必须为每个参数提供详细的描述
- 响应描述: 必须为每个响应提供详细的描述
- 示例数据: 必须为每个参数和响应提供示例数据
代码质量约束
- 代码规范: 必须遵循项目编码规范
- 日志记录: 必须使用 @Slf4j 注解记录日志
- 异常处理: 必须正确处理异常,提供详细的错误信息
- 单元测试: 必须编写单元测试,确保测试覆盖率 ≥ 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 注解记录日志
- 必须记录关键操作的开始和结束
- 必须记录异常信息
- 必须记录请求参数和响应结果
验收标准
功能完整性验收标准
- 上传功能: 能够成功上传本地文件到 Salesforce Attachment、ContentDocument、Document 对象
- 下载功能: 能够成功从 Salesforce Attachment、ContentDocument、Document 对象下载文件到本地
- 参数验证: 能够正确验证参数,提供详细的错误信息
- 异常处理: 能够正确处理异常,提供详细的错误信息
- 日志记录: 能够记录详细的日志,便于问题排查
- API 文档: 能够生成清晰的 API 文档,便于前端调用和集成
代码质量验收标准
- 代码规范: 代码符合项目编码规范,有清晰的注释
- 设计模式: 使用策略模式封装三种文件对象的上传下载逻辑
- 单一职责: 遵循单一职责原则和开闭原则
- 测试覆盖: 测试覆盖率 ≥ 90%
API 设计验收标准
- RESTful 规范: 遵循 RESTful 设计规范
- HTTP 方法: 使用正确的 HTTP 方法(POST 用于上传,GET 用于下载)
- 参数传递: 使用 @RequestParam 和 @RequestPart 注解接收参数
- 响应状态码: 返回正确的 HTTP 状态码
- 统一响应: 使用统一的响应格式
文档质量验收标准
- Swagger 注解: 使用 Swagger/OpenAPI 注解生成 API 文档
- 参数描述: 为每个参数提供详细的描述
- 响应描述: 为每个响应提供详细的描述
- 示例数据: 为每个参数和响应提供示例数据
性能验收标准
- 上传性能: 文件上传性能良好,不影响系统响应
- 下载性能: 文件下载性能良好,不影响系统响应
- 并发性能: 并发上传下载性能良好,不出现资源竞争
风险
输出质量风险
- API 设计风险: API 接口设计可能不够完善
- 缓解措施: 提前进行 API 设计评审,参考业界最佳实践
- 参数验证风险: 参数验证可能不够严格
- 缓解措施: 使用 JSR-303 验证注解,提供详细的错误信息
- 响应封装风险: 响应封装可能不够统一
- 缓解措施: 定义统一的响应格式,使用统一的响应封装逻辑
技术实现风险
- 并发风险: 并发上传下载可能导致资源竞争
- 缓解措施: 使用线程池,限制并发数,监控并发性能
- 性能风险: API 接口性能可能不达标
- 缓解措施: 提前进行性能测试,优化代码性能
- 兼容性风险: 前端可能不兼容新的 API 接口
- 缓解措施: 提供详细的 API 文档,提供前端集成指南
时间成本风险
- 开发时间风险: 开发时间可能超出预期
- 缓解措施: 提前进行技术调研,参考官方文档和示例代码
- 测试时间风险: 测试时间可能超出预期
- 缓解措施: 提前进行性能测试,优化代码性能
使用记录
| 日期 | 使用场景 | 输入参数 | 输出结果 | 反馈 | 改进措施 |
|---|---|---|---|---|---|
| 2026-01-19 | REQ-011-5 阶段 4 执行 | REQ-011-5 需求文档、架构决策文档、提示词文档 | FileController 代码实现 | 待评估 | 待改进 |