datai/datai-scenes/datai-scene-salesforce/docs/design/2026-02-02-006-高级功能-设计.md

31 KiB
Raw Permalink Blame History

设计文档 - 高级功能

元数据

  • 需求编号001-06
  • 创建时间2026-02-02
  • 创建人AI Assistant
  • 状态:进行中

设计概述

基于 Salesforce Partner API 的高级功能设计,包括线索转换、回收站清空、流程提交、用户信息获取和服务器时间戳获取五个核心功能。设计采用分层架构,通过 PartnerConnectionFactory 获取连接,实现与 Salesforce 的高级业务操作集成。

架构设计

系统架构图

┌─────────────────────────────────────────────────────────────────┐
│                        表现层 (Controller)                       │
│  ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────────┐ │
│  │ConvertLead  │ │EmptyRecycle │ │ProcessSubmit │GetUserInfo   │ │
│  │Controller   │ │BinController│ │Controller   │Controller    │ │
│  └─────────────┘ └─────────────┘ └─────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                        业务层 (Service)                          │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │              PartnerAdvancedService                          ││
│  │  ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐││
│  │  │convertLead  │ │emptyRecycle │ │processSubmit │getUserInfo│││
│  │  │             │ │Bin          │ │Request      │getServer  │││
│  │  │             │ │             │ │             │Timestamp  │││
│  │  └─────────────┘ └─────────────┘ └─────────────────────────┘││
│  └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                        连接层 (Factory)                          │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │              PartnerConnectionFactory                        ││
│  │                 (已存在,复用)                                ││
│  └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────────┐
│                        API 层 (Salesforce)                       │
│  ┌─────────────────────────────────────────────────────────────┐│
│  │              PartnerConnection                               ││
│  │  ┌─────────────┐ ┌─────────────┐ ┌─────────────────────────┐││
│  │  │convertLead  │ │emptyRecycle │ │processSubmit │getUserInfo│││
│  │  │             │ │Bin          │ │Request      │getServer  │││
│  │  │             │ │             │ │             │Timestamp  │││
│  │  └─────────────┘ └─────────────┘ └─────────────────────────┘││
│  └─────────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────────┘

模块架构

datai-salesforce-partner
├── src/main/java/com/datai/partner
│   ├── controller
│   │   └── PartnerAdvancedController.java    # 高级功能控制器
│   ├── service
│   │   ├── PartnerAdvancedService.java       # 高级功能服务接口
│   │   └── impl
│   │       └── PartnerAdvancedServiceImpl.java # 服务实现
│   └── dto
│       ├── LeadConvertRequest.java           # 线索转换请求DTO
│       ├── EmptyRecycleBinRequest.java       # 清空回收站请求DTO
│       └── ProcessSubmitRequest.java         # 流程提交请求DTO
└── src/main/java/com/datai/partner/vo
    ├── LeadConvertResultVo.java              # 线索转换结果VO
    ├── EmptyRecycleBinResultVo.java          # 清空回收站结果VO
    ├── ProcessResultVo.java                  # 流程提交结果VO
    ├── UserInfoVo.java                       # 用户信息VO
    └── ServerTimestampVo.java                # 服务器时间戳VO

数据流图

┌──────────┐     ┌──────────┐     ┌──────────┐     ┌──────────┐
│  Client  │────▶│Controller│────▶│ Service  │────▶│ Factory  │
└──────────┘     └──────────┘     └──────────┘     └──────────┘
                                              │           │
                                              ▼           ▼
                                         ┌──────────┐     ┌──────────┐
                                         │   DTO    │     │Partner   │
                                         │Conversion│     │Connection│
                                         └──────────┘     └──────────┘
                                                                   │
                                                                   ▼
                                                            ┌──────────┐
                                                            │Salesforce│
                                                            │   API    │
                                                            └──────────┘

技术方案

技术选型

