datai/datai-scenes/datai-scene-salesforce/docs/prompts/021-metadata-component-index.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

25 KiB
Raw Blame History

Prompt - 元数据组件索引和查询

输入引用

引用相关的 docs 文档链接:

Context Maps

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

目标

实现元数据组件索引和查询功能,包括组件索引创建、组件查询、组件详情查看、组件统计。支持按类型、名称、版本等条件查询元数据组件。

输出格式

代码结构

datai-salesforce-metadata/
├── component-index/
│   ├── controller/
│   │   └── MetadataComponentIndexController.java
│   ├── service/
│   │   ├── IMetadataComponentIndexService.java
│   │   ├── MetadataComponentIndexServiceImpl.java
│   │   ├── IMetadataComponentQueryService.java
│   │   ├── MetadataComponentQueryServiceImpl.java
│   │   ├── IMetadataComponentDetailService.java
│   │   ├── MetadataComponentDetailServiceImpl.java
│   │   ├── IMetadataComponentStatisticsService.java
│   │   └── MetadataComponentStatisticsServiceImpl.java
│   ├── entity/
│   │   └── MetadataComponentIndex.java
│   ├── mapper/
│   │   └── MetadataComponentIndexMapper.java
│   ├── dto/
│   │   ├── ComponentIndexRequest.java
│   │   ├── ComponentIndexResponse.java
│   │   ├── ComponentQueryRequest.java
│   │   ├── ComponentQueryResponse.java
│   │   ├── ComponentDetailResponse.java
│   │   ├── ComponentStatisticsResponse.java
│   │   ├── ComponentTypeDistributionResponse.java
│   │   └── ComponentVersionDistributionResponse.java
│   ├── parser/
│   │   ├── MetadataComponentParser.java
│   │   ├── ApexClassParser.java
│   │   ├── VisualforcePageParser.java
│   │   ├── CustomObjectParser.java
│   │   └── CustomFieldParser.java
│   └── exception/
│       └── ComponentIndexException.java

代码示例语言Java

1. MetadataComponentIndexController 控制器

package com.datai.salesforce.metadata.component.controller;

import com.datai.salesforce.metadata.component.dto.*;
import com.datai.salesforce.metadata.component.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/metadata/component")
public class MetadataComponentIndexController {

    @Autowired
    private IMetadataComponentIndexService componentIndexService;

    @Autowired
    private IMetadataComponentQueryService componentQueryService;

    @Autowired
    private IMetadataComponentDetailService componentDetailService;

    @Autowired
    private IMetadataComponentStatisticsService componentStatisticsService;

    @PostMapping("/index")
    public ComponentIndexResponse createIndex(@RequestBody ComponentIndexRequest request) {
        return componentIndexService.createIndex(request);
    }

    @GetMapping("/query")
    public ComponentQueryResponse queryComponents(
            @RequestParam(required = false) String type,
            @RequestParam(required = false) String name,
            @RequestParam(required = false) String version,
            @RequestParam(defaultValue = "1") int page,
            @RequestParam(defaultValue = "10") int size) {
        ComponentQueryRequest request = new ComponentQueryRequest();
        request.setType(type);
        request.setName(name);
        request.setVersion(version);
        request.setPage(page);
        request.setSize(size);
        return componentQueryService.queryComponents(request);
    }

    @GetMapping("/detail/{id}")
    public ComponentDetailResponse getComponentDetail(@PathVariable Long id) {
        return componentDetailService.getComponentDetail(id);
    }

    @GetMapping("/detail/{id}/content")
    public String getComponentContent(@PathVariable Long id) {
        return componentDetailService.getComponentContent(id);
    }

    @GetMapping("/detail/{id}/dependencies")
    public ComponentDetailResponse getComponentDependencies(@PathVariable Long id) {
        return componentDetailService.getComponentDependencies(id);
    }

    @GetMapping("/statistics")
    public ComponentStatisticsResponse getStatistics(
            @RequestParam(required = false) String type,
            @RequestParam(required = false) String version,
            @RequestParam(required = false) Long orgConfigId) {
        return componentStatisticsService.getStatistics(type, version, orgConfigId);
    }

    @GetMapping("/statistics/type-distribution")
    public ComponentTypeDistributionResponse getTypeDistribution(
            @RequestParam(required = false) Long orgConfigId) {
        return componentStatisticsService.getTypeDistribution(orgConfigId);
    }

