datai/datai-scenes/datai-scene-salesforce/docs/prompts/014-task-definition-management.md
Kris 2e6f087732 docs: 完成REQ-010-17和REQ-010-2的文档创建
- 完成REQ-010-17(性能优化和限流处理)的所有6个阶段
  - 创建ADR文档:0026-performance-optimization.md
  - 创建Prompt文档:027-performance-optimization.md
  - 创建会话记录:20260119-performance-optimization.md
  - 创建变更记录:20260119-performance-optimization.md
  - 创建复盘报告:20260119-performance-optimization-retro.md
  - 更新index.md和CHANGELOG.md

- 完成REQ-010-2(基础实体类和Mapper创建)的前3个阶段
  - 更新ADR文档:0011-entity-mapper-create.md
  - 创建Prompt文档:002-entity-mapper-create.md
  - 更新index.md

所有文档均按照SSOT方法论创建,包括需求定义、架构决策、提示词资产化、执行会话、变更记录和闭环复盘。
2026-01-19 10:06:09 +08:00

22 KiB

元数据任务定义管理 - 实现提示词

提示词信息

  • 提示词编号: Prompt-014
  • 创建日期: 2026-01-18
  • 相关需求: REQ-010-4 - 元数据任务定义管理
  • 相关决策: ADR-0013 - 元数据任务定义管理架构决策

输入引用

真源文档

技术规范

  • Spring Boot 3 + Vue 3 技术栈
  • MyBatis Plus 持久层框架
  • RESTful API 设计规范
  • 项目编码规范

提示词内容

角色设定

你是一个经验丰富的 Spring Boot 全栈开发工程师,专注于 Salesforce 元数据管理系统的开发。你熟悉以下技术栈:

  • 后端: Spring Boot 3, MyBatis Plus, MySQL
  • 前端: Vue 3, Element Plus
  • Salesforce API: Metadata API, Partner API
  • 工具: Maven, Git, Postman

任务目标

实现元数据任务定义管理功能,包括:

  1. 任务定义 CRUD 功能

    • 创建元数据任务
    • 编辑元数据任务
    • 删除元数据任务
    • 查询元数据任务列表(支持分页和条件查询)
  2. package.xml 内容配置

    • 可视化编辑 package.xml 内容
    • package.xml 格式验证
    • 支持多种元数据类型配置
    • 支持通配符配置
  3. API 版本选择

    • API 版本列表展示
    • API 版本验证
    • API 版本友好显示
  4. 调度类型选择

    • 支持 Manual 和 Cron 两种调度类型
    • 调度类型验证
    • 调度类型友好显示
  5. Cron 表达式配置

    • Cron 表达式编辑
    • Cron 表达式验证
    • Cron 表达式预览
  6. 任务验证功能

    • package.xml 格式验证
    • Cron 表达式验证
    • 验证失败返回详细错误信息

实现要求

1. 数据模型

根据 ADR-0013 中定义的数据模型,创建以下实体类:

@Data
@TableName("datai_meta_task")
public class DataiMetaTask {
    @TableId(type = IdType.AUTO)
    private Long id;
    
    private String taskName;
    
    private String taskType;
    
    private Long orgConfigId;
    
    @TableField(type = DbType.TEXT)
    private String packageXml;
    
    private String apiVersion;
    
    private String scheduleType;
    
    private String cronExpression;
    
    private String status;
    
    private String description;
    
    private String createdBy;
    
    private LocalDateTime createdTime;
    
    private String updatedBy;
    
    private LocalDateTime updatedTime;
}

2. Mapper 接口

@Mapper
public interface DataiMetaTaskMapper extends BaseMapper<DataiMetaTask> {
    
    /**
     * 查询任务列表
     */
    IPage<DataiMetaTask> selectTaskPage(Page<DataiMetaTask> page, @Param("taskName") String taskName, 
                                       @Param("taskType") String taskType, @Param("status") String status);
    
    /**
     * 根据组织配置ID查询任务
     */
    List<DataiMetaTask> selectByOrgConfigId(@Param("orgConfigId") Long orgConfigId);
}

3. Service 层

@Service
public class DataiMetaTaskServiceImpl implements IDataiMetaTaskService {
    
    @Autowired
    private DataiMetaTaskMapper taskMapper;
    
    @Autowired
    private PackageXmlValidator packageXmlValidator;
    
