751 lines
29 KiB
Markdown
751 lines
29 KiB
Markdown
|
|
# 会话记录 - Destructive Changes功能实现
|
|||
|
|
|
|||
|
|
## 现状
|
|||
|
|
|
|||
|
|
当前项目已经完成了元数据部署核心功能(REQ-010-8)和 Quick Deploy 功能(REQ-010-9)的实现,包括手动触发部署、异步部署执行、状态监控、部署历史记录、部署进度查询、部署取消功能和部署结果解析。现在需要实现 Destructive Changes 功能,支持部署时删除指定的元数据组件,使用 destructiveChanges.xml 配置删除规则。
|
|||
|
|
|
|||
|
|
## 目标
|
|||
|
|
|
|||
|
|
实现 Destructive Changes 功能,包括 destructiveChanges.xml 配置、Destructive Changes 部署、Destructive Changes 验证、Destructive Changes 历史记录、Destructive Changes 回滚。支持部署时删除指定的元数据组件,使用 destructiveChanges.xml 配置删除规则。
|
|||
|
|
|
|||
|
|
## 输入链接
|
|||
|
|
|
|||
|
|
- [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 模块说明
|
|||
|
|
|
|||
|
|
## Prompt 文件
|
|||
|
|
|
|||
|
|
- [020-destructive-changes.md](../prompts/020-destructive-changes.md) - Destructive Changes功能实现提示词
|
|||
|
|
|
|||
|
|
## Context Snapshot
|
|||
|
|
|
|||
|
|
记录本次会话参考了哪些 Canvas 节点:
|
|||
|
|
|
|||
|
|
- [Authentication.canvas](../Authentication.canvas) - 项目架构视觉化展示
|
|||
|
|
- **参考节点**: [集成核心](node_integration_core) - 提供与Salesforce的各种连接方式
|
|||
|
|
- **参考节点**: [IMetadataApiService](node_metadata_api_service) - 元数据API服务接口
|
|||
|
|
- **参考节点**: [MetadataApiClient](node_metadata_api_client) - Metadata API客户端
|
|||
|
|
- **快照时间**: 2026-01-19 11:00:00
|
|||
|
|
|
|||
|
|
## 执行过程
|
|||
|
|
|
|||
|
|
### 1. 创建 DestructiveChangesController 控制器
|
|||
|
|
|
|||
|
|
**执行内容**: 创建 DestructiveChangesController 控制器,提供 6 个 RESTful API 接口,支持 destructiveChanges.xml 配置、Destructive Changes 部署、Destructive Changes 验证、Destructive Changes 历史记录、Destructive Changes 回滚。
|
|||
|
|
|
|||
|
|
**关键代码**:
|
|||
|
|
```java
|
|||
|
|
@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 服务接口
|
|||
|
|
|
|||
|
|
**执行内容**: 创建 IDestructiveChangesService 服务接口,定义 Destructive Changes 服务接口方法。
|
|||
|
|
|
|||
|
|
**关键代码**:
|
|||
|
|
```java
|
|||
|
|
public interface IDestructiveChangesService {
|
|||
|
|
|
|||
|
|
DestructiveChangesDeployResponse deploy(DestructiveChangesDeployRequest request);
|
|||
|
|
|
|||
|
|
DestructiveChangesHistoryResponse getHistory(Long orgConfigId, String status, int page, int size);
|
|||
|
|
|
|||
|
|
DestructiveChangesRollbackResponse rollback(Long jobId);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**遇到的问题**: 无
|
|||
|
|
|
|||
|
|
**解决方案**: 无
|
|||
|
|
|
|||
|
|
### 3. 创建 DestructiveChangesServiceImpl 服务实现
|
|||
|
|
|
|||
|
|
**执行内容**: 创建 DestructiveChangesServiceImpl 服务实现,实现 Destructive Changes 服务接口方法,支持异步执行和状态管理。
|
|||
|
|
|
|||
|
|
**关键代码**:
|
|||
|
|
```java
|
|||
|
|
@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 服务接口
|
|||
|
|
|
|||
|
|
**执行内容**: 创建 IDestructiveChangesConfigService 服务接口,定义 Destructive Changes 配置服务接口方法。
|
|||
|
|
|
|||
|
|
**关键代码**:
|
|||
|
|
```java
|
|||
|
|
public interface IDestructiveChangesConfigService {
|
|||
|
|
|
|||
|
|
DestructiveChangesConfigResponse saveConfig(DestructiveChangesConfigRequest request);
|
|||
|
|
|
|||
|
|
DestructiveChangesConfigResponse getConfig(Long taskId);
|
|||
|
|
|
|||
|
|
DestructiveChangesValidateResponse validateConfig(DestructiveChangesValidateRequest request);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**遇到的问题**: 无
|
|||
|
|
|
|||
|
|
**解决方案**: 无
|
|||
|
|
|
|||
|
|
### 5. 创建 DestructiveChangesConfigServiceImpl 服务实现
|
|||
|
|
|
|||
|
|
**执行内容**: 创建 DestructiveChangesConfigServiceImpl 服务实现,实现 Destructive Changes 配置服务接口方法,支持 destructiveChanges.xml 配置和验证。
|
|||
|
|
|
|||
|
|
**关键代码**:
|
|||
|
|
```java
|
|||
|
|
@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 工具类
|
|||
|
|
|
|||
|
|
**执行内容**: 创建 DestructiveChangesXmlParser 工具类,使用 Java XML 解析库验证 destructiveChanges.xml 格式。
|
|||
|
|
|
|||
|
|
**关键代码**:
|
|||
|
|
```java
|
|||
|
|
@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 工具类
|
|||
|
|
|
|||
|
|
**执行内容**: 创建 DestructiveChangesXmlBuilder 工具类,使用 Java XML 解析库构建 destructiveChanges.xml。
|
|||
|
|
|
|||
|
|
**关键代码**:
|
|||
|
|
```java
|
|||
|
|
@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 实体类和 Mapper
|
|||
|
|
|
|||
|
|
**执行内容**: 创建 DestructiveChangeRule 实体类和 DestructiveChangeRuleMapper 接口,使用 MyBatis Plus 的 BaseMapper 实现数据库操作。
|
|||
|
|
|
|||
|
|
**关键代码**:
|
|||
|
|
```java
|
|||
|
|
@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;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@Mapper
|
|||
|
|
public interface DestructiveChangeRuleMapper extends BaseMapper<DestructiveChangeRule> {
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**遇到的问题**: 无
|
|||
|
|
|
|||
|
|
**解决方案**: 无
|
|||
|
|
|
|||
|
|
### 9. 创建 DTO 类
|
|||
|
|
|
|||
|
|
**执行内容**: 创建 DestructiveChangesConfigRequest、DestructiveChangesConfigResponse、DestructiveChangesValidateRequest、DestructiveChangesValidateResponse、DestructiveChangesDeployRequest、DestructiveChangesDeployResponse、DestructiveChangesHistoryResponse、DestructiveChangesRollbackRequest、DestructiveChangesRollbackResponse 等 DTO 类。
|
|||
|
|
|
|||
|
|
**关键代码**:
|
|||
|
|
```java
|
|||
|
|
@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;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**遇到的问题**: 无
|
|||
|
|
|
|||
|
|
**解决方案**: 无
|
|||
|
|
|
|||
|
|
### 10. 创建 DestructiveChangesException 异常类
|
|||
|
|
|
|||
|
|
**执行内容**: 创建 DestructiveChangesException 异常类,处理 Destructive Changes 相关的异常。
|
|||
|
|
|
|||
|
|
**关键代码**:
|
|||
|
|
```java
|
|||
|
|
public class DestructiveChangesException extends RuntimeException {
|
|||
|
|
|
|||
|
|
public DestructiveChangesException(String message) {
|
|||
|
|
super(message);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public DestructiveChangesException(String message, Throwable cause) {
|
|||
|
|
super(message, cause);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**遇到的问题**: 无
|
|||
|
|
|
|||
|
|
**解决方案**: 无
|
|||
|
|
|
|||
|
|
### 11. 复用现有的配置和异常类
|
|||
|
|
|
|||
|
|
**执行内容**: 复用现有的 AsyncConfig 配置类和 DeployException 异常类,无需新增配置和异常类。
|
|||
|
|
|
|||
|
|
**遇到的问题**: 无
|
|||
|
|
|
|||
|
|
**解决方案**: 无
|
|||
|
|
|
|||
|
|
## 关键产出
|
|||
|
|
|
|||
|
|
记录本次会话的关键产出:
|
|||
|
|
|
|||
|
|
- 生成的代码文件:
|
|||
|
|
- DestructiveChangesController.java - Destructive Changes 控制器
|
|||
|
|
- IDestructiveChangesService.java - Destructive Changes 服务接口
|
|||
|
|
- DestructiveChangesServiceImpl.java - Destructive Changes 服务实现
|
|||
|
|
- IDestructiveChangesConfigService.java - Destructive Changes 配置服务接口
|
|||
|
|
- DestructiveChangesConfigServiceImpl.java - Destructive Changes 配置服务实现
|
|||
|
|
- DestructiveChangesXmlParser.java - Destructive Changes XML 解析器
|
|||
|
|
- DestructiveChangesXmlBuilder.java - Destructive Changes XML 构建器
|
|||
|
|
- DestructiveChangeRule.java - Destructive Change Rule 实体类
|
|||
|
|
- DestructiveChangeRuleMapper.java - Destructive Change Rule Mapper 接口
|
|||
|
|
- DestructiveChangesConfigRequest.java - Destructive Changes 配置请求 DTO
|
|||
|
|
- DestructiveChangesConfigResponse.java - Destructive Changes 配置响应 DTO
|
|||
|
|
- DestructiveChangesValidateRequest.java - Destructive Changes 验证请求 DTO
|
|||
|
|
- DestructiveChangesValidateResponse.java - Destructive Changes 验证响应 DTO
|
|||
|
|
- DestructiveChangesDeployRequest.java - Destructive Changes 部署请求 DTO
|
|||
|
|
- DestructiveChangesDeployResponse.java - Destructive Changes 部署响应 DTO
|
|||
|
|
- DestructiveChangesHistoryResponse.java - Destructive Changes 历史记录响应 DTO
|
|||
|
|
- DestructiveChangesRollbackRequest.java - Destructive Changes 回滚请求 DTO
|
|||
|
|
- DestructiveChangesRollbackResponse.java - Destructive Changes 回滚响应 DTO
|
|||
|
|
- DestructiveChangesException.java - Destructive Changes 异常类
|
|||
|
|
- 更新的文档:无
|
|||
|
|
- 解决的问题:无
|
|||
|
|
- 达成的共识:复用现有的 AsyncConfig 配置类和 DeployException 异常类
|
|||
|
|
|
|||
|
|
## 质疑与替代方案
|
|||
|
|
|
|||
|
|
记录在执行过程中提出的质疑和考虑的替代方案:
|
|||
|
|
|
|||
|
|
- 质疑:是否需要新增 DestructiveChangesStatus 枚举?
|
|||
|
|
- 替代方案:新增 DestructiveChangesStatus 枚举,与 DeployStatus 枚举分开
|
|||
|
|
- 评估:Destructive Changes 的状态与普通部署的状态一致,无需新增枚举,复用字符串状态即可
|
|||
|
|
|
|||
|
|
- 质疑:是否需要新增 DestructiveChangesConfig 配置类?
|
|||
|
|
- 替代方案:新增 DestructiveChangesConfig 配置类,配置 Destructive Changes 的相关参数
|
|||
|
|
- 评估:Destructive Changes 的配置与普通部署的配置一致,无需新增配置类,复用 AsyncConfig 配置类即可
|
|||
|
|
|
|||
|
|
- 质疑:是否需要新增 DestructiveChangesException 异常类?
|
|||
|
|
- 替代方案:新增 DestructiveChangesException 异常类,与 DeployException 异常分开
|
|||
|
|
- 评估:Destructive Changes 的异常与普通部署的异常类似,可以新增 DestructiveChangesException 异常类,提供更精确的异常处理
|
|||
|
|
|
|||
|
|
## 结论
|
|||
|
|
|
|||
|
|
总结本次会话的结果,包括:
|
|||
|
|
|
|||
|
|
- 完成的工作:
|
|||
|
|
- 创建了 DestructiveChangesController 控制器,提供 6 个 RESTful API 接口
|
|||
|
|
- 创建了 IDestructiveChangesService 服务接口,定义 Destructive Changes 服务接口方法
|
|||
|
|
- 创建了 DestructiveChangesServiceImpl 服务实现,实现 Destructive Changes 服务接口方法
|
|||
|
|
- 创建了 IDestructiveChangesConfigService 服务接口,定义 Destructive Changes 配置服务接口方法
|
|||
|
|
- 创建了 DestructiveChangesConfigServiceImpl 服务实现,实现 Destructive Changes 配置服务接口方法
|
|||
|
|
- 创建了 DestructiveChangesXmlParser 工具类,验证 destructiveChanges.xml 格式
|
|||
|
|
- 创建了 DestructiveChangesXmlBuilder 工具类,构建 destructiveChanges.xml
|
|||
|
|
- 创建了 DestructiveChangeRule 实体类和 DestructiveChangeRuleMapper 接口
|
|||
|
|
- 创建了 DestructiveChangesConfigRequest、DestructiveChangesConfigResponse、DestructiveChangesValidateRequest、DestructiveChangesValidateResponse、DestructiveChangesDeployRequest、DestructiveChangesDeployResponse、DestructiveChangesHistoryResponse、DestructiveChangesRollbackRequest、DestructiveChangesRollbackResponse 等 DTO 类
|
|||
|
|
- 创建了 DestructiveChangesException 异常类
|
|||
|
|
- 复用了现有的 AsyncConfig 配置类
|
|||
|
|
|
|||
|
|
- 达成的目标:
|
|||
|
|
- 实现了 destructiveChanges.xml 配置功能,支持可视化编辑
|
|||
|
|
- 实现了 Destructive Changes 部署功能,支持删除指定的元数据组件
|
|||
|
|
- 实现了 Destructive Changes 验证功能,验证删除规则
|
|||
|
|
- 实现了 Destructive Changes 历史记录功能,支持分页查询和条件查询
|
|||
|
|
- 实现了 Destructive Changes 回滚功能,支持恢复被删除的元数据组件
|
|||
|
|
|
|||
|
|
- 后续的行动计划:
|
|||
|
|
- 更新 IMetadataApiService 接口,添加 deploy() 方法支持传入 destructiveChanges.xml
|
|||
|
|
- 更新 MetadataApiClient 实现,实现 deploy() 方法支持传入 destructiveChanges.xml
|
|||
|
|
- 更新 IMetadataApiService 接口,添加 rollback() 方法
|
|||
|
|
- 更新 MetadataApiClient 实现,实现 rollback() 方法
|
|||
|
|
- 创建数据库表 datai_destructive_change_rule
|
|||
|
|
- 编写单元测试和集成测试
|
|||
|
|
- 编写 API 文档和使用文档
|
|||
|
|
|
|||
|
|
- 需要跟进的事项:
|
|||
|
|
- 更新 Canvas,添加 Destructive Changes 相关的节点
|
|||
|
|
- 更新 CHANGELOG.md,记录本次变更
|
|||
|
|
- 创建复盘报告,总结本次迭代的经验和教训
|
|||
|
|
|
|||
|
|
## Design Update
|
|||
|
|
|
|||
|
|
- [x] 是否需要更新 Canvas? 是
|
|||
|
|
- [ ] Authentication.canvas - 需要添加 Destructive Changes 相关的节点
|
|||
|
|
- [ ] 其他 Canvas 文件: 无
|
|||
|
|
|
|||
|
|
## 复现步骤
|
|||
|
|
|
|||
|
|
提供复现本次会话结果的具体步骤:
|
|||
|
|
|
|||
|
|
1. 创建 DestructiveChangesController 控制器,提供 6 个 RESTful API 接口
|
|||
|
|
2. 创建 IDestructiveChangesService 服务接口,定义 Destructive Changes 服务接口方法
|
|||
|
|
3. 创建 DestructiveChangesServiceImpl 服务实现,实现 Destructive Changes 服务接口方法
|
|||
|
|
4. 创建 IDestructiveChangesConfigService 服务接口,定义 Destructive Changes 配置服务接口方法
|
|||
|
|
5. 创建 DestructiveChangesConfigServiceImpl 服务实现,实现 Destructive Changes 配置服务接口方法
|
|||
|
|
6. 创建 DestructiveChangesXmlParser 工具类,验证 destructiveChanges.xml 格式
|
|||
|
|
7. 创建 DestructiveChangesXmlBuilder 工具类,构建 destructiveChanges.xml
|
|||
|
|
8. 创建 DestructiveChangeRule 实体类和 DestructiveChangeRuleMapper 接口
|
|||
|
|
9. 创建 DestructiveChangesConfigRequest、DestructiveChangesConfigResponse、DestructiveChangesValidateRequest、DestructiveChangesValidateResponse、DestructiveChangesDeployRequest、DestructiveChangesDeployResponse、DestructiveChangesHistoryResponse、DestructiveChangesRollbackRequest、DestructiveChangesRollbackResponse 等 DTO 类
|
|||
|
|
10. 创建 DestructiveChangesException 异常类
|
|||
|
|
11. 更新 IMetadataApiService 接口,添加 deploy() 方法支持传入 destructiveChanges.xml
|
|||
|
|
12. 更新 MetadataApiClient 实现,实现 deploy() 方法支持传入 destructiveChanges.xml
|
|||
|
|
13. 更新 IMetadataApiService 接口,添加 rollback() 方法
|
|||
|
|
14. 更新 MetadataApiClient 实现,实现 rollback() 方法
|
|||
|
|
15. 创建数据库表 datai_destructive_change_rule
|
|||
|
|
16. 编写单元测试和集成测试
|
|||
|
|
17. 编写 API 文档和使用文档
|
|||
|
|
18. 验证 Destructive Changes 功能是否正常工作
|
|||
|
|
|
|||
|
|
**验证方法**:
|
|||
|
|
- 调用 POST /metadata/destructive-changes/config 接口,验证 destructiveChanges.xml 配置是否成功
|
|||
|
|
- 调用 GET /metadata/destructive-changes/config/{taskId} 接口,验证 destructiveChanges.xml 查询是否正常
|
|||
|
|
- 调用 POST /metadata/destructive-changes/validate 接口,验证 destructiveChanges.xml 验证是否正常
|
|||
|
|
- 调用 POST /metadata/destructive-changes/deploy 接口,验证 Destructive Changes 部署是否成功
|
|||
|
|
- 调用 GET /metadata/destructive-changes/history 接口,验证 Destructive Changes 历史记录查询是否正常
|
|||
|
|
- 调用 POST /metadata/destructive-changes/rollback/{jobId} 接口,验证 Destructive Changes 回滚是否正常
|