datai/datai-scenes/datai-scene-salesforce/docs/sessions/20260119-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

27 KiB
Raw Blame History

会话记录 - 元数据组件索引和查询

现状

当前项目已经实现了文件存储和解压处理REQ-010-7包括 Zip 文件存储、文件解压、文件路径管理。现在需要实现元数据组件索引和查询功能,包括组件索引创建、组件查询、组件详情查看、组件统计。

目标

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

输入链接

Prompt 文件

Context Snapshot

记录本次会话参考了哪些 Canvas 节点:

执行过程

1. 创建 MetadataComponentIndexController 控制器

执行内容: 创建 MetadataComponentIndexController 控制器,提供 8 个 RESTful API 接口,支持组件索引创建、组件查询、组件详情查看、组件统计。

关键代码:

@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 服务接口

执行内容: 创建 IMetadataComponentIndexService 服务接口,定义组件索引服务接口方法。

关键代码:

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

遇到的问题: 无

解决方案: 无

3. 创建 MetadataComponentIndexServiceImpl 服务实现

执行内容: 创建 MetadataComponentIndexServiceImpl 服务实现,实现组件索引服务接口方法,支持异步索引创建。

关键代码:

@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 服务接口

执行内容: 创建 IMetadataComponentQueryService 服务接口,定义组件查询服务接口方法。

关键代码:

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

遇到的问题: 无

解决方案: 无

5. 创建 MetadataComponentQueryServiceImpl 服务实现

执行内容: 创建 MetadataComponentQueryServiceImpl 服务实现,实现组件查询服务接口方法,使用 MyBatis Plus 的 QueryWrapper 实现多条件查询。

关键代码:

@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 服务接口

执行内容: 创建 IMetadataComponentDetailService 服务接口,定义组件详情服务接口方法。

关键代码:

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

遇到的问题: 无

解决方案: 无

7. 创建 MetadataComponentDetailServiceImpl 服务实现

执行内容: 创建 MetadataComponentDetailServiceImpl 服务实现,实现组件详情服务接口方法,使用文件读取 API 读取组件文件内容。

关键代码:

@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 服务接口

执行内容: 创建 IMetadataComponentStatisticsService 服务接口,定义组件统计服务接口方法。

关键代码:

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

遇到的问题: 无

解决方案: 无

9. 创建 MetadataComponentStatisticsServiceImpl 服务实现

执行内容: 创建 MetadataComponentStatisticsServiceImpl 服务实现,实现组件统计服务接口方法,使用 MyBatis Plus 的聚合查询实现统计功能。

关键代码:

@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 接口和实现类

执行内容: 创建 MetadataComponentParser 接口和 ApexClassParser、VisualforcePageParser、CustomObjectParser、CustomFieldParser 实现类,解析不同类型的元数据文件。

关键代码:

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

@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;
    }
}

遇到的问题: 无

解决方案: 无

11. 创建 MetadataComponentIndex 实体类和 Mapper

执行内容: 创建 MetadataComponentIndex 实体类和 MetadataComponentIndexMapper 接口,使用 MyBatis Plus 的 BaseMapper 实现数据库操作。

关键代码:

@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;
}

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

遇到的问题: 无

解决方案: 无

12. 创建 DTO 类

执行内容: 创建 ComponentIndexRequest、ComponentIndexResponse、ComponentQueryRequest、ComponentQueryResponse、ComponentDetailResponse、ComponentStatisticsResponse、ComponentTypeDistributionResponse、ComponentVersionDistributionResponse 等 DTO 类。

关键代码:

@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;
}

遇到的问题: 无

解决方案: 无

13. 创建 ComponentIndexException 异常类

执行内容: 创建 ComponentIndexException 异常类,处理组件索引相关的异常。

关键代码:

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

遇到的问题: 无

解决方案: 无

14. 复用现有的服务

执行内容: 复用现有的 IFileStorageService 服务,无需新增服务。

遇到的问题: 无

解决方案: 无

关键产出