技术组件 版本/说明 选择理由
Spring Boot 2.7.x 若依框架基础,提供依赖注入和 REST API 支持
Salesforce Partner API partner.jar Salesforce 官方 API支持高级业务操作
Lombok 1.18.x 简化代码,自动生成 getter/setter
Jackson 2.13.x JSON 序列化/反序列化
datai-salesforce-auth 内部模块 提供连接管理和会话管理
datai-salesforce-common 内部模块 提供异常类和常量

核心算法设计

1. 线索转换算法

算法convertLead
输入leadId, convertedStatus, accountId, contactId, opportunityId, 
      overwriteLeadSource, doNotCreateOpportunity, sendNotificationEmail
输出LeadConvertResultVo

步骤:
1. 参数验证
   - 验证 leadId 不为空
   - 验证 convertedStatus 不为空
   - 验证 accountId/contactId/opportunityId 格式(如果提供)

2. 创建 LeadConvert 对象
   - 设置 leadId
   - 设置 convertedStatus
   - 设置 accountId如果提供
   - 设置 contactId如果提供
   - 设置 opportunityId如果提供
   - 设置 overwriteLeadSource默认为 false
   - 设置 doNotCreateOpportunity默认为 false
   - 设置 sendNotificationEmail默认为 false

3. 获取 PartnerConnection
   - 调用 partnerConnectionFactory.getConnection("source")

4. 执行线索转换
   - 调用 partnerConnection.convertLead(leadConvert)

5. 处理结果
   - 将 LeadConvertResult 转换为 LeadConvertResultVo
   - 返回结果

6. 异常处理
   - 捕获 ConnectionException → SalesforceAuthException
   - 捕获 ApiFault → SalesforceOperationException
   - 记录错误日志

2. 回收站清空算法

算法emptyRecycleBin
输入ids记录 ID 列表)
输出EmptyRecycleBinResultVo

步骤:
1. 参数验证
   - 验证 ids 不为空
   - 验证 ids 数量不超过 200Salesforce 限制)
   - 验证每个 ID 格式正确

2. 获取 PartnerConnection
   - 调用 partnerConnectionFactory.getConnection("source")

3. 执行清空回收站
   - 调用 partnerConnection.emptyRecycleBin(ids.toArray(new String[0]))

4. 处理结果
   - 将 EmptyRecycleBinResult[] 转换为 List<EmptyRecycleBinResultVo>
   - 计算整体成功状态
   - 返回结果

5. 异常处理
   - 捕获 ConnectionException → SalesforceAuthException
   - 捕获 ApiFault → SalesforceOperationException
   - 记录错误日志

3. 流程提交算法

算法processSubmitRequest
输入objectId, comments
输出ProcessResultVo

步骤:
1. 参数验证
   - 验证 objectId 不为空
   - 验证 objectId 格式正确

2. 创建 ProcessSubmitRequest 对象
   - 设置 objectId
   - 设置 comments如果提供

3. 获取 PartnerConnection
   - 调用 partnerConnectionFactory.getConnection("source")

4. 执行流程提交
   - 调用 partnerConnection.processSubmitRequest(request)

5. 处理结果
   - 将 ProcessResult 转换为 ProcessResultVo
   - 返回结果

6. 异常处理
   - 捕获 ConnectionException → SalesforceAuthException
   - 捕获 ApiFault → SalesforceOperationException
   - 记录错误日志

复用策略

GetUserInfo 复用

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

数据模型

DTO 设计

1. LeadConvertRequest线索转换请求

@Data
public class LeadConvertRequest {
    /** 线索 ID */
    @NotBlank(message = "线索 ID 不能为空")
    private String leadId;
    
    /** 转换状态 */
    @NotBlank(message = "转换状态不能为空")
    private String convertedStatus;
    
    /** 账户 ID可选 */
    private String accountId;
    
    /** 联系人 ID可选 */
    private String contactId;
    
    /** 商机 ID可选 */
    private String opportunityId;
    
    /** 是否覆盖线索来源(可选,默认 false */
    private Boolean overwriteLeadSource = false;
    
