datai/docs/archive/decisions/adr/0013-task-definition-management.md

12 KiB
Raw Permalink Blame History

Architecture Decision Record - 元数据任务定义管理

元数据

  • ADR 编号: ADR-0013
  • 创建日期: 2026-01-18
  • 状态: 已接受
  • 决策者: Datai Team
  • 相关需求: REQ-010-4 - 元数据任务定义管理

上下文和问题陈述

背景

REQ-010-4 需求要求实现元数据任务定义的完整管理功能包括任务的增删改查、package.xml内容配置、API版本选择、调度类型选择、Cron表达式配置、任务验证等。

问题

需要设计元数据任务定义管理的技术方案,解决以下关键问题:

  1. 数据模型设计: 如何设计元数据任务定义的数据模型支持任务配置、package.xml内容、API版本、调度类型等
  2. package.xml管理: 如何管理和验证package.xml内容确保符合Salesforce Metadata API规范
  3. 调度配置: 如何支持Manual和Cron两种调度类型如何验证Cron表达式
  4. 任务验证: 如何验证package.xml和调度配置确保任务可执行
  5. API设计: 如何设计RESTful API接口支持任务的CRUD操作

决策驱动因素

技术约束

  • 必须基于现有的Spring Boot 3 + Vue 3技术栈
  • 必须使用MyBatis Plus作为持久层框架
  • 必须遵循Authentication.canvas中定义的架构和调用关系
  • 必须在datai-salesforce-metadata模块下实现

业务需求

  • 支持任务的增删改查功能
  • 支持package.xml内容配置和验证
  • 支持API版本选择和验证
  • 支持调度类型选择Manual/Cron
  • 支持Cron表达式配置和验证
  • 支持任务验证功能

非功能需求

  • 代码符合项目编码规范,有清晰的注释
  • 操作界面友好易用,提供清晰的错误信息
  • 代码结构清晰,易于扩展和维护

考虑的选项

选项1使用XML文件存储package.xml

描述: 将package.xml内容作为XML字符串存储在数据库中使用XML解析库进行验证。

优点:

  • 实现简单直接存储XML内容
  • 易于验证XML格式
  • 符合Salesforce Metadata API规范

缺点:

  • XML字符串可能很长占用数据库空间
  • 需要额外的XML解析库
  • 难以可视化编辑

风险评估: 中

选项2使用JSON格式存储package.xml配置

描述: 将package.xml内容解析为JSON格式存储在数据库中使用时再转换为XML。

优点:

  • JSON格式更易于处理和验证
  • 可以使用现有的JSON工具库
  • 便于前端可视化编辑

缺点:

  • 需要JSON和XML之间的转换
  • 可能丢失XML的某些特性
  • 转换过程可能引入错误

风险评估: 中

选项3使用结构化数据存储package.xml

描述: 将package.xml内容解析为结构化数据元数据类型列表存储在关联表中。

优点:

  • 数据结构清晰,易于查询和管理
  • 便于前端可视化编辑
  • 可以支持更复杂的验证逻辑

缺点:

  • 实现复杂,需要设计多个关联表
  • 需要处理XML和结构化数据之间的转换
  • 可能无法支持所有XML特性

风险评估: 高

决策结果

选定方案选项1 - 使用XML文件存储package.xml

理由:

  1. 符合规范: 直接存储XML内容完全符合Salesforce Metadata API规范避免转换过程中的错误
  2. 实现简单: 实现复杂度最低,开发周期短
  3. 验证准确: 可以直接使用XML解析库验证XML格式验证结果准确
  4. 扩展性好: 未来如果需要支持更复杂的配置,可以在此基础上扩展

拒绝其他选项的理由

  • 选项2: JSON和XML之间的转换可能引入错误且无法完全保留XML的所有特性
  • 选项3: 实现复杂度高开发周期长且可能无法支持所有XML特性

技术方案

数据模型设计

主表datai_meta_task