    @GetMapping("/statistics/version-distribution")
    public ComponentVersionDistributionResponse getVersionDistribution(
            @RequestParam(required = false) Long orgConfigId) {
        return componentStatisticsService.getVersionDistribution(orgConfigId);
    }
}

2. IMetadataComponentIndexService 服务接口

package com.datai.salesforce.metadata.component.service;

import com.datai.salesforce.metadata.component.dto.ComponentIndexRequest;
import com.datai.salesforce.metadata.component.dto.ComponentIndexResponse;

public interface IMetadataComponentIndexService {
    
    ComponentIndexResponse createIndex(ComponentIndexRequest request);
}

3. MetadataComponentIndexServiceImpl 服务实现

package com.datai.salesforce.metadata.component.service;

import com.datai.salesforce.metadata.component.dto.ComponentIndexRequest;
import com.datai.salesforce.metadata.component.dto.ComponentIndexResponse;
import com.datai.salesforce.metadata.component.entity.MetadataComponentIndex;
import com.datai.salesforce.metadata.component.mapper.MetadataComponentIndexMapper;
import com.datai.salesforce.metadata.component.parser.MetadataComponentParser;
import com.datai.salesforce.metadata.component.parser.ApexClassParser;
import com.datai.salesforce.metadata.component.parser.VisualforcePageParser;
import com.datai.salesforce.metadata.component.parser.CustomObjectParser;
import com.datai.salesforce.metadata.component.parser.CustomFieldParser;
import com.datai.salesforce.metadata.service.IFileStorageService;
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.ArrayList;
import java.util.List;
import java.util.concurrent.Future;

@Service
public class MetadataComponentIndexServiceImpl implements IMetadataComponentIndexService {

    @Autowired
    private MetadataComponentIndexMapper componentIndexMapper;

    @Autowired
    private IFileStorageService fileStorageService;

    @Autowired
    private ApexClassParser apexClassParser;

    @Autowired
    private VisualforcePageParser visualforcePageParser;

    @Autowired
    private CustomObjectParser customObjectParser;

    @Autowired
    private CustomFieldParser customFieldParser;

    @Override
    @Async("indexExecutor")
    @Transactional
    public ComponentIndexResponse createIndex(ComponentIndexRequest request) {
        File extractPath = fileStorageService.getExtractPath(request.getTaskId());
        
        List<MetadataComponentIndex> components = new ArrayList<>();
        
        components.addAll(apexClassParser.parse(extractPath, request.getTaskId()));
        components.addAll(visualforcePageParser.parse(extractPath, request.getTaskId()));
        components.addAll(customObjectParser.parse(extractPath, request.getTaskId()));
        components.addAll(customFieldParser.parse(extractPath, request.getTaskId()));
        
        componentIndexMapper.insertBatch(components);
        
        ComponentIndexResponse response = new ComponentIndexResponse();
        response.setTaskId(request.getTaskId());
        response.setTotal(components.size());
        response.setMessage("Index created successfully");
        
        return response;
    }
}

4. IMetadataComponentQueryService 服务接口

package com.datai.salesforce.metadata.component.service;

import com.datai.salesforce.metadata.component.dto.ComponentQueryRequest;
import com.datai.salesforce.metadata.component.dto.ComponentQueryResponse;

public interface IMetadataComponentQueryService {
    
    ComponentQueryResponse queryComponents(ComponentQueryRequest request);
}

5. MetadataComponentQueryServiceImpl 服务实现

package com.datai.salesforce.metadata.component.service;

import com.datai.salesforce.metadata.component.dto.ComponentQueryRequest;
import com.datai.salesforce.metadata.component.dto.ComponentQueryResponse;
import com.datai.salesforce.metadata.component.entity.MetadataComponentIndex;
import com.datai.salesforce.metadata.component.mapper.MetadataComponentIndexMapper;
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.stereotype.Service;

@Service
public class MetadataComponentQueryServiceImpl implements IMetadataComponentQueryService {

    @Autowired
    private MetadataComponentIndexMapper componentIndexMapper;