    /** 是否不创建商机(可选,默认 false */
    private Boolean doNotCreateOpportunity = false;
    
    /** 是否发送通知邮件(可选,默认 false */
    private Boolean sendNotificationEmail = false;
}

2. EmptyRecycleBinRequest清空回收站请求

@Data
public class EmptyRecycleBinRequest {
    /** 记录 ID 列表 */
    @NotEmpty(message = "记录 ID 列表不能为空")
    @Size(max = 200, message = "记录 ID 数量不能超过 200")
    private List<@NotBlank String> ids;
}

3. ProcessSubmitRequest流程提交请求

@Data
public class ProcessSubmitRequest {
    /** 对象 ID */
    @NotBlank(message = "对象 ID 不能为空")
    private String objectId;
    
    /** 评论(可选) */
    private String comments;
}

VO 设计

1. LeadConvertResultVo线索转换结果

@Data
public class LeadConvertResultVo {
    /** 账户 ID */
    private String accountId;
    
    /** 联系人 ID */
    private String contactId;
    
    /** 商机 ID */
    private String opportunityId;
    
    /** 原线索 ID */
    private String leadId;
    
    /** 是否成功 */
    private Boolean success;
    
    /** 错误信息列表 */
    private List<ErrorInfo> errors;
}

2. EmptyRecycleBinResultVo清空回收站结果

@Data
public class EmptyRecycleBinResultVo {
    /** 记录 ID */
    private String id;
    
    /** 是否成功 */
    private Boolean success;
    
    /** 错误信息列表 */
    private List<ErrorInfo> errors;
}

3. ProcessResultVo流程提交结果

@Data
public class ProcessResultVo {
    /** 流程实例 ID */
    private String processInstanceId;
    
    /** 是否成功 */
    private Boolean success;
    
    /** 错误信息列表 */
    private List<ErrorInfo> errors;
}

4. UserInfoVo用户信息

@Data
public class UserInfoVo {
    /** 用户 ID */
    private String userId;
    
    /** 用户名 */
    private String userName;
    
    /** 用户全名 */
    private String userFullName;
    
    /** 用户邮箱 */
    private String userEmail;
    
    /** 组织 ID */
    private String organizationId;
    
    /** 组织名称 */
    private String organizationName;
    
    /** 用户语言 */
    private String userLanguage;
    
    /** 用户时区 */
    private String userTimeZone;
    
    /** 货币符号 */
    private String currencySymbol;
}

5. ServerTimestampVo服务器时间戳

@Data
public class ServerTimestampVo {
    /** 服务器时间戳ISO 8601 格式) */
    private String timestamp;
    
    /** 时区 */
    private String timeZone;
    
    /** 是否成功 */
    private Boolean success;
    
    /** 错误信息列表 */
    private List<ErrorInfo> errors;
}

6. ErrorInfo错误信息

@Data
public class ErrorInfo {
    /** 错误码 */
    private String statusCode;
    
    /** 错误消息 */
    private String message;
    
    /** 字段(如果适用) */
    private String[] fields;
}

数据转换映射

LeadConvertResult → LeadConvertResultVo

源字段 目标字段 转换说明
getAccountId() accountId 直接映射
getContactId() contactId 直接映射
getOpportunityId() opportunityId 直接映射
getLeadId() leadId 直接映射
isSuccess() success 直接映射
getErrors() errors 转换为 ErrorInfo 列表

EmptyRecycleBinResult → EmptyRecycleBinResultVo

源字段 目标字段 转换说明
getId() id 直接映射
isSuccess() success 直接映射
getErrors() errors 转换为 ErrorInfo 列表

ProcessResult → ProcessResultVo

源字段 目标字段 转换说明
getInstanceId() processInstanceId 直接映射
isSuccess() success 直接映射
getErrors() errors 转换为 ErrorInfo 列表

GetUserInfoResult → UserInfoVo