    @Autowired
    private CronExpressionValidator cronValidator;
    
    /**
     * 创建任务
     */
    @Override
    @Transactional
    public Long createTask(DataiMetaTask task) {
        // 验证 package.xml
        ValidationResult xmlValidation = packageXmlValidator.validate(task.getPackageXml());
        if (!xmlValidation.isSuccess()) {
            throw new BusinessException(xmlValidation.getErrorMessage());
        }
        
        // 验证 Cron 表达式(如果是 Cron 调度)
        if ("Cron".equals(task.getScheduleType())) {
            ValidationResult cronValidation = cronValidator.validate(task.getCronExpression());
            if (!cronValidation.isSuccess()) {
                throw new BusinessException(cronValidation.getErrorMessage());
            }
        }
        
        // 设置默认状态
        task.setStatus("Active");
        task.setCreatedTime(LocalDateTime.now());
        task.setUpdatedTime(LocalDateTime.now());
        
        // 保存任务
        taskMapper.insert(task);
        
        return task.getId();
    }
    
    /**
     * 更新任务
     */
    @Override
    @Transactional
    public void updateTask(DataiMetaTask task) {
        // 验证 package.xml
        ValidationResult xmlValidation = packageXmlValidator.validate(task.getPackageXml());
        if (!xmlValidation.isSuccess()) {
            throw new BusinessException(xmlValidation.getErrorMessage());
        }
        
        // 验证 Cron 表达式(如果是 Cron 调度)
        if ("Cron".equals(task.getScheduleType())) {
            ValidationResult cronValidation = cronValidator.validate(task.getCronExpression());
            if (!cronValidation.isSuccess()) {
                throw new BusinessException(cronValidation.getErrorMessage());
            }
        }
        
        // 更新任务
        task.setUpdatedTime(LocalDateTime.now());
        taskMapper.updateById(task);
    }
    
    /**
     * 删除任务
     */
    @Override
    @Transactional
    public void deleteTask(Long id) {
        taskMapper.deleteById(id);
    }
    
    /**
     * 查询任务列表
     */
    @Override
    public IPage<DataiMetaTask> selectTaskPage(Integer pageNum, Integer pageSize, String taskName, 
                                             String taskType, String status) {
        Page<DataiMetaTask> page = new Page<>(pageNum, pageSize);
        QueryWrapper<DataiMetaTask> wrapper = new QueryWrapper<>();
        
        if (StringUtils.isNotBlank(taskName)) {
            wrapper.like("taskName", taskName);
        }
        if (StringUtils.isNotBlank(taskType)) {
            wrapper.eq("taskType", taskType);
        }
        if (StringUtils.isNotBlank(status)) {
            wrapper.eq("status", status);
        }
        
        wrapper.orderByDesc("createdTime");
        
        return taskMapper.selectTaskPage(page, taskName, taskType, status);
    }
    
    /**
     * 验证任务
     */
    @Override
    public ValidationResult validateTask(DataiMetaTask task) {
        // 验证 package.xml
        ValidationResult xmlValidation = packageXmlValidator.validate(task.getPackageXml());
        if (!xmlValidation.isSuccess()) {
            return xmlValidation;
        }
        
        // 验证 Cron 表达式(如果是 Cron 调度)
        if ("Cron".equals(task.getScheduleType())) {
            ValidationResult cronValidation = cronValidator.validate(task.getCronExpression());
            if (!cronValidation.isSuccess()) {
                return cronValidation;
            }
        }
        
        return ValidationResult.success();
    }
}

4. Controller 层

@RestController
@RequestMapping("/metadata/task")
public class DataiMetaTaskController {
    
    @Autowired
    private IDataiMetaTaskService taskService;
    
    /**
     * 创建任务
     */
    @PostMapping
    public Result<Long> createTask(@Valid @RequestBody DataiMetaTask task) {
        Long taskId = taskService.createTask(task);
        return Result.success(taskId);
    }
    
    /**
     * 更新任务
     */
    @PutMapping("/{id}")
    public Result<Void> updateTask(@PathVariable Long id, @Valid @RequestBody DataiMetaTask task) {
        task.setId(id);
        taskService.updateTask(task);
        return Result.success();
    }
    
    /**
     * 删除任务
     */
    @DeleteMapping("/{id}")
    public Result<Void> deleteTask(@PathVariable Long id) {
        taskService.deleteTask(id);
        return Result.success();
    }
    