    @Override
    public ComponentQueryResponse queryComponents(ComponentQueryRequest request) {
        QueryWrapper<MetadataComponentIndex> queryWrapper = new QueryWrapper<>();
        
        if (request.getType() != null && !request.getType().isEmpty()) {
            queryWrapper.eq("component_type", request.getType());
        }
        
        if (request.getName() != null && !request.getName().isEmpty()) {
            queryWrapper.like("component_name", request.getName());
        }
        
        if (request.getVersion() != null && !request.getVersion().isEmpty()) {
            queryWrapper.eq("component_version", request.getVersion());
        }
        
        queryWrapper.orderByDesc("create_time");
        
        Page<MetadataComponentIndex> pageResult = componentIndexMapper.selectPage(
                new Page<>(request.getPage(), request.getSize()), queryWrapper);
        
        ComponentQueryResponse response = new ComponentQueryResponse();
        response.setTotal(pageResult.getTotal());
        response.setPage(request.getPage());
        response.setSize(request.getSize());
        response.setRecords(pageResult.getRecords());
        
        return response;
    }
}

6. IMetadataComponentDetailService 服务接口

package com.datai.salesforce.metadata.component.service;

import com.datai.salesforce.metadata.component.dto.ComponentDetailResponse;

public interface IMetadataComponentDetailService {
    
    ComponentDetailResponse getComponentDetail(Long id);
    
    String getComponentContent(Long id);
    
    ComponentDetailResponse getComponentDependencies(Long id);
}

7. MetadataComponentDetailServiceImpl 服务实现

package com.datai.salesforce.metadata.component.service;

import com.datai.salesforce.metadata.component.dto.ComponentDetailResponse;
import com.datai.salesforce.metadata.component.entity.MetadataComponentIndex;
import com.datai.salesforce.metadata.component.mapper.MetadataComponentIndexMapper;
import com.datai.salesforce.metadata.service.IFileStorageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

@Service
public class MetadataComponentDetailServiceImpl implements IMetadataComponentDetailService {

    @Autowired
    private MetadataComponentIndexMapper componentIndexMapper;

    @Autowired
    private IFileStorageService fileStorageService;

    @Override
    public ComponentDetailResponse getComponentDetail(Long id) {
        MetadataComponentIndex component = componentIndexMapper.selectById(id);
        if (component == null) {
            throw new RuntimeException("Component not found");
        }
        
        ComponentDetailResponse response = new ComponentDetailResponse();
        response.setId(component.getId());
        response.setTaskId(component.getTaskId());
        response.setComponentType(component.getComponentType());
        response.setComponentName(component.getComponentName());
        response.setComponentVersion(component.getComponentVersion());
        response.setFilePath(component.getFilePath());
        response.setCreateTime(component.getCreateTime());
        
        return response;
    }

    @Override
    public String getComponentContent(Long id) {
        MetadataComponentIndex component = componentIndexMapper.selectById(id);
        if (component == null) {
            throw new RuntimeException("Component not found");
        }
        
        File file = new File(component.getFilePath());
        try {
            return new String(Files.readAllBytes(Paths.get(file.toURI())));
        } catch (Exception e) {
            throw new RuntimeException("Failed to read component content", e);
        }
    }

    @Override
    public ComponentDetailResponse getComponentDependencies(Long id) {
        MetadataComponentIndex component = componentIndexMapper.selectById(id);
        if (component == null) {
            throw new RuntimeException("Component not found");
        }
        
        ComponentDetailResponse response = new ComponentDetailResponse();
        response.setId(component.getId());
        response.setComponentName(component.getComponentName());
        response.setComponentType(component.getComponentType());
        
        return response;
    }
}

8. IMetadataComponentStatisticsService 服务接口

package com.datai.salesforce.metadata.component.service;

import com.datai.salesforce.metadata.component.dto.*;

public interface IMetadataComponentStatisticsService {
    
    ComponentStatisticsResponse getStatistics(String type, String version, Long orgConfigId);
    
    ComponentTypeDistributionResponse getTypeDistribution(Long orgConfigId);
    
    ComponentVersionDistributionResponse getVersionDistribution(Long orgConfigId);
}

9. MetadataComponentStatisticsServiceImpl 服务实现

package com.datai.salesforce.metadata.component.service;

