datai/docs/archive/prompts/020-destructive-changes.md

804 lines
27 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Prompt - Destructive Changes功能实现
## 输入引用
引用相关的 docs 文档链接:
- [REQ-010-10.md](../requirements/REQ-010-10.md) - Destructive Changes功能实现需求文档
- [0019-destructive-changes.md](../decisions/adr/0019-destructive-changes.md) - Destructive Changes功能实现架构决策
- [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客户端
## 目标
实现 Destructive Changes 功能,包括 destructiveChanges.xml 配置、Destructive Changes 部署、Destructive Changes 验证、Destructive Changes 历史记录、Destructive Changes 回滚。支持部署时删除指定的元数据组件,使用 destructiveChanges.xml 配置删除规则。
## 输出格式
### 代码结构
```
datai-salesforce-metadata/
├── destructive-changes/
│ ├── controller/
│ │ └── DestructiveChangesController.java
│ ├── service/
│ │ ├── IDestructiveChangesService.java
│ │ ├── DestructiveChangesServiceImpl.java
│ │ ├── IDestructiveChangesConfigService.java
│ │ └── DestructiveChangesConfigServiceImpl.java
│ ├── dto/
│ │ ├── DestructiveChangesConfigRequest.java
│ │ ├── DestructiveChangesConfigResponse.java
│ │ ├── DestructiveChangesDeployRequest.java
│ │ ├── DestructiveChangesDeployResponse.java
│ │ ├── DestructiveChangesValidateRequest.java
│ │ ├── DestructiveChangesValidateResponse.java
│ │ ├── DestructiveChangesHistoryResponse.java
│ │ ├── DestructiveChangesRollbackRequest.java
│ │ └── DestructiveChangesRollbackResponse.java
│ ├── entity/
│ │ └── DestructiveChangeRule.java
│ ├── mapper/
│ │ └── DestructiveChangeRuleMapper.java
│ ├── enums/
│ │ └── MetadataType.java
│ ├── util/
│ │ ├── DestructiveChangesXmlParser.java
│ │ └── DestructiveChangesXmlBuilder.java
│ └── exception/
│ └── DestructiveChangesException.java
```
### 代码示例语言Java
#### 1. DestructiveChangesController 控制器
```java
package com.datai.salesforce.metadata.destructive.controller;
import com.datai.salesforce.metadata.destructive.dto.*;
import com.datai.salesforce.metadata.destructive.service.IDestructiveChangesService;
import com.datai.salesforce.metadata.destructive.service.IDestructiveChangesConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/metadata/destructive-changes")
public class DestructiveChangesController {
@Autowired
private IDestructiveChangesService destructiveChangesService;
@Autowired
private IDestructiveChangesConfigService destructiveChangesConfigService;
@PostMapping("/config")
public DestructiveChangesConfigResponse saveConfig(@RequestBody DestructiveChangesConfigRequest request) {
return destructiveChangesConfigService.saveConfig(request);
}
@GetMapping("/config/{taskId}")
public DestructiveChangesConfigResponse getConfig(@PathVariable Long taskId) {
return destructiveChangesConfigService.getConfig(taskId);
}
@PostMapping("/validate")
public DestructiveChangesValidateResponse validateConfig(@RequestBody DestructiveChangesValidateRequest request) {
return destructiveChangesConfigService.validateConfig(request);
}
@PostMapping("/deploy")
public DestructiveChangesDeployResponse deploy(@RequestBody DestructiveChangesDeployRequest request) {
return destructiveChangesService.deploy(request);
}
@GetMapping("/history")
public DestructiveChangesHistoryResponse getHistory(
@RequestParam(required = false) Long orgConfigId,
@RequestParam(required = false) String status,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size) {
return destructiveChangesService.getHistory(orgConfigId, status, page, size);
}
@PostMapping("/rollback/{jobId}")
public DestructiveChangesRollbackResponse rollback(@PathVariable Long jobId) {
return destructiveChangesService.rollback(jobId);
}
}
```
#### 2. IDestructiveChangesService 服务接口
```java
package com.datai.salesforce.metadata.destructive.service;
import com.datai.salesforce.metadata.destructive.dto.*;
public interface IDestructiveChangesService {
DestructiveChangesDeployResponse deploy(DestructiveChangesDeployRequest request);
DestructiveChangesHistoryResponse getHistory(Long orgConfigId, String status, int page, int size);
DestructiveChangesRollbackResponse rollback(Long jobId);
}
```
#### 3. DestructiveChangesServiceImpl 服务实现
```java
package com.datai.salesforce.metadata.destructive.service;
import com.datai.salesforce.metadata.destructive.dto.*;
import com.datai.salesforce.metadata.destructive.entity.DestructiveChangeRule;
import com.datai.salesforce.metadata.destructive.mapper.DestructiveChangeRuleMapper;
import com.datai.salesforce.metadata.destructive.util.DestructiveChangesXmlBuilder;
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.io.File;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Future;
@Service
public class DestructiveChangesServiceImpl implements IDestructiveChangesService {
@Autowired
private IMetadataApiService metadataApiService;
@Autowired
private DataiMetaJobExecutionMapper jobExecutionMapper;
@Autowired
private DestructiveChangeRuleMapper destructiveChangeRuleMapper;
@Autowired
private DestructiveChangesXmlBuilder xmlBuilder;
@Override
@Transactional
public DestructiveChangesDeployResponse deploy(DestructiveChangesDeployRequest request) {
QueryWrapper<DestructiveChangeRule> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("task_id", request.getTaskId());
List<DestructiveChangeRule> rules = destructiveChangeRuleMapper.selectList(queryWrapper);
if (rules.isEmpty()) {
throw new RuntimeException("No destructive change rules found");
}
String destructiveChangesXml = xmlBuilder.buildXml(rules);
File destructiveChangesFile = xmlBuilder.saveXmlToFile(request.getTaskId(), destructiveChangesXml);
DataiMetaJobExecution jobExecution = new DataiMetaJobExecution();
jobExecution.setTaskId(request.getTaskId());
jobExecution.setOrgConfigId(request.getOrgConfigId());
jobExecution.setJobType("DESTRUCTIVE_CHANGES");
jobExecution.setStatus("PENDING");
jobExecution.setCreatedBy(request.getCreatedBy());
jobExecutionMapper.insert(jobExecution);
deployAsync(jobExecution.getId(), request.getTaskId(), request.getOrgConfigId(), destructiveChangesFile);
DestructiveChangesDeployResponse response = new DestructiveChangesDeployResponse();
response.setJobId(jobExecution.getId().toString());
response.setStatus("PENDING");
response.setMessage("Destructive Changes deploy task created");
return response;
}
@Override
@Async("deployExecutor")
public Future<Void> deployAsync(Long jobExecutionId, Long taskId, Long orgConfigId, File destructiveChangesFile) {
String jobId = jobExecutionId.toString();
try {
updateJobStatus(jobExecutionId, "PROCESSING");
String deployResult = metadataApiService.deploy(orgConfigId, null, destructiveChangesFile);
updateJobStatus(jobExecutionId, "SUCCESS");
parseDeployResult(jobExecutionId, deployResult);
} catch (Exception e) {
updateJobStatus(jobExecutionId, "FAILED");
throw new RuntimeException("Destructive Changes deploy failed", e);
}
return null;
}
@Override
public DestructiveChangesHistoryResponse getHistory(Long orgConfigId, String status, int page, int size) {
QueryWrapper<DataiMetaJobExecution> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("job_type", "DESTRUCTIVE_CHANGES");
if (orgConfigId != null) {
queryWrapper.eq("org_config_id", orgConfigId);
}
if (status != null && !status.isEmpty()) {
queryWrapper.eq("status", status);
}
queryWrapper.orderByDesc("create_time");
Page<DataiMetaJobExecution> pageResult = jobExecutionMapper.selectPage(
new Page<>(page, size), queryWrapper);
DestructiveChangesHistoryResponse response = new DestructiveChangesHistoryResponse();
response.setTotal(pageResult.getTotal());
response.setPage(page);
response.setSize(size);
response.setRecords(pageResult.getRecords());
return response;
}
@Override
@Transactional
public DestructiveChangesRollbackResponse rollback(Long jobId) {
DataiMetaJobExecution jobExecution = jobExecutionMapper.selectById(jobId);
if (jobExecution == null) {
throw new RuntimeException("Job not found");
}
if (!"SUCCESS".equals(jobExecution.getStatus())) {
throw new RuntimeException("Only successful deployments can be rolled back");
}
String backupData = jobExecution.getBackupData();
if (backupData == null || backupData.isEmpty()) {
throw new RuntimeException("No backup data found");
}
rollbackAsync(jobId, jobExecution.getOrgConfigId(), backupData);
DestructiveChangesRollbackResponse response = new DestructiveChangesRollbackResponse();
response.setJobId(jobId.toString());
response.setStatus("ROLLBACK_IN_PROGRESS");
response.setMessage("Rollback task created");
return response;
}
@Async("deployExecutor")
public Future<Void> rollbackAsync(Long jobId, Long orgConfigId, String backupData) {
try {
updateJobStatus(jobId, "ROLLBACK_PROCESSING");
String rollbackResult = metadataApiService.rollback(orgConfigId, backupData);
updateJobStatus(jobId, "ROLLBACK_SUCCESS");
parseRollbackResult(jobId, rollbackResult);
} catch (Exception e) {
updateJobStatus(jobId, "ROLLBACK_FAILED");
throw new RuntimeException("Rollback failed", e);
}
return null;
}
private void updateJobStatus(Long jobExecutionId, String status) {
DataiMetaJobExecution jobExecution = new DataiMetaJobExecution();
jobExecution.setId(jobExecutionId);
jobExecution.setStatus(status);
jobExecution.setUpdateTime(new Date());
jobExecutionMapper.updateById(jobExecution);
}
private void parseDeployResult(Long jobExecutionId, String deployResult) {
DataiMetaJobExecution jobExecution = jobExecutionMapper.selectById(jobExecutionId);
if (jobExecution != null) {
jobExecution.setRemark(deployResult);
jobExecutionMapper.updateById(jobExecution);
}
}
private void parseRollbackResult(Long jobId, String rollbackResult) {
DataiMetaJobExecution jobExecution = jobExecutionMapper.selectById(jobId);
if (jobExecution != null) {
jobExecution.setRemark(rollbackResult);
jobExecutionMapper.updateById(jobExecution);
}
}
}
```
#### 4. IDestructiveChangesConfigService 服务接口
```java
package com.datai.salesforce.metadata.destructive.service;
import com.datai.salesforce.metadata.destructive.dto.*;
public interface IDestructiveChangesConfigService {
DestructiveChangesConfigResponse saveConfig(DestructiveChangesConfigRequest request);
DestructiveChangesConfigResponse getConfig(Long taskId);
DestructiveChangesValidateResponse validateConfig(DestructiveChangesValidateRequest request);
}
```
#### 5. DestructiveChangesConfigServiceImpl 服务实现
```java
package com.datai.salesforce.metadata.destructive.service;
import com.datai.salesforce.metadata.destructive.dto.*;
import com.datai.salesforce.metadata.destructive.entity.DestructiveChangeRule;
import com.datai.salesforce.metadata.destructive.mapper.DestructiveChangeRuleMapper;
import com.datai.salesforce.metadata.destructive.util.DestructiveChangesXmlParser;
import com.datai.salesforce.metadata.destructive.util.DestructiveChangesXmlBuilder;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class DestructiveChangesConfigServiceImpl implements IDestructiveChangesConfigService {
@Autowired
private DestructiveChangeRuleMapper destructiveChangeRuleMapper;
@Autowired
private DestructiveChangesXmlParser xmlParser;
@Autowired
private DestructiveChangesXmlBuilder xmlBuilder;
@Override
@Transactional
public DestructiveChangesConfigResponse saveConfig(DestructiveChangesConfigRequest request) {
QueryWrapper<DestructiveChangeRule> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("task_id", request.getTaskId());
destructiveChangeRuleMapper.delete(queryWrapper);
for (DestructiveChangesConfigRequest.Rule rule : request.getRules()) {
DestructiveChangeRule entity = new DestructiveChangeRule();
entity.setTaskId(request.getTaskId());
entity.setMetadataType(rule.getMetadataType());
entity.setMemberName(rule.getMemberName());
destructiveChangeRuleMapper.insert(entity);
}
String xml = xmlBuilder.buildXml(request.getTaskId());
DestructiveChangesConfigResponse response = new DestructiveChangesConfigResponse();
response.setTaskId(request.getTaskId());
response.setXml(xml);
response.setMessage("Configuration saved successfully");
return response;
}
@Override
public DestructiveChangesConfigResponse getConfig(Long taskId) {
QueryWrapper<DestructiveChangeRule> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("task_id", taskId);
List<DestructiveChangeRule> rules = destructiveChangeRuleMapper.selectList(queryWrapper);
String xml = xmlBuilder.buildXml(taskId);
DestructiveChangesConfigResponse response = new DestructiveChangesConfigResponse();
response.setTaskId(taskId);
response.setXml(xml);
response.setRules(rules);
return response;
}
@Override
public DestructiveChangesValidateResponse validateConfig(DestructiveChangesValidateRequest request) {
try {
xmlParser.parseXml(request.getXml());
DestructiveChangesValidateResponse response = new DestructiveChangesValidateResponse();
response.setValid(true);
response.setMessage("Configuration is valid");
return response;
} catch (Exception e) {
DestructiveChangesValidateResponse response = new DestructiveChangesValidateResponse();
response.setValid(false);
response.setMessage(e.getMessage());
return response;
}
}
}
```
#### 6. DestructiveChangesXmlParser 工具类
```java
package com.datai.salesforce.metadata.destructive.util;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@Component
public class DestructiveChangesXmlParser {
public void parseXml(String xml) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new ByteArrayInputStream(xml.getBytes()));
Element root = document.getDocumentElement();
if (!"Package".equals(root.getNodeName())) {
throw new RuntimeException("Root element must be 'Package'");
}
NodeList types = root.getElementsByTagName("types");
for (int i = 0; i < types.getLength(); i++) {
Element typeElement = (Element) types.item(i);
NodeList members = typeElement.getElementsByTagName("members");
NodeList name = typeElement.getElementsByTagName("name");
if (members.getLength() == 0) {
throw new RuntimeException("Type element must have at least one 'members' element");
}
if (name.getLength() != 1) {
throw new RuntimeException("Type element must have exactly one 'name' element");
}
}
}
}
```
#### 7. DestructiveChangesXmlBuilder 工具类
```java
package com.datai.salesforce.metadata.destructive.util;
import com.datai.salesforce.metadata.destructive.entity.DestructiveChangeRule;
import com.datai.salesforce.metadata.destructive.mapper.DestructiveChangeRuleMapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Component
public class DestructiveChangesXmlBuilder {
@Autowired
private DestructiveChangeRuleMapper destructiveChangeRuleMapper;
public String buildXml(Long taskId) {
QueryWrapper<DestructiveChangeRule> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("task_id", taskId);
List<DestructiveChangeRule> rules = destructiveChangeRuleMapper.selectList(queryWrapper);
return buildXml(rules);
}
public String buildXml(List<DestructiveChangeRule> rules) {
StringBuilder xml = new StringBuilder();
xml.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
xml.append("<Package xmlns=\"http://soap.sforce.com/2006/04/metadata\">\n");
Map<String, List<DestructiveChangeRule>> groupedRules = rules.stream()
.collect(Collectors.groupingBy(DestructiveChangeRule::getMetadataType));
for (Map.Entry<String, List<DestructiveChangeRule>> entry : groupedRules.entrySet()) {
xml.append(" <types>\n");
for (DestructiveChangeRule rule : entry.getValue()) {
xml.append(" <members>").append(rule.getMemberName()).append("</members>\n");
}
xml.append(" <name>").append(entry.getKey()).append("</name>\n");
xml.append(" </types>\n");
}
xml.append(" <version>56.0</version>\n");
xml.append("</Package>");
return xml.toString();
}
public File saveXmlToFile(Long taskId, String xml) throws IOException {
String fileName = "destructiveChanges-" + taskId + ".xml";
File file = new File(System.getProperty("java.io.tmpdir"), fileName);
try (FileWriter writer = new FileWriter(file)) {
writer.write(xml);
}
return file;
}
}
```
#### 8. DestructiveChangeRule 实体类
```java
package com.datai.salesforce.metadata.destructive.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@TableName("datai_destructive_change_rule")
public class DestructiveChangeRule {
@TableId(type = IdType.AUTO)
private Long id;
private Long taskId;
private String metadataType;
private String memberName;
private LocalDateTime createTime;
private LocalDateTime updateTime;
}
```
#### 9. DestructiveChangeRuleMapper 接口
```java
package com.datai.salesforce.metadata.destructive.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.datai.salesforce.metadata.destructive.entity.DestructiveChangeRule;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface DestructiveChangeRuleMapper extends BaseMapper<DestructiveChangeRule> {
}
```
#### 10. DTO 类
```java
package com.datai.salesforce.metadata.destructive.dto;
import lombok.Data;
import java.util.List;
@Data
public class DestructiveChangesConfigRequest {
private Long taskId;
private List<Rule> rules;
@Data
public static class Rule {
private String metadataType;
private String memberName;
}
}
@Data
public class DestructiveChangesConfigResponse {
private Long taskId;
private String xml;
private List<Object> rules;
private String message;
}
@Data
public class DestructiveChangesValidateRequest {
private String xml;
}
@Data
public class DestructiveChangesValidateResponse {
private boolean valid;
private String message;
}
@Data
public class DestructiveChangesDeployRequest {
private Long taskId;
private Long orgConfigId;
private String createdBy;
}
@Data
public class DestructiveChangesDeployResponse {
private String jobId;
private String status;
private String message;
}
@Data
public class DestructiveChangesHistoryResponse {
private long total;
private int page;
private int size;
private List<Object> records;
}
@Data
public class DestructiveChangesRollbackRequest {
private Long jobId;
}
@Data
public class DestructiveChangesRollbackResponse {
private String jobId;
private String status;
private String message;
}
```
#### 11. DestructiveChangesException 异常类
```java
package com.datai.salesforce.metadata.destructive.exception;
public class DestructiveChangesException extends RuntimeException {
public DestructiveChangesException(String message) {
super(message);
}
public DestructiveChangesException(String message, Throwable cause) {
super(message, cause);
}
}
```
## 约束
### 技术栈限制
- 必须基于 Spring Boot 3 + MyBatis Plus + MySQL 技术栈
- 必须使用现有的 IMetadataApiService 接口进行 Salesforce API 调用
- 必须使用现有的 DataiMetaJobExecution 实体和 Mapper
- 必须使用 Java XML 解析库(如 JAXB 或 DOM4J处理 destructiveChanges.xml
### 架构约束
- 必须遵循 Authentication.canvas 中定义的架构和调用关系
- 必须在 datai-salesforce-metadata 模块下实现
- 必须使用 RESTful API 设计规范
### 性能要求
- destructiveChanges.xml 配置响应时间 < 1s
- Destructive Changes 部署触发响应时间 < 1s
- Destructive Changes 验证响应时间 < 500ms
- Destructive Changes 历史记录查询响应时间 < 1s
- Destructive Changes 回滚响应时间 < 2s
### 安全性要求
- Destructive Changes 操作需要认证
- 删除操作需要备份支持回滚
- 部署数据传输加密
- 敏感信息不记录到日志
## Rule Set
"请严格参考 @Authentication.canvas 中的状态机转移逻辑不要自行发挥。"
**具体规则**
- 必须使用 Canvas 中定义的类名和方法名
- 必须遵循 Canvas 中定义的调用关系
- 必须参考 Canvas 中的流程图逻辑
- 必须复用现有的 DeployStatus 枚举和状态管理逻辑
- 必须复用现有的 AsyncConfig 配置类和线程池
## 验收标准
### 功能完整性
- destructiveChanges.xml 配置成功支持可视化编辑
- destructiveChanges.xml 格式符合 Salesforce Metadata API 规范
- 支持多种元数据类型配置
- 支持通配符配置
- destructiveChanges.xml 验证功能正常工作
- Destructive Changes 部署成功支持删除指定的元数据组件
- 支持批量删除
- 删除操作安全可靠
- API 接口符合 RESTful 规范
- Destructive Changes 验证功能正常工作
- destructiveChanges.xml 格式验证正确
- 删除规则验证正确
- 验证失败返回详细错误信息
- Destructive Changes 历史记录成功
- 支持分页查询
- 支持条件查询
- 历史记录完整
- Destructive Changes 回滚功能正常工作
- 支持恢复被删除的元数据组件
- 回滚操作安全可靠
- 回滚日志记录完整
### 代码正确性
- 代码符合项目编码规范有清晰的注释
- 单元测试覆盖率 > 80%
- 集成测试通过率 100%
- 无严重的代码质量问题
### 文档准确性
- API 文档完整,包含接口说明、参数说明、返回值说明
- 代码注释清晰,包含方法说明、参数说明、返回值说明
- 使用文档完整,包含使用示例和注意事项
### 性能指标
- destructiveChanges.xml 配置响应时间 < 1s
- Destructive Changes 部署触发响应时间 < 1s
- Destructive Changes 验证响应时间 < 500ms
- Destructive Changes 历史记录查询响应时间 < 1s
- Destructive Changes 回滚响应时间 < 2s
## 风险
### 输出质量风险
- destructiveChanges.xml 格式验证不完善可能导致误删除
- 删除操作不可逆可能导致数据丢失
- 回滚功能不完善可能导致无法恢复
### 技术实现风险
- XML 解析库选择不当可能导致解析错误
- 备份和回滚逻辑复杂可能导致实现困难
- 异步任务状态跟踪困难
### 时间成本风险
- Destructive Changes 功能实现可能需要较长时间
- 测试和验证可能需要较长时间
- 文档编写可能需要较长时间
## 使用记录
| 日期 | 使用场景 | 输入参数 | 输出结果 | 反馈 | 改进措施 |
|------|---------|---------|---------|------|----------|
| 2026-01-19 | Destructive Changes功能实现 | REQ-010-10, ADR-0019 | Destructive Changes功能代码 | 待反馈 | 待改进 |