    /**
     * 查询任务列表
     */
    @GetMapping("/list")
    public Result<IPage<DataiMetaTask>> list(
            @RequestParam(defaultValue = "1") Integer pageNum,
            @RequestParam(defaultValue = "10") Integer pageSize,
            @RequestParam(required = false) String taskName,
            @RequestParam(required = false) String taskType,
            @RequestParam(required = false) String status) {
        IPage<DataiMetaTask> page = taskService.selectTaskPage(pageNum, pageSize, taskName, taskType, status);
        return Result.success(page);
    }
    
    /**
     * 查询任务详情
     */
    @GetMapping("/{id}")
    public Result<DataiMetaTask> getTask(@PathVariable Long id) {
        DataiMetaTask task = taskService.getById(id);
        return Result.success(task);
    }
    
    /**
     * 验证任务
     */
    @PostMapping("/{id}/validate")
    public Result<ValidationResult> validateTask(@PathVariable Long id) {
        DataiMetaTask task = taskService.getById(id);
        ValidationResult result = taskService.validateTask(task);
        return Result.success(result);
    }
}

5. package.xml 验证器

@Component
public class PackageXmlValidator {
    
    private static final List<String> SUPPORTED_METADATA_TYPES = Arrays.asList(
        "CustomObject", "CustomField", "ApexClass", "ApexTrigger",
        "Layout", "Workflow", "ValidationRule", "Flow"
    );
    
    public ValidationResult validate(String packageXml) {
        try {
            // 解析 XML
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse(new InputSource(new StringReader(packageXml)));
            
            // 验证根元素
            NodeList types = doc.getElementsByTagName("types");
            if (types.getLength() == 0) {
                return ValidationResult.error("package.xml 必须包含 types 元素");
            }
            
            // 验证元数据类型
            for (int i = 0; i < types.getLength(); i++) {
                Element type = (Element) types.item(i);
                String typeName = type.getTextContent();
                if (!isSupportedMetadataType(typeName)) {
                    return ValidationResult.error("不支持的元数据类型: " + typeName);
                }
            }
            
            return ValidationResult.success();
        } catch (Exception e) {
            return ValidationResult.error("package.xml 格式错误: " + e.getMessage());
        }
    }
    
    private boolean isSupportedMetadataType(String typeName) {
        return SUPPORTED_METADATA_TYPES.contains(typeName);
    }
}

6. Cron 表达式验证器

@Component
public class CronExpressionValidator {
    
    public ValidationResult validate(String cronExpression) {
        try {
            // 使用 Quartz 的 CronExpression 验证
            CronExpression cron = new CronExpression(cronExpression);
            
            // 验证是否有效
            if (!cron.isValid()) {
                return ValidationResult.error("Cron 表达式格式错误");
            }
            
            // 验证是否能够计算下次执行时间
            Date nextValidTime = cron.getNextValidTimeAfter(new Date());
            if (nextValidTime == null) {
                return ValidationResult.error("Cron 表达式无法计算下次执行时间");
            }
            
            return ValidationResult.success(nextValidTime);
        } catch (Exception e) {
            return ValidationResult.error("Cron 表达式解析错误: " + e.getMessage());
        }
    }
}

7. 前端组件

