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

27 KiB
Raw Permalink Blame History

Prompt - Destructive Changes功能实现

输入引用

引用相关的 docs 文档链接:

Context Maps

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

目标

实现 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 控制器

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 服务接口

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

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 服务接口

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

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 工具类

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 工具类

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 实体类

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 接口

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 类

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 异常类

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功能代码 待反馈 待改进