datai/datai-scenes/datai-scene-salesforce/docs/decisions/2026-02-02-006-ADR-高级功能技术选型.md

7.9 KiB
Raw Permalink Blame History

ADR-006: 高级功能技术选型

状态

已接受

日期

2026-02-02

背景

高级功能子需求需要实现 Salesforce Partner API 的五个核心业务功能:

  1. ConvertLead - 将线索转换为账户、联系人和商机
  2. EmptyRecycleBin - 永久删除回收站中的记录
  3. ProcessSubmitRequest - 提交流程请求,触发审批流程
  4. GetUserInfo - 获取当前用户信息
  5. GetServerTimestamp - 获取 Salesforce 服务器时间戳

这些功能涉及 Salesforce 的核心业务操作,需要选择合适的技术方案来实现:

  • 与 Salesforce API 的集成方式
  • 数据转换和映射策略
  • 异常处理机制
  • 复用已有实现的策略

决策

选择方案 1直接使用 Salesforce Partner APIpartner.jar,理由如下:

选择理由

  1. 官方支持

    • partner.jar 是 Salesforce 官方提供的 Java 客户端库
    • 与 Salesforce API 完全兼容,功能完整
    • 官方文档完善,社区支持良好
  2. 功能完整性

    • 原生支持所有高级功能ConvertLead、EmptyRecycleBin、ProcessSubmitRequest、GetUserInfo、GetServerTimestamp
    • 无需额外封装或适配
    • 支持所有高级功能的参数和选项
  3. 与现有架构一致

    • 与认证和会话管理、CRUD 操作、批量操作、查询功能、描述功能使用相同的技术栈
    • 可以复用 PartnerConnectionFactory 获取连接
    • 保持代码风格和技术选型的一致性
  4. 开发效率高

    • 无需自行实现 API 调用逻辑
    • 直接使用现有的 LeadConvert、EmptyRecycleBinResult、ProcessResult、GetUserInfoResult 等类
    • 减少开发工作量和维护成本
  5. 类型安全

    • 强类型 API编译时即可发现类型错误
    • 减少运行时错误
    • 更好的 IDE 支持和代码提示

放弃其他方案的理由

放弃方案 2使用 REST API

  • 理由
    1. REST API 需要手动处理 HTTP 请求和响应
    2. 需要自行实现数据序列化和反序列化
    3. 类型安全性差,容易出错
    4. 开发工作量大,维护成本高
    5. 与现有 SOAP API 架构不一致

放弃方案 3使用自定义封装层

  • 理由
    1. 增加不必要的抽象层
    2. 开发和维护成本高
    3. 可能引入新的 Bug
    4. 与现有直接使用 partner.jar 的策略不一致
    5. 不符合"不进行不必要的二次封装"的原则

后果

正面影响

  1. 开发效率高

    • 直接使用官方库,无需自行实现
    • 减少开发工作量和维护成本
    • 快速实现所有高级功能
  2. 代码质量高

    • 官方库经过充分测试
    • 类型安全,减少运行时错误
    • 与现有代码风格一致
  3. 维护成本低

    • 官方库持续更新和维护
    • 社区支持良好
    • 文档完善
  4. 功能完整

    • 支持所有高级功能
    • 支持所有参数和选项
    • 无需额外开发
  5. 架构一致性

    • 与现有子需求使用相同技术栈
    • 保持代码风格一致
    • 便于团队协作

负面影响

  1. 依赖外部库

    • 需要依赖 partner.jar
    • 升级时需要考虑兼容性
  2. 灵活性受限

    • 受限于官方 API 的设计
    • 无法自定义底层实现
  3. 学习成本

    • 需要了解 partner.jar 的 API
    • 需要了解 Salesforce 的数据模型

替代方案

方案 2使用 Salesforce REST API

技术选型:使用 Salesforce REST API 实现高级功能

优点

  1. 基于 HTTP/JSON易于调试
  2. 可以使用标准的 HTTP 客户端
  3. 跨平台兼容性好

缺点

  1. 需要手动处理 HTTP 请求和响应
  2. 需要自行实现数据序列化和反序列化
  3. 类型安全性差
  4. 开发工作量大
  5. 与现有 SOAP API 架构不一致

适用场景

  • 需要与非 Java 平台集成
  • 对类型安全要求不高的项目

方案 3使用自定义封装层

技术选型:在 partner.jar 之上创建自定义封装层

优点

  1. 可以统一接口风格
  2. 可以添加自定义功能
  3. 可以隐藏底层复杂性

缺点

  1. 增加不必要的抽象层
  2. 开发和维护成本高
  3. 可能引入新的 Bug
  4. 与现有策略不一致
  5. 增加学习成本

适用场景

  • 需要统一多个不同 API 的接口风格
  • 需要添加大量自定义功能

技术实现细节

核心类使用

功能 Salesforce 类 说明
ConvertLead LeadConvert 线索转换参数对象
ConvertLead LeadConvertResult 线索转换结果对象
EmptyRecycleBin EmptyRecycleBinResult 清空回收站结果对象
ProcessSubmitRequest ProcessSubmitRequest 流程提交请求对象
ProcessSubmitRequest ProcessResult 流程提交结果对象
GetUserInfo GetUserInfoResult 用户信息结果对象
GetServerTimestamp Calendar 服务器时间戳

数据转换策略

  1. DTO → Salesforce 对象

    • 使用 Builder 模式或工厂方法创建 Salesforce 对象
    • 设置所有必要的参数
    • 处理可选参数的默认值
  2. Salesforce 结果 → VO

    • 提取结果中的关键字段
    • 转换错误信息为友好的中文消息
    • 处理空值和异常情况
  3. 时间戳转换

    • Calendar 转换为 ISO 8601 格式的字符串
    • 保留时区信息

异常处理策略

  1. ConnectionException

    • 转换为 SalesforceAuthException
    • 返回友好的错误消息:"连接 Salesforce 失败"
  2. ApiFault

    • 转换为 SalesforceOperationException
    • 提取错误码和错误消息
    • 映射为中文错误消息
  3. 自定义异常映射

Salesforce 错误码 异常类型 错误消息
CANNOT_UPDATE_CONVERTED_LEAD SalesforceOperationException 无法更新已转换的线索
INVALID_LEAD_ID SalesforceOperationException 无效的线索 ID
INVALID_ACCOUNT_ID SalesforceOperationException 无效的账户 ID
INVALID_CONTACT_ID SalesforceOperationException 无效的联系人 ID
INVALID_OPPORTUNITY_ID SalesforceOperationException 无效的商机 ID
INVALID_CONVERTED_STATUS SalesforceOperationException 无效的转换状态
INVALID_ID SalesforceOperationException 无效的记录 ID
ENTITY_IS_DELETED SalesforceOperationException 记录已删除
ENTITY_IS_NOT_IN_RECYCLE_BIN SalesforceOperationException 记录不在回收站中
INVALID_OPERATION SalesforceOperationException 无效操作
INSUFFICIENT_ACCESS SalesforceOperationException 权限不足
OPERATION_FAILED SalesforceOperationException 操作失败
PROCESS_SUBMISSION_FAILED SalesforceOperationException 流程提交失败
INVALID_OBJECT_ID SalesforceOperationException 无效的对象 ID

复用策略

GetUserInfo 复用

  • 来源认证和会话管理子需求001-01
  • 实现类PartnerConnectionService
  • 方法getUserInfo()
  • 复用方式:在 PartnerAdvancedController 中直接调用 PartnerConnectionService.getUserInfo()
  • 理由:避免重复实现,保持代码一致性

相关文档