import com.datai.salesforce.metadata.component.dto.*;
import com.datai.salesforce.metadata.component.entity.MetadataComponentIndex;
import com.datai.salesforce.metadata.component.mapper.MetadataComponentIndexMapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class MetadataComponentStatisticsServiceImpl implements IMetadataComponentStatisticsService {

    @Autowired
    private MetadataComponentIndexMapper componentIndexMapper;

    @Override
    public ComponentStatisticsResponse getStatistics(String type, String version, Long orgConfigId) {
        QueryWrapper<MetadataComponentIndex> queryWrapper = new QueryWrapper<>();
        
        if (type != null && !type.isEmpty()) {
            queryWrapper.eq("component_type", type);
        }
        
        if (version != null && !version.isEmpty()) {
            queryWrapper.eq("component_version", version);
        }
        
        Long total = componentIndexMapper.selectCount(queryWrapper);
        
        ComponentStatisticsResponse response = new ComponentStatisticsResponse();
        response.setTotal(total);
        
        return response;
    }

    @Override
    public ComponentTypeDistributionResponse getTypeDistribution(Long orgConfigId) {
        QueryWrapper<MetadataComponentIndex> queryWrapper = new QueryWrapper<>();
        List<MetadataComponentIndex> components = componentIndexMapper.selectList(queryWrapper);
        
        Map<String, Long> typeDistribution = components.stream()
                .collect(Collectors.groupingBy(
                        MetadataComponentIndex::getComponentType,
                        Collectors.counting()
                ));
        
        ComponentTypeDistributionResponse response = new ComponentTypeDistributionResponse();
        response.setDistribution(typeDistribution);
        
        return response;
    }

    @Override
    public ComponentVersionDistributionResponse getVersionDistribution(Long orgConfigId) {
        QueryWrapper<MetadataComponentIndex> queryWrapper = new QueryWrapper<>();
        List<MetadataComponentIndex> components = componentIndexMapper.selectList(queryWrapper);
        
        Map<String, Long> versionDistribution = components.stream()
                .collect(Collectors.groupingBy(
                        MetadataComponentIndex::getComponentVersion,
                        Collectors.counting()
                ));
        
        ComponentVersionDistributionResponse response = new ComponentVersionDistributionResponse();
        response.setDistribution(versionDistribution);
        
        return response;
    }
}

10. MetadataComponentParser 接口

package com.datai.salesforce.metadata.component.parser;

import com.datai.salesforce.metadata.component.entity.MetadataComponentIndex;

import java.io.File;
import java.util.List;

public interface MetadataComponentParser {
    
    List<MetadataComponentIndex> parse(File extractPath, Long taskId);
}

11. ApexClassParser 实现类

package com.datai.salesforce.metadata.component.parser;

import com.datai.salesforce.metadata.component.entity.MetadataComponentIndex;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

@Component
public class ApexClassParser implements MetadataComponentParser {

    @Override
    public List<MetadataComponentIndex> parse(File extractPath, Long taskId) {
        List<MetadataComponentIndex> components = new ArrayList<>();
        
        File apexClassesDir = new File(extractPath, "classes");
        if (!apexClassesDir.exists()) {
            return components;
        }
        
        File[] files = apexClassesDir.listFiles((dir, name) -> name.endsWith(".cls"));
        if (files == null) {
            return components;
        }
        
        for (File file : files) {
            MetadataComponentIndex component = new MetadataComponentIndex();
            component.setTaskId(taskId);
            component.setComponentType("ApexClass");
            component.setComponentName(file.getName().replace(".cls", ""));
            component.setComponentVersion("1.0");
            component.setFilePath(file.getAbsolutePath());
            components.add(component);
        }
        
        return components;
    }
}

12. MetadataComponentIndex 实体类

package com.datai.salesforce.metadata.component.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_metadata_component_index")
public class MetadataComponentIndex {
    
    @TableId(type = IdType.AUTO)
    private Long id;
    
    private Long taskId;
    
    private String componentType;
    
    private String componentName;
    
    private String componentVersion;
    
    private String filePath;
    
    private LocalDateTime createTime;
    
    private LocalDateTime updateTime;
}

13. MetadataComponentIndexMapper 接口

package com.datai.salesforce.metadata.component.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.datai.salesforce.metadata.component.entity.MetadataComponentIndex;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface MetadataComponentIndexMapper extends BaseMapper<MetadataComponentIndex> {
}

14. DTO 类

package com.datai.salesforce.metadata.component.dto;

import lombok.Data;

@Data
public class ComponentIndexRequest {
    private Long taskId;
}

@Data
public class ComponentIndexResponse {
    private Long taskId;
    private Long total;
    private String message;
}

