# Prompt - Quick Deploy功能实现 ## 输入引用 引用相关的 docs 文档链接: - [REQ-010-9.md](../requirements/REQ-010-9.md) - Quick Deploy功能实现需求文档 - [0018-quick-deploy.md](../decisions/adr/0018-quick-deploy.md) - Quick Deploy功能实现架构决策 - [REQ-010-8.md](../requirements/REQ-010-8.md) - 元数据部署核心功能需求文档 - [0017-metadata-deploy-core.md](../decisions/adr/0017-metadata-deploy-core.md) - 元数据部署核心功能架构决策 - [metadata-module.md](../reference-code/com/docs/metadata-module.md) - Salesforce Metadata API 模块说明 ## Context Maps 强制列出本次 Prompt 依赖的 Canvas 文件: - [Authentication.canvas](../Authentication.canvas) - 项目架构视觉化展示 - **相关节点**: [集成核心](node_integration_core) - 提供与Salesforce的各种连接方式 - **相关节点**: [IMetadataApiService](node_metadata_api_service) - 元数据API服务接口 - **相关节点**: [MetadataApiClient](node_metadata_api_client) - Metadata API客户端 ## 目标 实现 Quick Deploy 功能,包括 Quick Deploy 触发、Quick Deploy 历史记录、Quick Deploy 状态监控、Quick Deploy 结果解析。基于最近一次成功的部署记录快速重新部署,无需重新运行测试,显著提高部署速度。 ## 输出格式 ### 代码结构 ``` datai-salesforce-metadata/ ├── deploy/ │ ├── controller/ │ │ ├── MetadataDeployController.java │ │ └── QuickDeployController.java │ ├── service/ │ │ ├── IMetadataDeployService.java │ │ ├── MetadataDeployServiceImpl.java │ │ ├── IQuickDeployService.java │ │ └── QuickDeployServiceImpl.java │ ├── enums/ │ │ └── DeployStatus.java │ ├── dto/ │ │ ├── DeployTriggerRequest.java │ │ ├── DeployTriggerResponse.java │ │ ├── DeployProgressResponse.java │ │ ├── DeployHistoryResponse.java │ │ ├── DeployResult.java │ │ ├── QuickDeployTriggerRequest.java │ │ ├── QuickDeployTriggerResponse.java │ │ ├── QuickDeployProgressResponse.java │ │ ├── QuickDeployHistoryResponse.java │ │ └── QuickDeployResult.java │ ├── config/ │ │ └── AsyncConfig.java │ └── exception/ │ └── DeployException.java ``` ### 代码示例(语言:Java) #### 1. QuickDeployController 控制器 ```java package com.datai.salesforce.metadata.deploy.controller; import com.datai.salesforce.metadata.deploy.dto.QuickDeployTriggerRequest; import com.datai.salesforce.metadata.deploy.dto.QuickDeployTriggerResponse; import com.datai.salesforce.metadata.deploy.dto.QuickDeployProgressResponse; import com.datai.salesforce.metadata.deploy.dto.QuickDeployHistoryResponse; import com.datai.salesforce.metadata.deploy.service.IQuickDeployService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/metadata/quick-deploy") public class QuickDeployController { @Autowired private IQuickDeployService quickDeployService; @PostMapping("/trigger") public QuickDeployTriggerResponse triggerQuickDeploy(@RequestBody QuickDeployTriggerRequest request) { return quickDeployService.triggerQuickDeploy(request); } @GetMapping("/progress/{jobId}") public QuickDeployProgressResponse getQuickDeployProgress(@PathVariable String jobId) { return quickDeployService.getQuickDeployProgress(jobId); } @PostMapping("/cancel/{jobId}") public void cancelQuickDeploy(@PathVariable String jobId) { quickDeployService.cancelQuickDeploy(jobId); } @GetMapping("/history") public QuickDeployHistoryResponse getQuickDeployHistory( @RequestParam(required = false) Long orgConfigId, @RequestParam(required = false) String status, @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "10") int size) { return quickDeployService.getQuickDeployHistory(orgConfigId, status, page, size); } } ``` #### 2. IQuickDeployService 服务接口 ```java package com.datai.salesforce.metadata.deploy.service; import com.datai.salesforce.metadata.deploy.dto.QuickDeployTriggerRequest; import com.datai.salesforce.metadata.deploy.dto.QuickDeployTriggerResponse; import com.datai.salesforce.metadata.deploy.dto.QuickDeployProgressResponse; import com.datai.salesforce.metadata.deploy.dto.QuickDeployHistoryResponse; public interface IQuickDeployService { QuickDeployTriggerResponse triggerQuickDeploy(QuickDeployTriggerRequest request); QuickDeployProgressResponse getQuickDeployProgress(String jobId); void cancelQuickDeploy(String jobId); QuickDeployHistoryResponse getQuickDeployHistory(Long orgConfigId, String status, int page, int size); } ``` #### 3. QuickDeployServiceImpl 服务实现 ```java package com.datai.salesforce.metadata.deploy.service; import com.datai.salesforce.metadata.deploy.dto.QuickDeployTriggerRequest; import com.datai.salesforce.metadata.deploy.dto.QuickDeployTriggerResponse; import com.datai.salesforce.metadata.deploy.dto.QuickDeployProgressResponse; import com.datai.salesforce.metadata.deploy.dto.QuickDeployHistoryResponse; import com.datai.salesforce.metadata.deploy.enums.DeployStatus; import com.datai.salesforce.metadata.deploy.exception.DeployException; import com.datai.salesforce.metadata.entity.DataiMetaJobExecution; import com.datai.salesforce.metadata.mapper.DataiMetaJobExecutionMapper; import com.datai.salesforce.metadata.service.IMetadataApiService; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; import java.util.Date; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Future; @Service public class QuickDeployServiceImpl implements IQuickDeployService { @Autowired private IMetadataApiService metadataApiService; @Autowired private DataiMetaJobExecutionMapper jobExecutionMapper; private final Map> quickDeployFutureMap = new ConcurrentHashMap<>(); private final Map progressMap = new ConcurrentHashMap<>(); @Override @Transactional public QuickDeployTriggerResponse triggerQuickDeploy(QuickDeployTriggerRequest request) { QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("org_config_id", request.getOrgConfigId()); queryWrapper.eq("status", DeployStatus.SUCCESS.getCode()); queryWrapper.orderByDesc("create_time"); queryWrapper.last("LIMIT 1"); DataiMetaJobExecution lastSuccessJob = jobExecutionMapper.selectOne(queryWrapper); if (lastSuccessJob == null) { throw new DeployException("No successful deployment record found"); } LocalDateTime deployTime = lastSuccessJob.getCreateTime(); LocalDateTime thirtyDaysAgo = LocalDateTime.now().minusDays(30); if (deployTime.isBefore(thirtyDaysAgo)) { throw new DeployException("Quick Deploy is only available for deployments within the last 30 days"); } DataiMetaJobExecution jobExecution = new DataiMetaJobExecution(); jobExecution.setTaskId(lastSuccessJob.getTaskId()); jobExecution.setOrgConfigId(request.getOrgConfigId()); jobExecution.setJobType("QUICK_DEPLOY"); jobExecution.setStatus(DeployStatus.PENDING.getCode()); jobExecution.setCreatedBy(request.getCreatedBy()); jobExecutionMapper.insert(jobExecution); quickDeployAsync(jobExecution.getId(), request.getOrgConfigId(), lastSuccessJob.getId()); QuickDeployTriggerResponse response = new QuickDeployTriggerResponse(); response.setJobId(jobExecution.getId().toString()); response.setStatus(DeployStatus.PENDING.getCode()); response.setMessage("Quick Deploy task created"); return response; } @Override @Async("deployExecutor") public Future quickDeployAsync(Long jobExecutionId, Long orgConfigId, Long lastJobId) { String jobId = jobExecutionId.toString(); try { updateJobStatus(jobExecutionId, DeployStatus.PROCESSING); String quickDeployResult = metadataApiService.quickDeploy(orgConfigId, lastJobId); updateJobStatus(jobExecutionId, DeployStatus.SUCCESS); parseQuickDeployResult(jobExecutionId, quickDeployResult); progressMap.remove(jobId); } catch (Exception e) { updateJobStatus(jobExecutionId, DeployStatus.FAILED); throw new DeployException("Quick Deploy failed", e); } finally { quickDeployFutureMap.remove(jobId); } return null; } @Override public QuickDeployProgressResponse getQuickDeployProgress(String jobId) { QuickDeployProgressResponse progress = progressMap.get(jobId); if (progress == null) { DataiMetaJobExecution jobExecution = jobExecutionMapper.selectById(Long.parseLong(jobId)); if (jobExecution == null) { throw new DeployException("Job not found"); } progress = new QuickDeployProgressResponse(); progress.setJobId(jobId); progress.setStatus(jobExecution.getStatus()); progress.setProgress(0); progress.setMessage("Quick Deploy in progress"); } return progress; } @Override public void cancelQuickDeploy(String jobId) { Future future = quickDeployFutureMap.get(jobId); if (future == null) { throw new DeployException("Job not found or already completed"); } future.cancel(true); updateJobStatus(Long.parseLong(jobId), DeployStatus.CANCELLED); progressMap.remove(jobId); } @Override public QuickDeployHistoryResponse getQuickDeployHistory(Long orgConfigId, String status, int page, int size) { QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("job_type", "QUICK_DEPLOY"); if (orgConfigId != null) { queryWrapper.eq("org_config_id", orgConfigId); } if (status != null && !status.isEmpty()) { queryWrapper.eq("status", status); } queryWrapper.orderByDesc("create_time"); Page pageResult = jobExecutionMapper.selectPage( new Page<>(page, size), queryWrapper); QuickDeployHistoryResponse response = new QuickDeployHistoryResponse(); response.setTotal(pageResult.getTotal()); response.setPage(page); response.setSize(size); response.setRecords(pageResult.getRecords()); return response; } private void updateJobStatus(Long jobExecutionId, DeployStatus status) { DataiMetaJobExecution jobExecution = new DataiMetaJobExecution(); jobExecution.setId(jobExecutionId); jobExecution.setStatus(status.getCode()); jobExecution.setUpdateTime(new Date()); jobExecutionMapper.updateById(jobExecution); } private void parseQuickDeployResult(Long jobExecutionId, String quickDeployResult) { DataiMetaJobExecution jobExecution = jobExecutionMapper.selectById(jobExecutionId); if (jobExecution != null) { jobExecution.setRemark(quickDeployResult); jobExecutionMapper.updateById(jobExecution); } } } ``` #### 4. QuickDeployTriggerRequest DTO ```java package com.datai.salesforce.metadata.deploy.dto; import lombok.Data; @Data public class QuickDeployTriggerRequest { private Long orgConfigId; private String createdBy; } ``` #### 5. QuickDeployTriggerResponse DTO ```java package com.datai.salesforce.metadata.deploy.dto; import lombok.Data; @Data public class QuickDeployTriggerResponse { private String jobId; private String status; private String message; } ``` #### 6. QuickDeployProgressResponse DTO ```java package com.datai.salesforce.metadata.deploy.dto; import lombok.Data; @Data public class QuickDeployProgressResponse { private String jobId; private String status; private int progress; private String message; } ``` #### 7. QuickDeployHistoryResponse DTO ```java package com.datai.salesforce.metadata.deploy.dto; import com.datai.salesforce.metadata.entity.DataiMetaJobExecution; import lombok.Data; import java.util.List; @Data public class QuickDeployHistoryResponse { private long total; private int page; private int size; private List records; } ``` #### 8. QuickDeployResult DTO ```java package com.datai.salesforce.metadata.deploy.dto; import lombok.Data; @Data public class QuickDeployResult { private String jobId; private String status; private String message; private String errorMessage; private int codeCoverage; } ``` ## 约束 ### 技术栈限制 - 必须基于 Spring Boot 3 + MyBatis Plus + MySQL 技术栈 - 必须使用现有的 IMetadataApiService 接口进行 Salesforce API 调用 - 必须使用现有的 DataiMetaJobExecution 实体和 Mapper ### 架构约束 - 必须遵循 Authentication.canvas 中定义的架构和调用关系 - 必须在 datai-salesforce-metadata 模块下实现 - 必须使用 RESTful API 设计规范 ### 性能要求 - Quick Deploy 触发响应时间 < 1s - Quick Deploy 状态轮询任务执行时间 < 1s - Quick Deploy 进度查询响应时间 < 500ms - Quick Deploy 历史记录查询响应时间 < 1s ### 安全性要求 - Quick Deploy 操作需要认证 - 部署数据传输加密 - 敏感信息不记录到日志 ## Rule Set "请严格参考 @Authentication.canvas 中的状态机转移逻辑,不要自行发挥。" **具体规则**: - 必须使用 Canvas 中定义的类名和方法名 - 必须遵循 Canvas 中定义的调用关系 - 必须参考 Canvas 中的流程图逻辑 - 必须复用现有的 DeployStatus 枚举和状态管理逻辑 - 必须复用现有的 AsyncConfig 配置类和线程池 ## 验收标准 ### 功能完整性 - Quick Deploy 触发成功,支持选择最近一次成功的部署记录 - Quick Deploy 验证成功,检查部署记录是否在 30 天内 - Quick Deploy 历史记录成功,支持分页查询和条件查询 - Quick Deploy 状态监控正常工作,使用状态机管理 Quick Deploy 状态 - Quick Deploy 结果解析成功,错误信息提取正确 ### 代码正确性 - 代码符合项目编码规范,有清晰的注释 - 单元测试覆盖率 > 80% - 集成测试通过率 100% - 无严重的代码质量问题 ### 文档准确性 - API 文档完整,包含接口说明、参数说明、返回值说明 - 代码注释清晰,包含方法说明、参数说明、返回值说明 - 使用文档完整,包含使用示例和注意事项 ### 性能指标 - Quick Deploy 触发响应时间 < 1s - Quick Deploy 状态轮询任务执行时间 < 1s - Quick Deploy 进度查询响应时间 < 500ms - Quick Deploy 历史记录查询响应时间 < 1s ## 风险 ### 输出质量风险 - Quick Deploy 验证逻辑复杂可能导致验证不准确 - 状态监控不准确可能导致 Quick Deploy 状态不一致 - 结果解析不完善可能导致错误信息不准确 ### 技术实现风险 - Quick Deploy 可能在某些情况下不可用 - 部署历史记录过多可能影响查询性能 - 异步任务状态跟踪困难 ### 时间成本风险 - Quick Deploy 功能实现可能需要较长时间 - 测试和验证可能需要较长时间 - 文档编写可能需要较长时间 ## 使用记录 | 日期 | 使用场景 | 输入参数 | 输出结果 | 反馈 | 改进措施 | |------|---------|---------|---------|------|----------| | 2026-01-19 | Quick Deploy功能实现 | REQ-010-9, ADR-0018 | Quick Deploy功能代码 | 待反馈 | 待改进 |