datai/docs/archive/prompts/004-metadata-task-management.md

856 lines
26 KiB
Markdown
Raw 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 - 元数据任务定义管理
## 输入引用
引用相关的 docs 文档链接:
- [REQ-010-4.md](../requirements/REQ-010-4.md) - 元数据任务定义管理需求文档
- [REQ-010-1.md](../requirements/REQ-010-1.md) - 数据库表结构设计和创建
- [REQ-010-2.md](../requirements/REQ-010-2.md) - 基础实体类和Mapper创建
- [REQ-010-3.md](../requirements/REQ-010-3.md) - Salesforce组织配置管理
- [0013-metadata-task-management.md](../decisions/adr/0013-metadata-task-management.md) - 元数据任务定义管理架构决策
## Context Maps
强制列出本次 Prompt 依赖的 Canvas 文件:
- [Authentication.canvas](../../Authentication.canvas) - 项目架构视觉化展示
- **相关节点**: [集成任务](node_integration_task) - 处理Salesforce的定时同步任务
## 目标
实现元数据任务定义的完整管理功能,包括:
1. 任务定义 CRUD 功能 - 支持分页查询、条件查询
2. package.xml 内容配置 - 支持可视化编辑,符合 Salesforce Metadata API 规范
3. API 版本选择 - 支持多种 API 版本
4. 调度类型选择 - 支持 Manual/Cron 调度类型
5. Cron 表达式配置 - 支持 Cron 表达式配置和验证
6. 任务验证功能 - 验证 package.xml 和调度配置
## 输出格式
### 代码输出格式
#### 1. 枚举类
```java
package com.datai.salesforce.metadata.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;
/**
* 调度类型枚举
*/
public enum ScheduleType {
MANUAL("manual", "手动执行"),
CRON("cron", "定时执行");
@EnumValue
private final String code;
@JsonValue
private final String displayName;
ScheduleType(String code, String displayName) {
this.code = code;
this.displayName = displayName;
}
public String getCode() {
return code;
}
public String getDisplayName() {
return displayName;
}
public static ScheduleType fromCode(String code) {
for (ScheduleType type : ScheduleType.values()) {
if (type.getCode().equals(code)) {
return type;
}
}
throw new IllegalArgumentException("Invalid schedule type code: " + code);
}
}
```
#### 2. package.xml 编辑器
```java
package com.datai.salesforce.metadata.util;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
/**
* package.xml 编辑器
*/
public class PackageXmlEditor {
private static final String PACKAGE_XML_HEADER = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Package xmlns=\"http://soap.sforce.com/2006/04/metadata\">\n</Package>";
/**
* 创建 package.xml
*/
public String createPackageXml() {
return PACKAGE_XML_HEADER;
}
/**
* 添加元数据类型
*/
public String addMetadataType(String packageXml, String metadataType) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new InputSource(new StringReader(packageXml)));
Element root = document.getDocumentElement();
Element typesElement = document.createElement("types");
root.appendChild(typesElement);
Element nameElement = document.createElement("name");
nameElement.setTextContent(metadataType);
typesElement.appendChild(nameElement);
return documentToString(document);
} catch (Exception e) {
throw new RuntimeException("Failed to add metadata type", e);
}
}
/**
* 添加元数据成员
*/
public String addMetadataMember(String packageXml, String metadataType, String member) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new InputSource(new StringReader(packageXml)));
Element root = document.getDocumentElement();
NodeList typesList = root.getElementsByTagName("types");
for (int i = 0; i < typesList.getLength(); i++) {
Element typesElement = (Element) typesList.item(i);
NodeList nameList = typesElement.getElementsByTagName("name");
if (nameList.getLength() > 0) {
Element nameElement = (Element) nameList.item(0);
if (nameElement.getTextContent().equals(metadataType)) {
Element membersElement = document.createElement("members");
membersElement.setTextContent(member);
typesElement.appendChild(membersElement);
break;
}
}
}
return documentToString(document);
} catch (Exception e) {
throw new RuntimeException("Failed to add metadata member", e);
}
}
/**
* 删除元数据类型
*/
public String removeMetadataType(String packageXml, String metadataType) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new InputSource(new StringReader(packageXml)));
Element root = document.getDocumentElement();
NodeList typesList = root.getElementsByTagName("types");
for (int i = 0; i < typesList.getLength(); i++) {
Element typesElement = (Element) typesList.item(i);
NodeList nameList = typesElement.getElementsByTagName("name");
if (nameList.getLength() > 0) {
Element nameElement = (Element) nameList.item(0);
if (nameElement.getTextContent().equals(metadataType)) {
root.removeChild(typesElement);
break;
}
}
}
return documentToString(document);
} catch (Exception e) {
throw new RuntimeException("Failed to remove metadata type", e);
}
}
/**
* 获取元数据类型列表
*/
public List<String> getMetadataTypes(String packageXml) {
List<String> metadataTypes = new ArrayList<>();
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(new InputSource(new StringReader(packageXml)));
Element root = document.getDocumentElement();
NodeList typesList = root.getElementsByTagName("types");
for (int i = 0; i < typesList.getLength(); i++) {
Element typesElement = (Element) typesList.item(i);
NodeList nameList = typesElement.getElementsByTagName("name");
if (nameList.getLength() > 0) {
Element nameElement = (Element) nameList.item(0);
metadataTypes.add(nameElement.getTextContent());
}
}
} catch (Exception e) {
throw new RuntimeException("Failed to get metadata types", e);
}
return metadataTypes;
}
/**
* 验证 package.xml
*/
public boolean validatePackageXml(String packageXml) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
builder.parse(new InputSource(new StringReader(packageXml)));
return true;
} catch (Exception e) {
return false;
}
}
private String documentToString(Document document) throws Exception {
TransformerFactory transformerFactory = TransformerFactory.newInstance();
Transformer transformer = transformerFactory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
StringWriter writer = new StringWriter();
transformer.transform(new DOMSource(document), new StreamResult(writer));
return writer.toString();
}
}
```
#### 3. Cron 表达式验证器
```java
package com.datai.salesforce.metadata.util;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
/**
* Cron 表达式验证器
*/
@Component
public class CronExpressionValidator {
/**
* 验证 Cron 表达式
*/
public ValidationResult validate(String cronExpression) {
ValidationResult result = new ValidationResult();
result.setValid(true);
if (cronExpression == null || cronExpression.trim().isEmpty()) {
result.setValid(false);
result.addError("Cron expression cannot be empty");
return result;
}
String[] parts = cronExpression.split("\\s+");
if (parts.length != 6) {
result.setValid(false);
result.addError("Cron expression must have 6 parts (second minute hour day month weekday)");
return result;
}
if (!validatePart(parts[0], 0, 59, "second")) {
result.setValid(false);
result.addError("Invalid second value");
}
if (!validatePart(parts[1], 0, 59, "minute")) {
result.setValid(false);
result.addError("Invalid minute value");
}
if (!validatePart(parts[2], 0, 23, "hour")) {
result.setValid(false);
result.addError("Invalid hour value");
}
if (!validatePart(parts[3], 1, 31, "day")) {
result.setValid(false);
result.addError("Invalid day value");
}
if (!validatePart(parts[4], 1, 12, "month")) {
result.setValid(false);
result.addError("Invalid month value");
}
if (!validatePart(parts[5], 0, 6, "weekday")) {
result.setValid(false);
result.addError("Invalid weekday value");
}
return result;
}
private boolean validatePart(String part, int min, int max, String partName) {
if (part.equals("*")) {
return true;
}
if (part.contains("/")) {
String[] rangeParts = part.split("/");
if (rangeParts.length != 2) {
return false;
}
try {
int step = Integer.parseInt(rangeParts[1]);
if (step <= 0) {
return false;
}
return validatePart(rangeParts[0], min, max, partName);
} catch (NumberFormatException e) {
return false;
}
}
if (part.contains("-")) {
String[] rangeParts = part.split("-");
if (rangeParts.length != 2) {
return false;
}
try {
int start = Integer.parseInt(rangeParts[0]);
int end = Integer.parseInt(rangeParts[1]);
return start >= min && end <= max && start <= end;
} catch (NumberFormatException e) {
return false;
}
}
if (part.contains(",")) {
String[] values = part.split(",");
for (String value : values) {
try {
int num = Integer.parseInt(value);
if (num < min || num > max) {
return false;
}
} catch (NumberFormatException e) {
return false;
}
}
return true;
}
try {
int num = Integer.parseInt(part);
return num >= min && num <= max;
} catch (NumberFormatException e) {
return false;
}
}
/**
* 验证结果
*/
public static class ValidationResult {
private boolean valid = true;
private List<String> errors = new ArrayList<>();
public boolean isValid() {
return valid;
}
public void setValid(boolean valid) {
this.valid = valid;
}
public List<String> getErrors() {
return errors;
}
public void addError(String error) {
this.valid = false;
this.errors.add(error);
}
}
}
```
#### 4. 控制器
```java
package com.datai.salesforce.metadata.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.datai.salesforce.metadata.entity.DataiMetaTask;
import com.datai.salesforce.metadata.service.IMetadataTaskService;
import com.datai.salesforce.metadata.service.ITaskValidationService;
import com.datai.salesforce.metadata.validation.TaskValidationResult;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
/**
* 元数据任务控制器
*/
@RestController
@RequestMapping("/api/metadata-task")
@RequiredArgsConstructor
@Validated
public class MetadataTaskController {
private final IMetadataTaskService metadataTaskService;
private final ITaskValidationService taskValidationService;
/**
* 创建元数据任务
*/
@PostMapping
public DataiMetaTask createMetadataTask(@Valid @RequestBody DataiMetaTask task) {
return metadataTaskService.createMetadataTask(task);
}
/**
* 更新元数据任务
*/
@PutMapping("/{id}")
public DataiMetaTask updateMetadataTask(@PathVariable Long id, @Valid @RequestBody DataiMetaTask task) {
task.setId(id);
return metadataTaskService.updateMetadataTask(task);
}
/**
* 删除元数据任务
*/
@DeleteMapping("/{id}")
public void deleteMetadataTask(@PathVariable Long id) {
metadataTaskService.deleteMetadataTask(id);
}
/**
* 查询元数据任务详情
*/
@GetMapping("/{id}")
public DataiMetaTask getMetadataTask(@PathVariable Long id) {
return metadataTaskService.getMetadataTask(id);
}
/**
* 查询元数据任务列表
*/
@GetMapping
public Page<DataiMetaTask> getMetadataTaskList(
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(required = false) String taskName,
@RequestParam(required = false) String scheduleType) {
return metadataTaskService.getMetadataTaskList(page, size, taskName, scheduleType);
}
/**
* 验证元数据任务
*/
@PostMapping("/{id}/validate")
public TaskValidationResult validateMetadataTask(@PathVariable Long id) {
return taskValidationService.validateMetadataTask(id);
}
}
```
#### 5. 服务接口
```java
package com.datai.salesforce.metadata.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.datai.salesforce.metadata.entity.DataiMetaTask;
/**
* 元数据任务服务接口
*/
public interface IMetadataTaskService {
/**
* 创建元数据任务
*/
DataiMetaTask createMetadataTask(DataiMetaTask task);
/**
* 更新元数据任务
*/
DataiMetaTask updateMetadataTask(DataiMetaTask task);
/**
* 删除元数据任务
*/
void deleteMetadataTask(Long id);
/**
* 查询元数据任务详情
*/
DataiMetaTask getMetadataTask(Long id);
/**
* 查询元数据任务列表
*/
Page<DataiMetaTask> getMetadataTaskList(int page, int size, String taskName, String scheduleType);
}
```
#### 6. 服务实现
```java
package com.datai.salesforce.metadata.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.datai.salesforce.metadata.entity.DataiMetaTask;
import com.datai.salesforce.metadata.enums.TaskStatus;
import com.datai.salesforce.metadata.mapper.DataiMetaTaskMapper;
import com.datai.salesforce.metadata.service.IMetadataTaskService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 元数据任务服务实现
*/
@Service
@RequiredArgsConstructor
public class MetadataTaskServiceImpl implements IMetadataTaskService {
private final DataiMetaTaskMapper taskMapper;
@Override
@Transactional
public DataiMetaTask createMetadataTask(DataiMetaTask task) {
task.setTaskStatus(TaskStatus.PENDING);
taskMapper.insert(task);
return task;
}
@Override
@Transactional
public DataiMetaTask updateMetadataTask(DataiMetaTask task) {
DataiMetaTask existing = taskMapper.selectById(task.getId());
if (existing == null) {
throw new RuntimeException("Task not found");
}
taskMapper.updateById(task);
return task;
}
@Override
@Transactional
public void deleteMetadataTask(Long id) {
taskMapper.deleteById(id);
}
@Override
public DataiMetaTask getMetadataTask(Long id) {
return taskMapper.selectById(id);
}
@Override
public Page<DataiMetaTask> getMetadataTaskList(int page, int size, String taskName, String scheduleType) {
QueryWrapper<DataiMetaTask> queryWrapper = new QueryWrapper<>();
if (taskName != null && !taskName.isEmpty()) {
queryWrapper.like("task_name", taskName);
}
if (scheduleType != null && !scheduleType.isEmpty()) {
queryWrapper.eq("schedule_type", scheduleType);
}
return taskMapper.selectPage(new Page<>(page, size), queryWrapper);
}
}
```
#### 7. 任务验证服务接口
```java
package com.datai.salesforce.metadata.service;
import com.datai.salesforce.metadata.validation.TaskValidationResult;
/**
* 任务验证服务接口
*/
public interface ITaskValidationService {
/**
* 验证元数据任务
*/
TaskValidationResult validateMetadataTask(Long id);
}
```
#### 8. 任务验证服务实现
```java
package com.datai.salesforce.metadata.service.impl;
import com.datai.salesforce.metadata.entity.DataiMetaTask;
import com.datai.salesforce.metadata.mapper.DataiMetaTaskMapper;
import com.datai.salesforce.metadata.service.ITaskValidationService;
import com.datai.salesforce.metadata.util.CronExpressionValidator;
import com.datai.salesforce.metadata.util.PackageXmlEditor;
import com.datai.salesforce.metadata.validation.TaskValidationResult;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
/**
* 任务验证服务实现
*/
@Service
@RequiredArgsConstructor
public class TaskValidationServiceImpl implements ITaskValidationService {
private final DataiMetaTaskMapper taskMapper;
private final PackageXmlEditor packageXmlEditor;
private final CronExpressionValidator cronExpressionValidator;
@Override
public TaskValidationResult validateMetadataTask(Long id) {
DataiMetaTask task = taskMapper.selectById(id);
if (task == null) {
return TaskValidationResult.failure("Task not found");
}
TaskValidationResult result = new TaskValidationResult();
if (!packageXmlEditor.validatePackageXml(task.getPackageXml())) {
result.addError("Invalid package.xml format");
}
if (task.getScheduleType().equals("cron")) {
CronExpressionValidator.ValidationResult cronResult = cronExpressionValidator.validate(task.getCronExpression());
if (!cronResult.isValid()) {
result.addErrors(cronResult.getErrors());
}
}
return result;
}
}
```
#### 9. 验证结果类
```java
package com.datai.salesforce.metadata.validation;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 任务验证结果
*/
@Data
public class TaskValidationResult {
private boolean valid = true;
private List<String> errors = new ArrayList<>();
public static TaskValidationResult success() {
return new TaskValidationResult();
}
public static TaskValidationResult failure(String error) {
TaskValidationResult result = new TaskValidationResult();
result.setValid(false);
result.addError(error);
return result;
}
public void addError(String error) {
this.valid = false;
this.errors.add(error);
}
public void addErrors(List<String> errors) {
this.valid = false;
this.errors.addAll(errors);
}
}
```
#### 10. 单元测试
```java
package com.datai.salesforce.metadata.service.impl;
import com.datai.salesforce.metadata.entity.DataiMetaTask;
import com.datai.salesforce.metadata.enums.TaskStatus;
import com.datai.salesforce.metadata.mapper.DataiMetaTaskMapper;
import com.datai.salesforce.metadata.service.IMetadataTaskService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class MetadataTaskServiceImplTest {
@Mock
private DataiMetaTaskMapper taskMapper;
@InjectMocks
private MetadataTaskServiceImpl metadataTaskService;
@Test
void testCreateMetadataTask() {
DataiMetaTask task = new DataiMetaTask();
task.setTaskName("Test Task");
task.setScheduleType("manual");
when(taskMapper.insert(any(DataiMetaTask.class))).thenReturn(1);
DataiMetaTask result = metadataTaskService.createMetadataTask(task);
assertNotNull(result);
assertEquals(TaskStatus.PENDING, result.getTaskStatus());
verify(taskMapper, times(1)).insert(any(DataiMetaTask.class));
}
@Test
void testGetMetadataTaskList() {
when(taskMapper.selectPage(any(), any())).thenReturn(new Page<>());
Page<DataiMetaTask> result = metadataTaskService.getMetadataTaskList(1, 10, null, null);
assertNotNull(result);
verify(taskMapper, times(1)).selectPage(any(), any());
}
}
```
## 约束
### 技术栈限制
- 必须基于现有的 Spring Boot 3 + Vue 3 技术栈
- 必须使用 MyBatis Plus 作为持久层框架
### 架构约束
- 必须遵循 Authentication.canvas 中定义的架构和调用关系
- 必须在 datai-salesforce-metadata 模块下实现
### API 约束
- 必须使用 RESTful API 设计接口
- 必须使用 @Valid 注解进行参数验证
- 必须支持分页查询和条件查询
### package.xml 约束
- package.xml 格式必须符合 Salesforce Metadata API 规范
- package.xml 必须支持多种元数据类型配置
- package.xml 必须支持通配符配置
### Cron 表达式约束
- Cron 表达式必须符合标准格式
- Cron 表达式必须支持秒、分、时、日、月、周
### 性能约束
- 元数据任务 CRUD 操作响应时间不超过 500ms
- package.xml 配置操作响应时间不超过 1s
- Cron 表达式验证操作响应时间不超过 500ms
- 任务验证操作响应时间不超过 1s
## Rule Set
"请严格参考 @Authentication.canvas 中的状态机转移逻辑,不要自行发挥。"
**具体规则**
- 必须使用 Canvas 中定义的类名和方法名
- 必须遵循 Canvas 中定义的调用关系
- 必须参考 Canvas 中的流程图逻辑
- 必须使用现有的集成核心功能进行 API 调用
- 必须遵循现有的异常处理机制
- 必须遵循现有的日志记录规范
## 验收标准
### 功能完整性
- 任务定义 CRUD 功能正常工作,能够成功创建、编辑、删除、查询元数据任务
- package.xml 内容配置正常工作,能够正确配置和验证 package.xml
- API 版本选择正常工作,能够正确选择和显示 API 版本
- 调度类型选择正常工作,能够正确选择和显示调度类型
- Cron 表达式配置正常工作,能够正确配置和验证 Cron 表达式
- 任务验证功能正常工作,能够正确验证 package.xml 和调度配置
### 代码规范性
- 代码符合项目编码规范,有清晰的注释
- 单元测试覆盖率不低于 80%
- 集成测试覆盖率不低于 60%
- 代码审查通过,没有严重的问题
### 性能指标
- 元数据任务 CRUD 操作响应时间不超过 500ms
- package.xml 配置操作响应时间不超过 1s
- Cron 表达式验证操作响应时间不超过 500ms
- 任务验证操作响应时间不超过 1s
## 风险
### 输出质量风险
- package.xml 验证不准确可能导致验证错误
- Cron 表达式解析不准确可能导致验证错误
- 任务验证逻辑复杂可能导致验证不准确
### 技术实现风险
- DOM 解析器内存占用高可能导致性能问题
- Cron 表达式解析库不支持某些格式可能导致验证错误
- 多任务管理复杂可能导致任务冲突
### 时间成本风险
- 功能复杂可能导致开发周期延长
- 测试覆盖面广可能导致测试时间增加
- 文档编写详细可能导致文档时间增加
## 使用记录
| 日期 | 使用场景 | 输入参数 | 输出结果 | 反馈 | 改进措施 |
|------|---------|---------|---------|------|----------|
| 2026-01-19 | 实现任务定义 CRUD 功能 | REQ-010-4 需求文档 | MetadataTaskController、IMetadataTaskService、MetadataTaskServiceImpl | 功能正常,符合需求 | 无 |
| 2026-01-19 | 实现 package.xml 内容配置 | REQ-010-4 需求文档 | PackageXmlEditor | 功能正常,符合需求 | 无 |
| 2026-01-19 | 实现 Cron 表达式配置 | REQ-010-4 需求文档 | CronExpressionValidator | 功能正常,符合需求 | 无 |
| 2026-01-19 | 实现任务验证功能 | REQ-010-4 需求文档 | ITaskValidationService、TaskValidationServiceImpl | 功能正常,符合需求 | 无 |