# Prompt - Salesforce组织配置管理 ## 输入引用 引用相关的 docs 文档链接: - [REQ-010-3.md](../requirements/REQ-010-3.md) - Salesforce组织配置管理需求文档 - [REQ-010-1.md](../requirements/REQ-010-1.md) - 数据库表结构设计和创建 - [REQ-010-2.md](../requirements/REQ-010-2.md) - 基础实体类和Mapper创建 - [0012-org-config-management.md](../decisions/adr/0012-org-config-management.md) - Salesforce组织配置管理架构决策 ## Context Maps 强制列出本次 Prompt 依赖的 Canvas 文件: - [Authentication.canvas](../../Authentication.canvas) - 项目架构视觉化展示 - **相关节点**: [SessionManager](node_session_manager_detail) - 会话管理,提供登录服务 - **相关节点**: [集成核心](node_integration_core) - 提供与Salesforce的各种连接方式 ## 目标 实现 Salesforce 组织配置的完整管理功能,包括: 1. 组织配置 CRUD 功能 - 支持多环境配置、分页查询、条件查询 2. OAuth 认证信息加密存储 - 使用 AES 加密算法,确保安全性 3. 环境类型管理 - 支持 Sandbox/Production 环境类型 4. 存储根路径配置 - 支持 OSS 和本地文件系统 5. 连接状态管理 - 支持 Active/Inactive/Auth_Invalid 状态 6. 配置验证功能 - 验证 OAuth 认证信息和连接可用性 ## 输出格式 ### 代码输出格式 #### 1. 枚举类 ```java 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. 加密工具类 ```java 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. 控制器 ```java 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 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. 服务接口 ```java 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 getOrgConfigList(int page, int size, String orgName, String environmentType); } ``` #### 5. 服务实现 ```java 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 getOrgConfigList(int page, int size, String orgName, String environmentType) { QueryWrapper 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. 配置验证服务接口 ```java package com.datai.salesforce.metadata.service; import com.datai.salesforce.metadata.validation.OrgConfigValidationResult; /** * 组织配置验证服务接口 */ public interface IOrgConfigValidationService { /** * 验证组织配置 */ OrgConfigValidationResult validateOrgConfig(Long id); } ``` #### 7. 配置验证服务实现 ```java 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. 验证结果类 ```java 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 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. 配置文件 ```yaml encryption: key: your-32-byte-encryption-key-here ``` #### 10. 单元测试 ```java 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 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 | 验证功能正常,符合需求 | 无 |