datai/datai-scenes/datai-scene-salesforce/docs/prompts/2026-02-02-005-prompt-描述功能.md

12 KiB
Raw Permalink Blame History

提示词:描述功能

引用真源

需求描述

根据需求文档,实现 Salesforce Partner API 的描述功能,包括以下六个核心功能:

  1. DescribeGlobal:描述所有可用对象

    • 获取 Salesforce 中所有可用的对象列表
    • 返回对象的基本信息名称、标签、ID 前缀、权限等)
    • 不包含字段详细信息
  2. DescribeSObject:描述特定对象

    • 获取特定对象的详细定义
    • 返回对象的字段列表、子关系、权限信息等
    • 包含完整的字段元数据
  3. DescribeSObjects:描述多个对象

    • 批量获取多个对象的详细定义
    • 使用 describeSObjects(String[]) 方法减少 API 调用次数
    • 支持部分失败场景
  4. DescribeLayout:描述对象布局

    • 获取对象的页面布局信息
    • 支持指定记录类型 ID
    • 返回编辑布局、详细布局等信息
  5. DescribeTabs:描述标签页

    • 获取 Salesforce 中的所有标签页信息
    • 支持自定义标签页和标准标签页
  6. DescribeQuickActions:描述快速操作

    • 获取对象的快速操作定义
    • 包含创建记录、发送邮件、更新记录等操作

设计方案

根据设计文档和决策记录,采用以下技术方案:

  1. 架构设计

    • 分层架构Controller → Service → PartnerConnectionFactory → Salesforce API
    • 使用 PartnerConnectionFactory 获取连接(固定使用 source org 类型)
    • 使用 Map<String, Object> 格式转换描述结果
  2. 描述结果转换策略

    • 将 DescribeSObjectResult 转换为 Map<String, Object>
    • 将 DescribeGlobalSObjectResult 转换为 Map<String, Object>
    • 将 DescribeLayoutResult 转换为 Map<String, Object>
    • 将 DescribeTabSetResult 转换为 Map<String, Object>
    • 将 DescribeQuickActionResult 转换为 Map<String, Object>
    • 递归处理嵌套对象(字段列表、子关系列表等)
  3. 批量描述优化

    • 使用 describeSObjects(String[]) 方法批量描述多个对象
    • 减少 API 调用次数,提高性能
  4. 字段信息处理

    • 完整返回字段定义(名称、类型、标签、是否可创建/更新等)
    • 处理 Picklist 字段的选项值
    • 处理关系字段的引用信息
  5. 异常处理

    • 使用 datai-salesforce-common 模块的异常类
    • 捕获 ConnectionException 并转换为自定义异常
    • 返回友好的错误消息(中文)

输出格式要求

1. 必须包含以下文件

Controller

  • 类名PartnerDescribeController.java
  • 路径datai-salesforce-partner/src/main/java/com/datai/partner/controller/PartnerDescribeController.java
  • 功能
    • GET /partner/describe/global - 描述所有可用对象
    • POST /partner/describe/sobject - 描述特定对象
    • POST /partner/describe/sobjects - 描述多个对象
    • POST /partner/describe/layout - 描述对象布局
    • GET /partner/describe/tabs - 描述标签页
    • POST /partner/describe/quick-actions - 描述快速操作

Service 接口

  • 类名IPartnerDescribeService.java
  • 路径datai-salesforce-partner/src/main/java/com/datai/partner/service/IPartnerDescribeService.java
  • 方法
    • DescribeGlobalResultVo describeGlobal()
    • DescribeSObjectResultVo describeSObject(DescribeSObjectDto dto)
    • DescribeSObjectResultVo[] describeSObjects(DescribeSObjectsDto dto)
    • DescribeLayoutResultVo describeLayout(DescribeLayoutDto dto)
    • DescribeTabResultVo describeTabs()
    • DescribeQuickActionResultVo describeQuickActions(DescribeQuickActionsDto dto)

Service 实现

  • 类名PartnerDescribeServiceImpl.java
  • 路径datai-salesforce-partner/src/main/java/com/datai/partner/service/impl/PartnerDescribeServiceImpl.java

DTO 类

  • DescribeSObjectDto.java:描述单个对象请求参数
  • DescribeSObjectsDto.java:描述多个对象请求参数
  • DescribeLayoutDto.java:描述对象布局请求参数
  • DescribeQuickActionsDto.java:描述快速操作请求参数

