datai/docs/archive/prompts/003-org-config-management.md

639 lines
20 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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<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. 服务接口
```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<DataiMetaOrgConfig> 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<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. 配置验证服务接口
```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<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. 配置文件
```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<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 | 验证功能正常,符合需求 | 无 |