datai/docs/archive/prompts/032-file-controller-api.md

15 KiB
Raw Permalink Blame History

Prompt First - REQ-011-5 文件上传下载 Controller 和 API 接口

输入引用

引用相关的 docs 文档链接:

Context Maps

强制列出本次 Prompt 依赖的 Canvas 文件:

目标

实现 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

必需导入:

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();
    }
}

约束

技术栈约束

  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 代码实现 待评估 待改进