VO 类

  • DescribeGlobalResultVo.javaDescribeGlobal 结果
  • DescribeSObjectResultVo.javaDescribeSObject 结果
  • DescribeLayoutResultVo.javaDescribeLayout 结果
  • DescribeTabResultVo.javaDescribeTabs 结果
  • DescribeQuickActionResultVo.javaDescribeQuickActions 结果

工具类

  • 类名DescribeResultConverter.java
  • 路径datai-salesforce-partner/src/main/java/com/datai/partner/util/DescribeResultConverter.java
  • 功能:将 Salesforce 描述结果转换为 Map<String, Object> 格式

单元测试

  • 类名PartnerDescribeServiceImplTest.java
  • 路径datai-salesforce-partner/src/test/java/com/datai/partner/service/impl/PartnerDescribeServiceImplTest.java
  • 覆盖率:不低于 80%

2. 代码结构要求

  • 使用 Spring Boot 3.x 和 Spring Security 6.x
  • 使用 Lombok 注解(@Data、@NoArgsConstructor、@AllArgsConstructor、@Builder
  • 使用 Swagger 注解(@Schema、@Operation、@ApiResponse
  • 使用 JSR-303 验证注解(@NotBlank、@NotEmpty
  • 使用依赖注入(@Autowired、@Service、@RestController

代码规范要求

1. 命名规范

  • 类命名:首字母大写,驼峰命名,如 PartnerDescribeController
  • 方法命名:首字母小写,驼峰命名,如 describeGlobal
  • 变量命名:首字母小写,驼峰命名,如 objectType
  • 常量命名:全大写,下划线分隔,如 MAX_BATCH_SIZE

2. 注释规范

  • 类注释:使用 /** */,包含类功能描述、作者、创建时间
  • 方法注释:使用 /** */,包含方法功能、参数、返回值、异常描述
  • 字段注释:使用 /** */,包含字段功能描述
  • 复杂逻辑注释:使用 //,说明业务逻辑

3. 代码格式

  • 使用 4 个空格缩进
  • 行宽不超过 120 字符
  • 导入规范:使用 import 静态导入,避免通配符导入
  • 空行规范:方法之间、逻辑块之间使用空行分隔

4. 异常处理规范

  • 使用 try-catch 捕获异常
  • 将 Salesforce 异常转换为自定义异常
  • 返回友好的错误消息(中文)
  • 记录详细的错误日志

测试要求

1. 单元测试覆盖率

  • 不低于 80%

2. 测试用例场景

  • 正常场景

    • 描述所有可用对象成功
    • 描述特定对象成功
    • 描述多个对象成功
    • 描述对象布局成功
    • 描述标签页成功
    • 描述快速操作成功
  • 异常场景

    • 连接异常ConnectionException
    • 对象不存在异常
    • API 调用异常
    • 参数验证失败

3. 测试框架

  • 使用 JUnit 5 进行测试
  • 使用 Mockito 进行模拟
  • 使用 AssertJ 进行断言

4. 测试用例命名规范

  • test+方法名+场景,如 testDescribeGlobalSuccess
  • test+方法名+异常,如 testDescribeSObjectConnectionException

注意事项

1. 描述结果转换

  • 必须完整转换所有重要属性
  • 保留字段的原始类型
  • 处理嵌套对象(字段列表、子关系列表等)
  • 统一字段命名规范(驼峰命名)

2. 字段类型处理

  • 处理所有 Salesforce 字段类型string、picklist、reference、date、datetime 等)
  • Picklist 字段需要转换选项值列表
  • 关系字段需要转换引用信息

3. 批量描述

  • 使用 describeSObjects(String[]) 方法
  • 处理部分对象不存在的情况
  • 返回每个对象的描述结果或错误信息

4. 安全性

  • 使用 @PreAuthorize 进行权限控制
  • 验证所有输入参数
  • 防止 SQL 注入(虽然使用 API但仍需验证参数

5. 性能考虑

  • 描述结果可以缓存(可选实现)
  • 避免重复转换相同的描述结果
  • 批量描述减少 API 调用次数

6. 兼容性

  • 使用 partner.jar 现有类
  • 不引入额外的依赖
  • 保持与现有模块的一致性

示例代码

DescribeGlobal 使用示例

// Controller
@GetMapping("/global")
@Operation(summary = "描述所有可用对象")
public AjaxResult describeGlobal() {
    DescribeGlobalResultVo result = partnerDescribeService.describeGlobal();
    return AjaxResult.success(result);
}

// Service
@Override
public DescribeGlobalResultVo describeGlobal() {
    try {
        PartnerConnection connection = partnerConnectionFactory.getConnection("source");
        DescribeGlobalResult result = connection.describeGlobal();
        
        List<Map<String, Object>> sObjects = new ArrayList<>();
        for (DescribeGlobalSObjectResult sObject : result.getSobjects()) {
            sObjects.add(convertGlobalSObjectToMap(sObject));
        }
        
        return DescribeGlobalResultVo.builder()
            .sObjects(sObjects)
            .maxBatchSize(result.getMaxBatchSize())
            .success(true)
            .build();
    } catch (ConnectionException e) {
        throw new SalesforceOperationException("描述所有可用对象失败: " + e.getMessage(), e);
    }
}

DescribeSObject 使用示例

// Controller
@PostMapping("/sobject")
@Operation(summary = "描述特定对象")
public AjaxResult describeSObject(@RequestBody @Valid DescribeSObjectDto dto) {
    DescribeSObjectResultVo result = partnerDescribeService.describeSObject(dto);
    return AjaxResult.success(result);
}

// Service
@Override
public DescribeSObjectResultVo describeSObject(DescribeSObjectDto dto) {
    try {
        PartnerConnection connection = partnerConnectionFactory.getConnection("source");
        DescribeSObjectResult[] results = connection.describeSObjects(new String[]{dto.getObjectType()});
        
        if (results.length > 0) {
            Map<String, Object> sObject = convertDescribeResultToMap(results[0]);
            return DescribeSObjectResultVo.builder()
                .sObject(sObject)
                .success(true)
                .build();
        }
        
        return DescribeSObjectResultVo.builder()
            .success(false)
            .errors(Collections.singletonList(ErrorVo.builder()
                .message("对象不存在: " + dto.getObjectType())
                .build()))
            .build();
    } catch (ConnectionException e) {
        throw new SalesforceOperationException("描述对象失败: " + e.getMessage(), e);
    }
}

描述结果转换示例

private Map<String, Object> convertDescribeResultToMap(DescribeSObjectResult result) {
    Map<String, Object> sObjectMap = new HashMap<>();
    sObjectMap.put("name", result.getName());
    sObjectMap.put("label", result.getLabel());
    sObjectMap.put("keyPrefix", result.getKeyPrefix());
    sObjectMap.put("labelPlural", result.getLabelPlural());
    sObjectMap.put("createable", result.isCreateable());
    sObjectMap.put("updateable", result.isUpdateable());
    sObjectMap.put("deletable", result.isDeletable());
    sObjectMap.put("queryable", result.isQueryable());
    
    // 转换字段列表
    List<Map<String, Object>> fields = new ArrayList<>();
    for (Field field : result.getFields()) {
        fields.add(convertFieldToMap(field));
    }
    sObjectMap.put("fields", fields);
    
    return sObjectMap;
}

private Map<String, Object> convertFieldToMap(Field field) {
    Map<String, Object> fieldMap = new HashMap<>();
    fieldMap.put("name", field.getName());
    fieldMap.put("type", field.getType().toString());
    fieldMap.put("label", field.getLabel());
    fieldMap.put("createable", field.isCreateable());
    fieldMap.put("updateable", field.isUpdateable());
    fieldMap.put("nillable", field.isNillable());
    
    // Picklist 字段处理
    if (field.getType() == FieldType.picklist || field.getType() == FieldType.multipicklist) {
        List<Map<String, Object>> picklistValues = new ArrayList<>();
        for (PicklistEntry entry : field.getPicklistValues()) {
            Map<String, Object> valueMap = new HashMap<>();
            valueMap.put("value", entry.getValue());
            valueMap.put("label", entry.getLabel());
            valueMap.put("active", entry.isActive());
            valueMap.put("defaultValue", entry.isDefaultValue());
            picklistValues.add(valueMap);
        }
        fieldMap.put("picklistValues", picklistValues);
    }
    
    return fieldMap;
}