CREATE TABLE `datai_meta_task` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '任务ID',
  `task_name` varchar(255) NOT NULL COMMENT '任务名称',
  `task_type` varchar(50) NOT NULL COMMENT '任务类型retrieve/deploy',
  `org_config_id` bigint NOT NULL COMMENT '组织配置ID',
  `package_xml` longtext COMMENT 'package.xml内容',
  `api_version` varchar(20) NOT NULL COMMENT 'API版本',
  `schedule_type` varchar(20) NOT NULL COMMENT '调度类型Manual/Cron',
  `cron_expression` varchar(100) COMMENT 'Cron表达式',
  `status` varchar(20) NOT NULL COMMENT '任务状态Active/Inactive',
  `description` varchar(500) COMMENT '任务描述',
  `created_by` varchar(100) COMMENT '创建人',
  `created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updated_by` varchar(100) COMMENT '更新人',
  `updated_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `idx_org_config_id` (`org_config_id`),
  KEY `idx_task_type` (`task_type`),
  KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='元数据任务定义表';

API设计

1. 创建任务

  • 接口路径: POST /metadata/task
  • 请求参数:
    {
      "taskName": "任务名称",
      "taskType": "retrieve",
      "orgConfigId": 1,
      "packageXml": "<?xml version=\"1.0\"...",
      "apiVersion": "58.0",
      "scheduleType": "Cron",
      "cronExpression": "0 0 0 * * ?",
      "description": "任务描述"
    }
    
  • 响应: 返回创建的任务ID

2. 查询任务列表

  • 接口路径: GET /metadata/task/list
  • 请求参数:
    • pageNum: 页码
    • pageSize: 每页大小
    • taskName: 任务名称(可选)
    • taskType: 任务类型(可选)
    • status: 任务状态(可选)
  • 响应: 返回任务列表和分页信息

3. 查询任务详情

  • 接口路径: GET /metadata/task/{id}
  • 响应: 返回任务详细信息

4. 更新任务

  • 接口路径: PUT /metadata/task/{id}
  • 请求参数: 同创建任务
  • 响应: 返回更新结果

5. 删除任务

  • 接口路径: DELETE /metadata/task/{id}
  • 响应: 返回删除结果

6. 验证任务

  • 接口路径: POST /metadata/task/{id}/validate
  • 响应: 返回验证结果

package.xml验证

验证规则

  1. 格式验证: 使用XML解析库验证XML格式是否正确
  2. 元数据类型验证: 验证package.xml中配置的元数据类型是否在支持的类型列表中
  3. 通配符验证: 验证通配符使用是否正确
  4. API版本验证: 验证API版本是否在支持的版本列表中

验证实现

public class PackageXmlValidator {
    
    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 MetadataTypeRegistry.contains(typeName);
    }
}

Cron表达式验证

验证规则

  1. 格式验证: 使用Cron表达式解析库验证Cron表达式格式是否正确
  2. 解析验证: 验证Cron表达式是否能够正确解析
  3. 预览验证: 提供Cron表达式预览显示下次执行时间

验证实现

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());
        }
    }
}

调度类型管理

枚举定义

public enum ScheduleType {
    MANUAL("Manual", "手动调度"),
    CRON("Cron", "Cron调度");
    
    private final String code;
    private final String description;
    
    ScheduleType(String code, String description) {
        this.code = code;
        this.description = description;
    }
    
    public String getCode() {
        return code;
    }
    
    public String getDescription() {
        return description;
    }
}

API版本管理

版本列表

public class MetadataApiVersion {
    
    private static final List<String> SUPPORTED_VERSIONS = Arrays.asList(
        "58.0", "57.0", "56.0", "55.0", "54.0"
    );
    
    public static List<String> getSupportedVersions() {
        return SUPPORTED_VERSIONS;
    }
    
    public static boolean isSupported(String version) {
        return SUPPORTED_VERSIONS.contains(version);
    }
}

后果

正面影响

  1. 开发效率: 实现简单,开发周期短
  2. 维护性: 代码结构清晰,易于维护
  3. 扩展性: 未来可以在此基础上扩展更多功能
  4. 准确性: 直接存储XML避免转换错误

负面影响

  1. 存储空间: XML字符串可能占用较多数据库空间
  2. 可视化: 需要额外的XML编辑器组件支持可视化编辑
  3. 查询性能: XML字符串查询可能不如结构化数据高效

回滚策略

如果本决策在实践中出现问题可以考虑回滚到选项2或选项3

  1. 回滚条件: 如果XML字符串存储导致严重的性能问题或存储空间不足
  2. 回滚步骤:
    • 设计结构化数据模型
    • 实现XML和结构化数据之间的转换
    • 迁移现有数据
  3. 回滚风险: 数据迁移可能引入新的错误

相关文档

审核记录

日期 审核人 审核结果 审核意见
2026-01-18 Datai Team 已接受 方案合理,可以实施

变更历史

日期 版本 变更内容 变更人
2026-01-18 v1.0.0 初始版本 Datai Team