源字段 目标字段 转换说明
getUserId() userId 直接映射
getUserName() userName 直接映射
getUserFullName() userFullName 直接映射
getUserEmail() userEmail 直接映射
getOrganizationId() organizationId 直接映射
getOrganizationName() organizationName 直接映射
getUserLanguage() userLanguage 直接映射
getUserTimeZone() userTimeZone 直接映射
getCurrencySymbol() currencySymbol 直接映射

Calendar → ServerTimestampVo

源字段 目标字段 转换说明
Calendar 对象 timestamp 转换为 ISO 8601 格式字符串
getTimeZone().getID() timeZone 直接映射

接口设计

RESTful API 设计

1. 转换线索

接口信息:

  • HTTP 方法: POST
  • URL 路径: /api/partner/advanced/convert-lead
  • 权限要求: @PreAuthorize("@ss.hasLogin()")

请求参数:

参数名 类型 必填 说明
leadId String 线索 ID
convertedStatus String 转换状态
accountId String 账户 ID
contactId String 联系人 ID
opportunityId String 商机 ID
overwriteLeadSource Boolean 是否覆盖线索来源(默认 false
doNotCreateOpportunity Boolean 是否不创建商机(默认 false
sendNotificationEmail Boolean 是否发送通知邮件(默认 false

响应参数:

参数名 类型 说明
code Integer 状态码200 成功,其他失败)
msg String 提示信息
data Object 响应数据
data.accountId String 账户 ID
data.contactId String 联系人 ID
data.opportunityId String 商机 ID
data.leadId String 原线索 ID
data.success Boolean 是否成功
data.errors Array 错误信息列表

2. 清空回收站

接口信息:

  • HTTP 方法: POST
  • URL 路径: /api/partner/advanced/empty-recycle-bin
  • 权限要求: @PreAuthorize("@ss.hasLogin()")

请求参数:

参数名 类型 必填 说明
ids Array 记录 ID 列表(最多 200 个)

响应参数:

参数名 类型 说明
code Integer 状态码200 成功,其他失败)
msg String 提示信息
data Object 响应数据
data.results Array 结果列表
data.results[].id String 记录 ID
data.results[].success Boolean 是否成功
data.results[].errors Array 错误信息列表
data.success Boolean 是否全部成功
data.errors Array 整体错误信息列表

3. 处理提交请求

接口信息:

  • HTTP 方法: POST
  • URL 路径: /api/partner/advanced/process-submit-request
  • 权限要求: @PreAuthorize("@ss.hasLogin()")

请求参数:

参数名 类型 必填 说明
objectId String 对象 ID
comments String 评论

响应参数:

参数名 类型 说明
code Integer 状态码200 成功,其他失败)
msg String 提示信息
data Object 响应数据
data.processInstanceId String 流程实例 ID
data.success Boolean 是否成功
data.errors Array 错误信息列表

4. 获取用户信息

接口信息:

  • HTTP 方法: GET
  • URL 路径: /api/partner/advanced/user-info
  • 权限要求: @PreAuthorize("@ss.hasLogin()")
  • 说明: 复用认证和会话管理子需求的实现

请求参数: 无

响应参数:

参数名 类型 说明
code Integer 状态码200 成功,其他失败)
msg String 提示信息
data Object 响应数据
data.userInfo Object 用户信息
data.userInfo.userId String 用户 ID
data.userInfo.userName String 用户名
data.userInfo.userFullName String 用户全名
data.userInfo.userEmail String 用户邮箱
data.userInfo.organizationId String 组织 ID
data.userInfo.organizationName String 组织名称
data.userInfo.userLanguage String 用户语言
data.userInfo.userTimeZone String 用户时区
data.userInfo.currencySymbol String 货币符号
data.success Boolean 是否成功
data.errors Array 错误信息列表

5. 获取服务器时间戳

接口信息:

  • HTTP 方法: GET
  • URL 路径: /api/partner/advanced/server-timestamp
  • 权限要求: @PreAuthorize("@ss.hasLogin()")

