31 KiB
31 KiB
设计文档 - 高级功能
元数据
- 需求编号: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 数量不超过 200(Salesforce 限制)
- 验证每个 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. 日志记录
- 记录所有高级操作的日志
- 便于审计和故障排查
相关文档
- 需求文档 - 高级功能需求文档
- 决策记录 - 已创建
- 提示词文档 - 待创建
- 认证和会话管理设计文档 - GetUserInfo 复用参考
- Partner API 模块说明 - Partner API 文档