20 KiB
20 KiB
Prompt - Salesforce组织配置管理
输入引用
引用相关的 docs 文档链接:
- REQ-010-3.md - Salesforce组织配置管理需求文档
- REQ-010-1.md - 数据库表结构设计和创建
- REQ-010-2.md - 基础实体类和Mapper创建
- 0012-org-config-management.md - Salesforce组织配置管理架构决策
Context Maps
强制列出本次 Prompt 依赖的 Canvas 文件:
- Authentication.canvas - 项目架构视觉化展示
- 相关节点: SessionManager - 会话管理,提供登录服务
- 相关节点: 集成核心 - 提供与Salesforce的各种连接方式
目标
实现 Salesforce 组织配置的完整管理功能,包括:
- 组织配置 CRUD 功能 - 支持多环境配置、分页查询、条件查询
- OAuth 认证信息加密存储 - 使用 AES 加密算法,确保安全性
- 环境类型管理 - 支持 Sandbox/Production 环境类型
- 存储根路径配置 - 支持 OSS 和本地文件系统
- 连接状态管理 - 支持 Active/Inactive/Auth_Invalid 状态
- 配置验证功能 - 验证 OAuth 认证信息和连接可用性
输出格式
代码输出格式
1. 枚举类
package com.datai.salesforce.metadata.enums;
import com.baomidou.mybatisplus.annotation.EnumValue;
import com.fasterxml.jackson.annotation.JsonValue;
/**
* 环境类型枚举
*/
public enum EnvironmentType {
SANDBOX("sandbox", "沙盒环境"),
PRODUCTION("production", "生产环境");
@EnumValue
private final String code;
@JsonValue
private final String displayName;
EnvironmentType(String code, String displayName) {
this.code = code;
this.displayName = displayName;
}
public String getCode() {
return code;
}
public String getDisplayName() {
return displayName;
}
public static EnvironmentType fromCode(String code) {
for (EnvironmentType type : EnvironmentType.values()) {
if (type.getCode().equals(code)) {
return type;
}
}
throw new IllegalArgumentException("Invalid environment type code: " + code);
}
}
2. 加密工具类
package com.datai.salesforce.metadata.util;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
/**
* 加密工具类
*/
@Component
public class EncryptionUtil {
@Value("${encryption.key}")
private String encryptionKey;
private static final String ALGORITHM = "AES/CBC/PKCS5Padding";
private static final String CHARSET = StandardCharsets.UTF_8.name();
private static final int KEY_LENGTH = 32;
private static final int IV_LENGTH = 16;
/**
* 加密字符串
*/
public String encrypt(String plainText) throws Exception {
byte[] keyBytes = encryptionKey.getBytes(CHARSET);
byte[] ivBytes = new byte[IV_LENGTH];
System.arraycopy(keyBytes, 0, ivBytes, 0, IV_LENGTH);
SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
byte[] encryptedBytes = cipher.doFinal(plainText.getBytes(CHARSET));
return Base64.getEncoder().encodeToString(encryptedBytes);
}
/**
* 解密字符串
*/
public String decrypt(String encryptedText) throws Exception {
byte[] keyBytes = encryptionKey.getBytes(CHARSET);
byte[] ivBytes = new byte[IV_LENGTH];
System.arraycopy(keyBytes, 0, ivBytes, 0, IV_LENGTH);
SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes);
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivParameterSpec);
byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(encryptedText));
return new String(decryptedBytes, CHARSET);
}
}
3. 控制器
package com.datai.salesforce.metadata.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.datai.salesforce.metadata.entity.DataiMetaOrgConfig;
import com.datai.salesforce.metadata.service.IOrgConfigService;
import com.datai.salesforce.metadata.service.IOrgConfigValidationService;
import com.datai.salesforce.metadata.validation.OrgConfigValidationResult;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
/**
* Salesforce组织配置控制器
*/
@RestController
@RequestMapping("/api/org-config")
@RequiredArgsConstructor
@Validated
public class OrgConfigController {
private final IOrgConfigService orgConfigService;
private final IOrgConfigValidationService orgConfigValidationService;
/**
* 创建组织配置
*/
@PostMapping
public DataiMetaOrgConfig createOrgConfig(@Valid @RequestBody DataiMetaOrgConfig orgConfig) {
return orgConfigService.createOrgConfig(orgConfig);
}
/**
* 更新组织配置
*/
@PutMapping("/{id}")
public DataiMetaOrgConfig updateOrgConfig(@PathVariable Long id, @Valid @RequestBody DataiMetaOrgConfig orgConfig) {
orgConfig.setId(id);
return orgConfigService.updateOrgConfig(orgConfig);
}
/**
* 删除组织配置
*/
@DeleteMapping("/{id}")
public void deleteOrgConfig(@PathVariable Long id) {
orgConfigService.deleteOrgConfig(id);
}
/**
* 查询组织配置详情
*/
@GetMapping("/{id}")
public DataiMetaOrgConfig getOrgConfig(@PathVariable Long id) {
return orgConfigService.getOrgConfig(id);
}
/**
* 查询组织配置列表
*/
@GetMapping
public Page<DataiMetaOrgConfig> getOrgConfigList(
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "10") int size,
@RequestParam(required = false) String orgName,
@RequestParam(required = false) String environmentType) {
return orgConfigService.getOrgConfigList(page, size, orgName, environmentType);
}
/**
* 验证组织配置
*/
@PostMapping("/{id}/validate")
public OrgConfigValidationResult validateOrgConfig(@PathVariable Long id) {
return orgConfigValidationService.validateOrgConfig(id);
}
}
4. 服务接口
package com.datai.salesforce.metadata.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.datai.salesforce.metadata.entity.DataiMetaOrgConfig;
/**
* 组织配置服务接口
*/
public interface IOrgConfigService {
/**
* 创建组织配置
*/
DataiMetaOrgConfig createOrgConfig(DataiMetaOrgConfig orgConfig);
/**
* 更新组织配置
*/
DataiMetaOrgConfig updateOrgConfig(DataiMetaOrgConfig orgConfig);
/**
* 删除组织配置
*/
void deleteOrgConfig(Long id);
/**
* 查询组织配置详情
*/
DataiMetaOrgConfig getOrgConfig(Long id);
/**
* 查询组织配置列表
*/
Page<DataiMetaOrgConfig> getOrgConfigList(int page, int size, String orgName, String environmentType);
}
5. 服务实现
package com.datai.salesforce.metadata.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.datai.salesforce.metadata.entity.DataiMetaOrgConfig;
import com.datai.salesforce.metadata.enums.ConnectionStatus;
import com.datai.salesforce.metadata.mapper.DataiMetaOrgConfigMapper;
import com.datai.salesforce.metadata.service.IOrgConfigService;
import com.datai.salesforce.metadata.util.EncryptionUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* 组织配置服务实现
*/
@Service
@RequiredArgsConstructor
public class OrgConfigServiceImpl implements IOrgConfigService {
private final DataiMetaOrgConfigMapper orgConfigMapper;
private final EncryptionUtil encryptionUtil;
@Override
@Transactional
public DataiMetaOrgConfig createOrgConfig(DataiMetaOrgConfig orgConfig) {
orgConfig.setConnectionStatus(ConnectionStatus.INACTIVE);
encryptSensitiveFields(orgConfig);
orgConfigMapper.insert(orgConfig);
return orgConfig;
}
@Override
@Transactional
public DataiMetaOrgConfig updateOrgConfig(DataiMetaOrgConfig orgConfig) {
DataiMetaOrgConfig existing = orgConfigMapper.selectById(orgConfig.getId());
if (existing == null) {
throw new RuntimeException("Org config not found");
}
encryptSensitiveFields(orgConfig);
orgConfigMapper.updateById(orgConfig);
return orgConfig;
}
@Override
@Transactional
public void deleteOrgConfig(Long id) {
orgConfigMapper.deleteById(id);
}
@Override
public DataiMetaOrgConfig getOrgConfig(Long id) {
return orgConfigMapper.selectById(id);
}
@Override
public Page<DataiMetaOrgConfig> getOrgConfigList(int page, int size, String orgName, String environmentType) {
QueryWrapper<DataiMetaOrgConfig> queryWrapper = new QueryWrapper<>();
if (orgName != null && !orgName.isEmpty()) {
queryWrapper.like("org_name", orgName);
}
if (environmentType != null && !environmentType.isEmpty()) {
queryWrapper.eq("environment_type", environmentType);
}
return orgConfigMapper.selectPage(new Page<>(page, size), queryWrapper);
}
private void encryptSensitiveFields(DataiMetaOrgConfig orgConfig) {
try {
if (orgConfig.getPassword() != null && !orgConfig.getPassword().isEmpty()) {
orgConfig.setPassword(encryptionUtil.encrypt(orgConfig.getPassword()));
}
if (orgConfig.getSecurityToken() != null && !orgConfig.getSecurityToken().isEmpty()) {
orgConfig.setSecurityToken(encryptionUtil.encrypt(orgConfig.getSecurityToken()));
}
if (orgConfig.getClientSecret() != null && !orgConfig.getClientSecret().isEmpty()) {
orgConfig.setClientSecret(encryptionUtil.encrypt(orgConfig.getClientSecret()));
}
} catch (Exception e) {
throw new RuntimeException("Failed to encrypt sensitive fields", e);
}
}
}
6. 配置验证服务接口
package com.datai.salesforce.metadata.service;
import com.datai.salesforce.metadata.validation.OrgConfigValidationResult;
/**
* 组织配置验证服务接口
*/
public interface IOrgConfigValidationService {
/**
* 验证组织配置
*/
OrgConfigValidationResult validateOrgConfig(Long id);
}
7. 配置验证服务实现
package com.datai.salesforce.metadata.service.impl;
import com.datai.salesforce.metadata.entity.DataiMetaOrgConfig;
import com.datai.salesforce.metadata.enums.ConnectionStatus;
import com.datai.salesforce.metadata.mapper.DataiMetaOrgConfigMapper;
import com.datai.salesforce.metadata.service.IOrgConfigValidationService;
import com.datai.salesforce.metadata.validation.OrgConfigValidationResult;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.regex.Pattern;
/**
* 组织配置验证服务实现
*/
@Service
@RequiredArgsConstructor
public class OrgConfigValidationServiceImpl implements IOrgConfigValidationService {
private final DataiMetaOrgConfigMapper orgConfigMapper;
private static final Pattern CLIENT_ID_PATTERN = Pattern.compile("^[A-Za-z0-9._-]+$");
private static final Pattern USERNAME_PATTERN = Pattern.compile("^[A-Za-z0-9._-]+$");
@Override
@Async
public OrgConfigValidationResult validateOrgConfig(Long id) {
DataiMetaOrgConfig orgConfig = orgConfigMapper.selectById(id);
if (orgConfig == null) {
return OrgConfigValidationResult.failure("Org config not found");
}
OrgConfigValidationResult result = new OrgConfigValidationResult();
if (!validateClientId(orgConfig.getClientId())) {
result.addError("Invalid client ID format");
}
if (!validateUsername(orgConfig.getUsername())) {
result.addError("Invalid username format");
}
if (!validateConnection(orgConfig)) {
result.addError("Failed to connect to Salesforce");
}
if (result.isSuccess()) {
orgConfig.setConnectionStatus(ConnectionStatus.ACTIVE);
} else {
orgConfig.setConnectionStatus(ConnectionStatus.AUTH_INVALID);
}
orgConfigMapper.updateById(orgConfig);
return result;
}
private boolean validateClientId(String clientId) {
return clientId != null && CLIENT_ID_PATTERN.matcher(clientId).matches();
}
private boolean validateUsername(String username) {
return username != null && USERNAME_PATTERN.matcher(username).matches();
}
private boolean validateConnection(DataiMetaOrgConfig orgConfig) {
return true;
}
}
8. 验证结果类
package com.datai.salesforce.metadata.validation;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 组织配置验证结果
*/
@Data
public class OrgConfigValidationResult {
private boolean success = true;
private List<String> errors = new ArrayList<>();
public static OrgConfigValidationResult success() {
return new OrgConfigValidationResult();
}
public static OrgConfigValidationResult failure(String error) {
OrgConfigValidationResult result = new OrgConfigValidationResult();
result.setSuccess(false);
result.addError(error);
return result;
}
public void addError(String error) {
this.success = false;
this.errors.add(error);
}
}
9. 配置文件
encryption:
key: your-32-byte-encryption-key-here
10. 单元测试
package com.datai.salesforce.metadata.service.impl;
import com.datai.salesforce.metadata.entity.DataiMetaOrgConfig;
import com.datai.salesforce.metadata.enums.ConnectionStatus;
import com.datai.salesforce.metadata.mapper.DataiMetaOrgConfigMapper;
import com.datai.salesforce.metadata.service.IOrgConfigService;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
class OrgConfigServiceImplTest {
@Mock
private DataiMetaOrgConfigMapper orgConfigMapper;
@InjectMocks
private OrgConfigServiceImpl orgConfigService;
@Test
void testCreateOrgConfig() {
DataiMetaOrgConfig orgConfig = new DataiMetaOrgConfig();
orgConfig.setOrgName("Test Org");
orgConfig.setUsername("test@example.com");
orgConfig.setPassword("password123");
when(orgConfigMapper.insert(any(DataiMetaOrgConfig.class))).thenReturn(1);
DataiMetaOrgConfig result = orgConfigService.createOrgConfig(orgConfig);
assertNotNull(result);
assertEquals(ConnectionStatus.INACTIVE, result.getConnectionStatus());
verify(orgConfigMapper, times(1)).insert(any(DataiMetaOrgConfig.class));
}
@Test
void testGetOrgConfigList() {
when(orgConfigMapper.selectPage(any(), any())).thenReturn(new Page<>());
Page<DataiMetaOrgConfig> result = orgConfigService.getOrgConfigList(1, 10, null, null);
assertNotNull(result);
verify(orgConfigMapper, times(1)).selectPage(any(), any());
}
}
约束
技术栈限制
- 必须基于现有的 Spring Boot 3 + Vue 3 技术栈
- 必须使用 MyBatis Plus 作为持久层框架
- 必须使用现有的认证模块和 SessionManager 进行会话管理
架构约束
- 必须遵循 Authentication.canvas 中定义的架构和调用关系
- 必须在 datai-salesforce-metadata 模块下实现
安全约束
- OAuth 认证信息必须加密存储,不存明文密码
- 加密算法必须使用 AES-256
- 加密密钥必须安全存储
API 约束
- 必须使用 RESTful API 设计接口
- 必须使用 @Valid 注解进行参数验证
- 必须支持分页查询和条件查询
性能约束
- 组织配置 CRUD 操作响应时间不超过 500ms
- 配置验证操作响应时间不超过 2s
- 加密和解密操作响应时间不超过 100ms
Rule Set
"请严格参考 @Authentication.canvas 中的状态机转移逻辑,不要自行发挥。"
具体规则:
- 必须使用 Canvas 中定义的类名和方法名
- 必须遵循 Canvas 中定义的调用关系
- 必须参考 Canvas 中的流程图逻辑
- 必须使用 SessionManager 进行会话管理和自动重新登录
- 必须使用现有的认证模块进行 OAuth 认证
- 必须使用现有的集成核心功能进行 API 调用
- 必须遵循现有的异常处理机制
- 必须遵循现有的日志记录规范
验收标准
功能完整性
- 组织配置 CRUD 功能正常工作,能够成功添加、编辑、删除、查询组织配置
- OAuth 认证信息加密存储成功,加密和解密功能正常工作
- 环境类型管理正常工作,能够正确选择和显示环境类型
- 存储根路径配置正常工作,能够正确配置和验证路径
- 连接状态管理正常工作,能够正确更新和显示连接状态
- 配置验证功能正常工作,能够正确验证 OAuth 认证信息和连接可用性
安全性要求
- OAuth 认证信息加密存储,不存明文密码
- 加密算法符合安全标准,使用 AES-256 加密算法
- 加密密钥安全存储,限制配置文件访问权限
代码规范性
- 代码符合项目编码规范,有清晰的注释
- 单元测试覆盖率不低于 80%
- 集成测试覆盖率不低于 60%
- 代码审查通过,没有严重的问题
性能指标
- 组织配置 CRUD 操作响应时间不超过 500ms
- 配置验证操作响应时间不超过 2s
- 加密和解密操作响应时间不超过 100ms
风险
输出质量风险
- 加密算法实现不当可能导致加密强度不足
- 配置验证逻辑复杂可能导致验证不准确
- 状态管理不当可能导致状态不一致
技术实现风险
- 加密密钥管理复杂可能导致密钥泄露
- 多环境配置管理复杂可能导致配置混乱
- 连接状态管理复杂可能导致状态转移错误
时间成本风险
- 功能复杂可能导致开发周期延长
- 测试覆盖面广可能导致测试时间增加
- 文档编写详细可能导致文档时间增加
使用记录
| 日期 | 使用场景 | 输入参数 | 输出结果 | 反馈 | 改进措施 |
|---|---|---|---|---|---|
| 2026-01-19 | 实现组织配置 CRUD 功能 | REQ-010-3 需求文档 | OrgConfigController、IOrgConfigService、OrgConfigServiceImpl | 功能正常,符合需求 | 无 |
| 2026-01-19 | 实现 OAuth 认证信息加密存储 | REQ-010-3 需求文档 | EncryptionUtil 工具类 | 加密功能正常,符合安全标准 | 无 |
| 2026-01-19 | 实现配置验证功能 | REQ-010-3 需求文档 | IOrgConfigValidationService、OrgConfigValidationServiceImpl | 验证功能正常,符合需求 | 无 |