请求参数: 无

响应参数:

参数名 类型 说明
code Integer 状态码200 成功,其他失败)
msg String 提示信息
data Object 响应数据
data.timestamp String 服务器时间戳ISO 8601 格式)
data.timeZone String 时区
data.success Boolean 是否成功
data.errors Array 错误信息列表

接口权限设计

所有接口统一使用若依框架的权限注解:

@PreAuthorize("@ss.hasLogin()")

说明:

  • 用户必须登录才能访问高级功能接口
  • 不限制具体权限,因为高级功能通常需要较高的权限级别
  • 实际权限控制由 Salesforce API 层面处理

实现要点

关键实现逻辑

1. 线索转换实现

@Override
public LeadConvertResultVo convertLead(LeadConvertRequest request) {
    try {
        // 1. 获取连接
        PartnerConnection connection = partnerConnectionFactory.getConnection("source");
        
        // 2. 创建 LeadConvert 对象
        LeadConvert leadConvert = new LeadConvert();
        leadConvert.setLeadId(request.getLeadId());
        leadConvert.setConvertedStatus(request.getConvertedStatus());
        
        if (StringUtils.hasText(request.getAccountId())) {
            leadConvert.setAccountId(request.getAccountId());
        }
        if (StringUtils.hasText(request.getContactId())) {
            leadConvert.setContactId(request.getContactId());
        }
        if (StringUtils.hasText(request.getOpportunityId())) {
            leadConvert.setOpportunityId(request.getOpportunityId());
        }
        
        leadConvert.setOverwriteLeadSource(
            request.getOverwriteLeadSource() != null ? request.getOverwriteLeadSource() : false);
        leadConvert.setDoNotCreateOpportunity(
            request.getDoNotCreateOpportunity() != null ? request.getDoNotCreateOpportunity() : false);
        leadConvert.setSendNotificationEmail(
            request.getSendNotificationEmail() != null ? request.getSendNotificationEmail() : false);
        
        // 3. 执行转换
        LeadConvertResult[] results = connection.convertLead(new LeadConvert[] { leadConvert });
        
        // 4. 转换结果
        if (results != null && results.length > 0) {
            return convertToVo(results[0]);
        }
        
        throw new SalesforceOperationException("线索转换返回结果为空");
        
    } catch (ConnectionException e) {
        log.error("线索转换连接异常", e);
        throw new SalesforceAuthException("连接 Salesforce 失败: " + e.getMessage());
    } catch (ApiFault e) {
        log.error("线索转换 API 异常", e);
        throw new SalesforceOperationException("线索转换失败: " + e.getExceptionMessage());
    }
}

2. 回收站清空实现

@Override
public EmptyRecycleBinResultVo emptyRecycleBin(EmptyRecycleBinRequest request) {
    try {
        // 1. 获取连接
        PartnerConnection connection = partnerConnectionFactory.getConnection("source");
        
        // 2. 执行清空
        String[] ids = request.getIds().toArray(new String[0]);
        EmptyRecycleBinResult[] results = connection.emptyRecycleBin(ids);
        
        // 3. 转换结果
        EmptyRecycleBinResultVo vo = new EmptyRecycleBinResultVo();
        List<EmptyRecycleBinResultVo.Item> items = new ArrayList<>();
        boolean allSuccess = true;
        
        for (EmptyRecycleBinResult result : results) {
            EmptyRecycleBinResultVo.Item item = new EmptyRecycleBinResultVo.Item();
            item.setId(result.getId());
            item.setSuccess(result.isSuccess());
            
            if (result.getErrors() != null) {
                item.setErrors(convertErrors(result.getErrors()));
            }
            
            if (!result.isSuccess()) {
                allSuccess = false;
            }
            
            items.add(item);
        }
        
        vo.setResults(items);
        vo.setSuccess(allSuccess);
        
        return vo;
        
    } catch (ConnectionException e) {
        log.error("清空回收站连接异常", e);
        throw new SalesforceAuthException("连接 Salesforce 失败: " + e.getMessage());
    } catch (ApiFault e) {
        log.error("清空回收站 API 异常", e);
        throw new SalesforceOperationException("清空回收站失败: " + e.getExceptionMessage());
    }
}