记录本次会话的关键产出:

  • 生成的代码文件:
    • MetadataComponentIndexController.java - 元数据组件索引控制器
    • IMetadataComponentIndexService.java - 元数据组件索引服务接口
    • MetadataComponentIndexServiceImpl.java - 元数据组件索引服务实现
    • IMetadataComponentQueryService.java - 元数据组件查询服务接口
    • MetadataComponentQueryServiceImpl.java - 元数据组件查询服务实现
    • IMetadataComponentDetailService.java - 元数据组件详情服务接口
    • MetadataComponentDetailServiceImpl.java - 元数据组件详情服务实现
    • IMetadataComponentStatisticsService.java - 元数据组件统计服务接口
    • MetadataComponentStatisticsServiceImpl.java - 元数据组件统计服务实现
    • MetadataComponentParser.java - 元数据组件解析器接口
    • ApexClassParser.java - Apex Class 解析器
    • VisualforcePageParser.java - Visualforce Page 解析器
    • CustomObjectParser.java - Custom Object 解析器
    • CustomFieldParser.java - Custom Field 解析器
    • MetadataComponentIndex.java - 元数据组件索引实体类
    • MetadataComponentIndexMapper.java - 元数据组件索引 Mapper 接口
    • ComponentIndexRequest.java - 组件索引请求 DTO
    • ComponentIndexResponse.java - 组件索引响应 DTO
    • ComponentQueryRequest.java - 组件查询请求 DTO
    • ComponentQueryResponse.java - 组件查询响应 DTO
    • ComponentDetailResponse.java - 组件详情响应 DTO
    • ComponentStatisticsResponse.java - 组件统计响应 DTO
    • ComponentTypeDistributionResponse.java - 组件类型分布响应 DTO
    • ComponentVersionDistributionResponse.java - 组件版本分布响应 DTO
    • ComponentIndexException.java - 组件索引异常类
  • 更新的文档:无
  • 解决的问题:无
  • 达成的共识:复用现有的 IFileStorageService 服务

质疑与替代方案

记录在执行过程中提出的质疑和考虑的替代方案:

  • 质疑:是否需要使用搜索引擎实现组件查询?

    • 替代方案:使用 Elasticsearch 或 Solr 实现组件查询
    • 评估MyBatis Plus 的查询性能已经能够满足需求,使用搜索引擎会增加不必要的复杂度
  • 质疑:是否需要使用缓存提高查询性能?

    • 替代方案:使用 Redis 或 Caffeine 缓存查询结果
    • 评估MyBatis Plus 的查询性能已经能够满足需求,使用缓存会增加不必要的复杂度
  • 质疑:是否需要使用 NoSQL 数据库存储组件索引?

    • 替代方案:使用 MongoDB 或 Elasticsearch 存储组件索引
    • 评估MyBatis Plus 的查询性能已经能够满足需求,使用 NoSQL 数据库会增加不必要的复杂度

结论