<template>
  <div class="task-manager">
    <el-card>
      <template #header>
        <span>元数据任务管理</span>
        <el-button type="primary" @click="showCreateDialog">创建任务</el-button>
      </template>
      
      <!-- 搜索表单 -->
      <el-form :inline="true" :model="searchForm">
        <el-form-item label="任务名称">
          <el-input v-model="searchForm.taskName" placeholder="请输入任务名称" />
        </el-form-item>
        <el-form-item label="任务类型">
          <el-select v-model="searchForm.taskType" placeholder="请选择任务类型">
            <el-option label="拉取" value="retrieve" />
            <el-option label="部署" value="deploy" />
          </el-select>
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="handleSearch">查询</el-button>
          <el-button @click="handleReset">重置</el-button>
        </el-form-item>
      </el-form>
      
      <!-- 任务列表 -->
      <el-table :data="taskList" border>
        <el-table-column prop="taskName" label="任务名称" />
        <el-table-column prop="taskType" label="任务类型">
          <template #default="{ row }">
            <el-tag v-if="row.taskType === 'retrieve'" type="success">拉取</el-tag>
            <el-tag v-else type="warning">部署</el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="scheduleType" label="调度类型" />
        <el-table-column prop="status" label="状态">
          <template #default="{ row }">
            <el-tag v-if="row.status === 'Active'" type="success">启用</el-tag>
            <el-tag v-else type="info">禁用</el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="createdTime" label="创建时间" />
        <el-table-column label="操作" width="300">
          <template #default="{ row }">
            <el-button size="small" @click="handleEdit(row)">编辑</el-button>
            <el-button size="small" type="danger" @click="handleDelete(row)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
      
      <!-- 分页 -->
      <el-pagination
        v-model:current-page="pagination.pageNum"
        v-model:page-size="pagination.pageSize"
        :total="pagination.total"
        @current-change="handlePageChange"
        @size-change="handleSizeChange"
      />
    </el-card>
    
    <!-- 创建/编辑对话框 -->
    <el-dialog v-model="dialogVisible" :title="dialogTitle" width="80%">
      <el-form :model="taskForm" :rules="formRules" ref="taskFormRef">
        <el-form-item label="任务名称" prop="taskName">
          <el-input v-model="taskForm.taskName" placeholder="请输入任务名称" />
        </el-form-item>
        <el-form-item label="任务类型" prop="taskType">
          <el-select v-model="taskForm.taskType" placeholder="请选择任务类型">
            <el-option label="拉取" value="retrieve" />
            <el-option label="部署" value="deploy" />
          </el-select>
        </el-form-item>
        <el-form-item label="组织配置" prop="orgConfigId">
          <el-select v-model="taskForm.orgConfigId" placeholder="请选择组织配置">
            <el-option
              v-for="org in orgList"
              :key="org.id"
              :label="org.orgName"
              :value="org.id"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="package.xml" prop="packageXml">
          <el-input
            v-model="taskForm.packageXml"
            type="textarea"
            :rows="10"
            placeholder="请输入 package.xml 内容"
          />
        </el-form-item>
        <el-form-item label="API 版本" prop="apiVersion">
          <el-select v-model="taskForm.apiVersion" placeholder="请选择 API 版本">
            <el-option label="58.0" value="58.0" />
            <el-option label="57.0" value="57.0" />
            <el-option label="56.0" value="56.0" />
          </el-select>
        </el-form-item>
        <el-form-item label="调度类型" prop="scheduleType">
          <el-select v-model="taskForm.scheduleType" placeholder="请选择调度类型">
            <el-option label="手动" value="Manual" />
            <el-option label="Cron" value="Cron" />
          </el-select>
        </el-form-item>
        <el-form-item label="Cron 表达式" prop="cronExpression" v-if="taskForm.scheduleType === 'Cron'">
          <el-input v-model="taskForm.cronExpression" placeholder="请输入 Cron 表达式" />
          <div class="cron-preview">
            下次执行时间: {{ cronPreview }}
          </div>
        </el-form-item>
        <el-form-item label="任务描述">
          <el-input
            v-model="taskForm.description"
            type="textarea"
            :rows="3"
            placeholder="请输入任务描述"
          />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="dialogVisible = false">取消</el-button>
        <el-button type="primary" @click="handleSave">保存</el-button>
        <el-button type="info" @click="handleValidate">验证</el-button>
      </template>
    </el-dialog>
  </div>
</template>

<script setup>
import { ref, reactive, computed } from 'vue';
import { ElMessage } from 'element-plus';

const taskList = ref([]);
const dialogVisible = ref(false);
const dialogTitle = ref('创建任务');
const taskFormRef = ref(null);
const searchForm = reactive({
  taskName: '',
  taskType: '',
  status: ''
});
const taskForm = reactive({
  id: null,
  taskName: '',
  taskType: 'retrieve',
  orgConfigId: null,
  packageXml: '',
  apiVersion: '58.0',
  scheduleType: 'Manual',
  cronExpression: '',
  description: ''
});
const pagination = reactive({
  pageNum: 1,
  pageSize: 10,
  total: 0
});
const orgList = ref([]);

const cronPreview = computed(() => {
  // 计算 Cron 表达式的下次执行时间
  if (taskForm.scheduleType === 'Cron' && taskForm.cronExpression) {
    // 调用后端 API 计算
    return '计算中...';
  }
  return '';
});