3. 流程提交实现

@Override
public ProcessResultVo processSubmitRequest(ProcessSubmitRequest request) {
    try {
        // 1. 获取连接
        PartnerConnection connection = partnerConnectionFactory.getConnection("source");
        
        // 2. 创建 ProcessSubmitRequest 对象
        com.sforce.soap.partner.ProcessSubmitRequest submitRequest = 
            new com.sforce.soap.partner.ProcessSubmitRequest();
        submitRequest.setObjectId(request.getObjectId());
        
        if (StringUtils.hasText(request.getComments())) {
            submitRequest.setComments(request.getComments());
        }
        
        // 3. 执行提交
        ProcessResult[] results = connection.processSubmitRequest(
            new com.sforce.soap.partner.ProcessSubmitRequest[] { submitRequest });
        
        // 4. 转换结果
        if (results != null && results.length > 0) {
            return convertToVo(results[0]);
        }
        
        throw new SalesforceOperationException("流程提交返回结果为空");
        
    } catch (ConnectionException e) {
        log.error("流程提交连接异常", e);
        throw new SalesforceAuthException("连接 Salesforce 失败: " + e.getMessage());
    } catch (ApiFault e) {
        log.error("流程提交 API 异常", e);
        throw new SalesforceOperationException("流程提交失败: " + e.getExceptionMessage());
    }
}

4. 获取服务器时间戳实现

@Override
public ServerTimestampVo getServerTimestamp() {
    try {
        // 1. 获取连接
        PartnerConnection connection = partnerConnectionFactory.getConnection("source");
        
        // 2. 获取时间戳
        Calendar timestamp = connection.getServerTimestamp();
        
        // 3. 转换结果
        ServerTimestampVo vo = new ServerTimestampVo();
        
        // 转换为 ISO 8601 格式
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
        sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
        vo.setTimestamp(sdf.format(timestamp.getTime()));
        
        vo.setTimeZone(timestamp.getTimeZone().getID());
        vo.setSuccess(true);
        
        return vo;
        
    } catch (ConnectionException e) {
        log.error("获取服务器时间戳连接异常", e);
        throw new SalesforceAuthException("连接 Salesforce 失败: " + e.getMessage());
    }
}

异常处理设计

异常映射表

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

统一异常处理

@ExceptionHandler(SalesforceOperationException.class)
public AjaxResult handleOperationException(SalesforceOperationException e) {
    log.error("操作异常: {}", e.getMessage());
    return AjaxResult.error(e.getMessage());
}

@ExceptionHandler(SalesforceAuthException.class)
public AjaxResult handleSalesforceAuthException(SalesforceAuthException e) {
    log.error("Salesforce 认证异常: {}", e.getMessage());
    return AjaxResult.error("Salesforce 认证失败: " + e.getMessage());
}

性能优化设计

1. 连接复用

  • 使用 PartnerConnectionFactory 的连接缓存机制
  • 避免频繁创建和销毁连接

2. 批量处理

  • 清空回收站支持批量操作(最多 200 个)
  • 减少 API 调用次数

3. 参数验证

  • 在 Service 层进行参数验证
  • 提前发现错误,减少无效 API 调用

安全设计

1. 权限控制

  • 所有接口需要登录才能访问
  • 实际权限由 Salesforce API 控制

2. 数据验证

  • 使用 JSR-303 注解进行参数验证
  • 验证 ID 格式正确性

3. 敏感操作确认

  • 清空回收站为不可逆操作
  • 建议在 UI 层增加二次确认

4. 日志记录

  • 记录所有高级操作的日志
  • 便于审计和故障排查

相关文档