@Data
public class ComponentQueryRequest {
    private String type;
    private String name;
    private String version;
    private int page;
    private int size;
}

@Data
public class ComponentQueryResponse {
    private long total;
    private int page;
    private int size;
    private List<Object> records;
}

@Data
public class ComponentDetailResponse {
    private Long id;
    private Long taskId;
    private String componentType;
    private String componentName;
    private String componentVersion;
    private String filePath;
    private LocalDateTime createTime;
}

@Data
public class ComponentStatisticsResponse {
    private Long total;
}

@Data
public class ComponentTypeDistributionResponse {
    private Map<String, Long> distribution;
}

@Data
public class ComponentVersionDistributionResponse {
    private Map<String, Long> distribution;
}

15. ComponentIndexException 异常类

package com.datai.salesforce.metadata.component.exception;

public class ComponentIndexException extends RuntimeException {
    
    public ComponentIndexException(String message) {
        super(message);
    }
    
    public ComponentIndexException(String message, Throwable cause) {
        super(message, cause);
    }
}

约束

技术栈限制

  • 必须基于 Spring Boot 3 + MyBatis Plus + MySQL 技术栈
  • 必须使用现有的 IFileStorageService 接口进行文件读取
  • 必须使用现有的文件存储和解压处理功能

架构约束

  • 必须遵循 Authentication.canvas 中定义的架构和调用关系
  • 必须在 datai-salesforce-metadata 模块下实现
  • 必须使用 RESTful API 设计规范

性能要求

  • 组件索引创建响应时间 < 5s
  • 组件查询响应时间 < 1s
  • 组件详情查看响应时间 < 500ms
  • 组件统计响应时间 < 1s

安全性要求

  • 组件索引和查询操作需要认证
  • 文件读取需要权限控制
  • 敏感信息不记录到日志

Rule Set

"请严格参考 @Authentication.canvas 中的状态机转移逻辑,不要自行发挥。"

具体规则

  • 必须使用 Canvas 中定义的类名和方法名
  • 必须遵循 Canvas 中定义的调用关系
  • 必须参考 Canvas 中的流程图逻辑
  • 必须复用现有的 IFileStorageService 接口
  • 必须使用 MyBatis Plus 的 QueryWrapper 实现多条件查询
  • 必须使用 MyBatis Plus 的分页插件实现分页查询

验收标准

功能完整性

  • 组件索引创建成功,支持多种元数据类型解析
  • 索引信息完整准确
  • 索引创建性能满足要求
  • 组件查询功能正常工作
  • 支持按类型查询
  • 支持按名称查询
  • 支持按版本查询
  • 支持多条件组合查询
  • 支持分页查询
  • API 接口符合 RESTful 规范
  • 组件详情查看成功
  • 组件信息完整准确
  • 支持查看组件文件内容
  • 支持查看组件依赖关系
  • 组件统计功能正常工作
  • 统计信息准确
  • 支持按类型统计
  • 支持按版本统计
  • 支持按组织统计

代码正确性

  • 代码符合项目编码规范,有清晰的注释
  • 单元测试覆盖率 > 80%
  • 集成测试通过率 100%
  • 无严重的代码质量问题

文档准确性

  • API 文档完整,包含接口说明、参数说明、返回值说明
  • 代码注释清晰,包含方法说明、参数说明、返回值说明
  • 使用文档完整,包含使用示例和注意事项

性能指标

  • 组件索引创建响应时间 < 5s
  • 组件查询响应时间 < 1s
  • 组件详情查看响应时间 < 500ms
  • 组件统计响应时间 < 1s

风险

输出质量风险

  • 文件解析错误可能导致索引信息不准确
  • 查询逻辑不完善可能导致查询结果不准确
  • 统计逻辑错误可能导致统计信息不准确

技术实现风险

  • 文件解析库选择不当可能导致解析错误
  • 查询性能不佳可能影响用户体验
  • 统计逻辑复杂可能导致实现困难

时间成本风险

  • 元数据组件索引和查询功能实现可能需要较长时间
  • 测试和验证可能需要较长时间
  • 文档编写可能需要较长时间

使用记录

日期 使用场景 输入参数 输出结果 反馈 改进措施
2026-01-19 元数据组件索引和查询功能实现 REQ-010-11, ADR-0020 元数据组件索引和查询功能代码 待反馈 待改进