const formRules = {
  taskName: [{ required: true, message: '请输入任务名称', trigger: 'blur' }],
  taskType: [{ required: true, message: '请选择任务类型', trigger: 'change' }],
  orgConfigId: [{ required: true, message: '请选择组织配置', trigger: 'change' }],
  packageXml: [{ required: true, message: '请输入 package.xml 内容', trigger: 'blur' }],
  apiVersion: [{ required: true, message: '请选择 API 版本', trigger: 'change' }],
  scheduleType: [{ required: true, message: '请选择调度类型', trigger: 'change' }],
  cronExpression: [{ required: true, message: '请输入 Cron 表达式', trigger: 'blur' }]
};

const handleSearch = () => {
  // 查询任务列表
};

const handleReset = () => {
  searchForm.taskName = '';
  searchForm.taskType = '';
  searchForm.status = '';
  handleSearch();
};

const showCreateDialog = () => {
  dialogTitle.value = '创建任务';
  Object.assign(taskForm, {
    id: null,
    taskName: '',
    taskType: 'retrieve',
    orgConfigId: null,
    packageXml: '',
    apiVersion: '58.0',
    scheduleType: 'Manual',
    cronExpression: '',
    description: ''
  });
  dialogVisible.value = true;
};

const handleEdit = (row) => {
  dialogTitle.value = '编辑任务';
  Object.assign(taskForm, row);
  dialogVisible.value = true;
};

const handleDelete = (row) => {
  ElMessage.confirm('确定要删除该任务吗?', '提示', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
  }).then(() => {
    // 调用删除 API
    ElMessage.success('删除成功');
    handleSearch();
  });
};

const handleSave = () => {
  taskFormRef.value.validate((valid) => {
    if (valid) {
      // 调用保存 API
      ElMessage.success('保存成功');
      dialogVisible.value = false;
      handleSearch();
    }
  });
};

const handleValidate = () => {
  // 调用验证 API
};

const handlePageChange = (pageNum) => {
  pagination.pageNum = pageNum;
  handleSearch();
};

const handleSizeChange = (pageSize) => {
  pagination.pageSize = pageSize;
  handleSearch();
};
</script>

<style scoped>
.task-manager {
  padding: 20px;
}
.cron-preview {
  margin-top: 10px;
  padding: 10px;
  background-color: #f5f7fa;
  border-radius: 4px;
}
</style>

输出格式

1. 代码结构

datai-salesforce-metadata/
├── src/main/java/com/datai/metadata/
│   ├── controller/
│   │   └── DataiMetaTaskController.java
│   ├── service/
│   │   ├── IDataiMetaTaskService.java
│   │   └── impl/
│   │       └── DataiMetaTaskServiceImpl.java
│   ├── mapper/
│   │   └── DataiMetaTaskMapper.java
│   ├── entity/
│   │   └── DataiMetaTask.java
│   └── validator/
│       ├── PackageXmlValidator.java
│       └── CronExpressionValidator.java
└── src/main/resources/
    └── mapper/
        └── DataiMetaTaskMapper.xml

2. 单元测试

必须为以下类编写单元测试:

  • DataiMetaTaskServiceImpl
  • PackageXmlValidator
  • CronExpressionValidator
  • DataiMetaTaskController

3. 集成测试

必须编写以下集成测试:

  • 任务 CRUD 操作
  • package.xml 验证
  • Cron 表达式验证
  • API 接口测试

4. API 文档

必须为以下接口编写 API 文档:

  • POST /metadata/task - 创建任务
  • PUT /metadata/task/{id} - 更新任务
  • DELETE /metadata/task/{id} - 删除任务
  • GET /metadata/task/list - 查询任务列表
  • GET /metadata/task/{id} - 查询任务详情
  • POST /metadata/task/{id}/validate - 验证任务

注意事项

  1. package.xml 存储: 使用 TEXT 类型存储,避免 VARCHAR 长度限制
  2. Cron 表达式: 使用 Quartz 库进行验证,确保格式正确
  3. 事务管理: 创建、更新、删除操作必须使用 @Transactional 注解
  4. 参数验证: 使用 @Valid 注解进行参数验证
  5. 异常处理: 使用统一的异常处理机制,返回友好的错误信息
  6. 日志记录: 记录关键操作的日志,便于问题排查
  7. 权限控制: 根据用户权限控制任务的访问和操作

参考资料