总结本次会话的结果,包括:

  • 完成的工作:

    • 创建了 MetadataComponentIndexController 控制器,提供 8 个 RESTful API 接口
    • 创建了 IMetadataComponentIndexService 服务接口,定义组件索引服务接口方法
    • 创建了 MetadataComponentIndexServiceImpl 服务实现,实现组件索引服务接口方法
    • 创建了 IMetadataComponentQueryService 服务接口,定义组件查询服务接口方法
    • 创建了 MetadataComponentQueryServiceImpl 服务实现,实现组件查询服务接口方法
    • 创建了 IMetadataComponentDetailService 服务接口,定义组件详情服务接口方法
    • 创建了 MetadataComponentDetailServiceImpl 服务实现,实现组件详情服务接口方法
    • 创建了 IMetadataComponentStatisticsService 服务接口,定义组件统计服务接口方法
    • 创建了 MetadataComponentStatisticsServiceImpl 服务实现,实现组件统计服务接口方法
    • 创建了 MetadataComponentParser 接口和 ApexClassParser、VisualforcePageParser、CustomObjectParser、CustomFieldParser 实现类
    • 创建了 MetadataComponentIndex 实体类和 MetadataComponentIndexMapper 接口
    • 创建了 ComponentIndexRequest、ComponentIndexResponse、ComponentQueryRequest、ComponentQueryResponse、ComponentDetailResponse、ComponentStatisticsResponse、ComponentTypeDistributionResponse、ComponentVersionDistributionResponse 等 DTO 类
    • 创建了 ComponentIndexException 异常类
    • 复用了现有的 IFileStorageService 服务
  • 达成的目标:

    • 实现了组件索引创建功能,支持多种元数据类型解析
    • 实现了组件查询功能,支持按类型、名称、版本等条件查询
    • 实现了组件详情查看功能,支持查看组件文件内容和依赖关系
    • 实现了组件统计功能,支持按类型、版本、组织等维度统计
  • 后续的行动计划:

    • 创建数据库表 datai_metadata_component_index
    • 编写单元测试和集成测试
    • 编写 API 文档和使用文档
  • 需要跟进的事项:

    • 更新 Canvas添加元数据组件索引和查询相关的节点
    • 更新 CHANGELOG.md记录本次变更
    • 创建复盘报告,总结本次迭代的经验和教训

Design Update

  • 是否需要更新 Canvas? 是
  • Authentication.canvas - 需要添加元数据组件索引和查询相关的节点
  • 其他 Canvas 文件: 无

复现步骤

提供复现本次会话结果的具体步骤:

  1. 创建 MetadataComponentIndexController 控制器,提供 8 个 RESTful API 接口
  2. 创建 IMetadataComponentIndexService 服务接口,定义组件索引服务接口方法
  3. 创建 MetadataComponentIndexServiceImpl 服务实现,实现组件索引服务接口方法
  4. 创建 IMetadataComponentQueryService 服务接口,定义组件查询服务接口方法
  5. 创建 MetadataComponentQueryServiceImpl 服务实现,实现组件查询服务接口方法
  6. 创建 IMetadataComponentDetailService 服务接口,定义组件详情服务接口方法
  7. 创建 MetadataComponentDetailServiceImpl 服务实现,实现组件详情服务接口方法
  8. 创建 IMetadataComponentStatisticsService 服务接口,定义组件统计服务接口方法
  9. 创建 MetadataComponentStatisticsServiceImpl 服务实现,实现组件统计服务接口方法
  10. 创建 MetadataComponentParser 接口和 ApexClassParser、VisualforcePageParser、CustomObjectParser、CustomFieldParser 实现类
  11. 创建 MetadataComponentIndex 实体类和 MetadataComponentIndexMapper 接口
  12. 创建 ComponentIndexRequest、ComponentIndexResponse、ComponentQueryRequest、ComponentQueryResponse、ComponentDetailResponse、ComponentStatisticsResponse、ComponentTypeDistributionResponse、ComponentVersionDistributionResponse 等 DTO 类
  13. 创建 ComponentIndexException 异常类
  14. 创建数据库表 datai_metadata_component_index
  15. 编写单元测试和集成测试
  16. 编写 API 文档和使用文档
  17. 验证元数据组件索引和查询功能是否正常工作

验证方法:

  • 调用 POST /metadata/component/index 接口,验证组件索引创建是否成功
  • 调用 GET /metadata/component/query 接口,验证组件查询是否正常
  • 调用 GET /metadata/component/detail/{id} 接口,验证组件详情查看是否正常
  • 调用 GET /metadata/component/detail/{id}/content 接口,验证组件文件内容查看是否正常
  • 调用 GET /metadata/component/detail/{id}/dependencies 接口,验证组件依赖关系查看是否正常
  • 调用 GET /metadata/component/statistics 接口,验证组件统计是否正常
  • 调用 GET /metadata/component/statistics/type-distribution 接口,验证组件类型分布统计是否正常
  • 调用 GET /metadata/component/statistics/version-distribution 接口,验证组件版本分布统计是否正常