mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-22 07:03:53 +08:00
修复秘钥污染的bug
This commit is contained in:
@@ -5,35 +5,39 @@ import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 敏感数据处理工具类
|
||||
*/
|
||||
public class SensitiveDataUtils {
|
||||
|
||||
|
||||
// 敏感字段列表
|
||||
private static final Set<String> SENSITIVE_FIELDS = new HashSet<>(Arrays.asList(
|
||||
"api_key", "personal_access_token", "access_token", "token",
|
||||
"secret", "access_key_secret", "secret_key"
|
||||
));
|
||||
|
||||
"api_key", "personal_access_token", "access_token", "token",
|
||||
"secret", "access_key_secret", "secret_key"));
|
||||
|
||||
/**
|
||||
* 检查字段是否为敏感字段
|
||||
*/
|
||||
public static boolean isSensitiveField(String fieldName) {
|
||||
return StringUtils.isNotBlank(fieldName) && SENSITIVE_FIELDS.contains(fieldName.toLowerCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏字符串中间部分
|
||||
* @param value 原始字符串
|
||||
* @return 隐藏后的字符串
|
||||
*/
|
||||
public static String maskMiddle(String value) {
|
||||
if (StringUtils.isBlank(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
int length = value.length();
|
||||
if (length <= 8) {
|
||||
// 字符串太短,返回前2后2,中间用*代替
|
||||
return value.substring(0, 2) + "****" + value.substring(length - 2);
|
||||
} else {
|
||||
// 返回前4后4,中间用*代替
|
||||
int maskLength = length - 8;
|
||||
StringBuilder maskBuilder = new StringBuilder();
|
||||
for (int i = 0; i < maskLength; i++) {
|
||||
@@ -42,43 +46,46 @@ public class SensitiveDataUtils {
|
||||
return value.substring(0, 4) + maskBuilder.toString() + value.substring(length - 4);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断字符串是否是被掩码处理过的值
|
||||
*/
|
||||
public static boolean isMaskedValue(String value) {
|
||||
if (StringUtils.isBlank(value)) {
|
||||
return false;
|
||||
}
|
||||
// 掩码值至少包含4个连续的*
|
||||
return value.contains("****");
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理JSONObject中的敏感字段
|
||||
* @param jsonObject 原始JSONObject
|
||||
* @return 处理后的JSONObject副本
|
||||
*/
|
||||
public static JSONObject maskSensitiveFields(JSONObject jsonObject) {
|
||||
if (jsonObject == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 创建副本避免修改原始数据
|
||||
|
||||
JSONObject result = new JSONObject();
|
||||
|
||||
|
||||
for (String key : jsonObject.keySet()) {
|
||||
Object value = jsonObject.get(key);
|
||||
|
||||
|
||||
if (SENSITIVE_FIELDS.contains(key.toLowerCase()) && value instanceof String) {
|
||||
// 处理敏感字段
|
||||
result.put(key, maskMiddle((String) value));
|
||||
} else if (value instanceof JSONObject) {
|
||||
// 递归处理嵌套的JSONObject
|
||||
result.put(key, maskSensitiveFields((JSONObject) value));
|
||||
} else {
|
||||
// 非敏感字段保持不变
|
||||
result.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 比较两个JSONObject的敏感字段处理后是否相同
|
||||
* @param original 原始JSONObject
|
||||
* @param updated 更新后的JSONObject
|
||||
* @return 是否相同
|
||||
* 比较两个JSONObject的敏感字段是否相同
|
||||
* 特别针对api_key等敏感字段进行单独比较
|
||||
*/
|
||||
public static boolean isSensitiveDataEqual(JSONObject original, JSONObject updated) {
|
||||
if (original == null && updated == null) {
|
||||
@@ -87,10 +94,69 @@ public class SensitiveDataUtils {
|
||||
if (original == null || updated == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
JSONObject maskedOriginal = maskSensitiveFields(original);
|
||||
JSONObject maskedUpdated = maskSensitiveFields(updated);
|
||||
|
||||
return maskedOriginal.toString().equals(maskedUpdated.toString());
|
||||
|
||||
// 提取并比较特定敏感字段
|
||||
return compareSpecificSensitiveFields(original, updated, "api_key") &&
|
||||
compareSpecificSensitiveFields(original, updated, "personal_access_token") &&
|
||||
compareSpecificSensitiveFields(original, updated, "access_token") &&
|
||||
compareSpecificSensitiveFields(original, updated, "token") &&
|
||||
compareSpecificSensitiveFields(original, updated, "secret") &&
|
||||
compareSpecificSensitiveFields(original, updated, "access_key_secret") &&
|
||||
compareSpecificSensitiveFields(original, updated, "secret_key");
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较两个JSON对象中特定敏感字段是否相同
|
||||
* 遍历整个JSON对象树,查找并比较指定敏感字段
|
||||
*/
|
||||
private static boolean compareSpecificSensitiveFields(JSONObject original, JSONObject updated, String fieldName) {
|
||||
// 提取原始对象中的指定敏感字段
|
||||
Map<String, String> originalFields = new HashMap<>();
|
||||
extractSpecificSensitiveField(original, originalFields, fieldName, "");
|
||||
|
||||
// 提取更新对象中的指定敏感字段
|
||||
Map<String, String> updatedFields = new HashMap<>();
|
||||
extractSpecificSensitiveField(updated, updatedFields, fieldName, "");
|
||||
|
||||
// 如果字段数量不同,说明有增删
|
||||
if (originalFields.size() != updatedFields.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 比较每个字段的值
|
||||
for (Map.Entry<String, String> entry : originalFields.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
String originalValue = entry.getValue();
|
||||
String updatedValue = updatedFields.get(key);
|
||||
|
||||
if (updatedValue == null || !updatedValue.equals(originalValue)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归提取JSON对象中指定名称的敏感字段
|
||||
*/
|
||||
private static void extractSpecificSensitiveField(JSONObject jsonObject, Map<String, String> fieldsMap,
|
||||
String targetFieldName, String parentPath) {
|
||||
if (jsonObject == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (String key : jsonObject.keySet()) {
|
||||
String fullPath = parentPath.isEmpty() ? key : parentPath + "." + key;
|
||||
Object value = jsonObject.get(key);
|
||||
|
||||
if (value instanceof JSONObject) {
|
||||
// 递归处理嵌套JSON对象
|
||||
extractSpecificSensitiveField((JSONObject) value, fieldsMap, targetFieldName, fullPath);
|
||||
} else if (value instanceof String && key.equalsIgnoreCase(targetFieldName)) {
|
||||
// 找到目标敏感字段,保存其路径和值
|
||||
fieldsMap.put(fullPath, (String) value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -300,7 +300,7 @@ public class ConfigServiceImpl implements ConfigService {
|
||||
Map<String, Object> voiceprintConfig = new HashMap<>();
|
||||
voiceprintConfig.put("url", voiceprintUrl);
|
||||
voiceprintConfig.put("speakers", speakers);
|
||||
|
||||
|
||||
// 获取声纹识别相似度阈值,默认0.4
|
||||
String thresholdStr = sysParamsService.getValue("server.voiceprint_similarity_threshold", true);
|
||||
if (StringUtils.isNotBlank(thresholdStr) && !"null".equals(thresholdStr)) {
|
||||
@@ -389,9 +389,9 @@ public class ConfigServiceImpl implements ConfigService {
|
||||
if (model.getConfigJson() != null) {
|
||||
// 复制一份配置,避免修改原始数据
|
||||
JSONObject configJsonCopy = new JSONObject(model.getConfigJson());
|
||||
|
||||
|
||||
typeConfig.put(model.getId(), configJsonCopy);
|
||||
|
||||
|
||||
}
|
||||
result.put(modelTypes[i], typeConfig);
|
||||
|
||||
|
||||
+343
-195
@@ -1,5 +1,6 @@
|
||||
package xiaozhi.modules.model.service.impl;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -11,8 +12,8 @@ import org.springframework.stereotype.Service;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.metadata.OrderItem;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import com.baomidou.mybatisplus.core.metadata.OrderItem;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
@@ -38,8 +39,6 @@ import xiaozhi.modules.model.entity.ModelConfigEntity;
|
||||
import xiaozhi.modules.model.service.ModelConfigService;
|
||||
import xiaozhi.modules.model.service.ModelProviderService;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, ModelConfigEntity>
|
||||
@@ -69,7 +68,7 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
|
||||
.eq("is_enabled", 1)
|
||||
.like(StringUtils.isNotBlank(modelName), "model_name", "%" + modelName + "%")
|
||||
.select("id", "model_name", "config_json"));
|
||||
// 处理获取到的内容
|
||||
|
||||
return entities.stream().map(item -> {
|
||||
LlmModelBasicInfoDTO dto = new LlmModelBasicInfoDTO();
|
||||
dto.setId(item.getId());
|
||||
@@ -82,139 +81,386 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
|
||||
|
||||
@Override
|
||||
public PageData<ModelConfigDTO> getPageList(String modelType, String modelName, String page, String limit) {
|
||||
Map<String, Object> params = new HashMap<String, Object>();
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put(Constant.PAGE, page);
|
||||
params.put(Constant.LIMIT, limit);
|
||||
|
||||
// 不再使用默认的getPage方法,而是直接创建Page对象并自定义排序
|
||||
|
||||
long curPage = Long.parseLong(page);
|
||||
long pageSize = Long.parseLong(limit);
|
||||
Page<ModelConfigEntity> pageInfo = new Page<>(curPage, pageSize);
|
||||
|
||||
|
||||
// 添加排序规则:先按is_enabled降序,再按sort升序
|
||||
pageInfo.addOrder(OrderItem.desc("is_enabled"));
|
||||
pageInfo.addOrder(OrderItem.asc("sort"));
|
||||
|
||||
// 执行分页查询
|
||||
|
||||
IPage<ModelConfigEntity> modelConfigEntityIPage = modelConfigDao.selectPage(
|
||||
pageInfo,
|
||||
new QueryWrapper<ModelConfigEntity>()
|
||||
.eq("model_type", modelType)
|
||||
.like(StringUtils.isNotBlank(modelName), "model_name", "%" + modelName + "%"));
|
||||
|
||||
|
||||
return getPageData(modelConfigEntityIPage, ModelConfigDTO.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelConfigDTO edit(String modelType, String provideCode, String id, ModelConfigBodyDTO modelConfigBodyDTO) {
|
||||
// 先验证有没有供应器
|
||||
if (StringUtils.isBlank(modelType) || StringUtils.isBlank(provideCode)) {
|
||||
throw new RenException("modelType和provideCode不能为空");
|
||||
}
|
||||
List<ModelProviderDTO> providerList = modelProviderService.getList(modelType, provideCode);
|
||||
if (CollectionUtil.isEmpty(providerList)) {
|
||||
throw new RenException(ErrorCode.MODEL_PROVIDER_NOT_EXIST);
|
||||
}
|
||||
|
||||
// 获取原始配置
|
||||
ModelConfigEntity originalEntity = modelConfigDao.selectById(id);
|
||||
if (originalEntity == null) {
|
||||
throw new RenException(ErrorCode.RESOURCE_NOT_FOUND);
|
||||
}
|
||||
|
||||
// 验证LLM配置
|
||||
if (modelConfigBodyDTO.getConfigJson().containsKey("llm")) {
|
||||
String llm = modelConfigBodyDTO.getConfigJson().get("llm").toString();
|
||||
ModelConfigEntity modelConfigEntity = modelConfigDao.selectOne(new LambdaQueryWrapper<ModelConfigEntity>()
|
||||
.eq(ModelConfigEntity::getId, llm));
|
||||
String selectModelType = (modelConfigEntity == null || modelConfigEntity.getModelType() == null) ? null
|
||||
: modelConfigEntity.getModelType().toUpperCase();
|
||||
if (modelConfigEntity == null || !"LLM".equals(selectModelType)) {
|
||||
throw new RenException(ErrorCode.LLM_NOT_EXIST);
|
||||
}
|
||||
String type = modelConfigEntity.getConfigJson().get("type").toString();
|
||||
// 如果查询大语言模型是openai或者ollama,意图识别选参数都可以
|
||||
if (!"openai".equals(type) && !"ollama".equals(type)) {
|
||||
throw new RenException(ErrorCode.INVALID_LLM_TYPE);
|
||||
}
|
||||
}
|
||||
// 1. 参数验证
|
||||
validateEditParameters(modelType, provideCode, id, modelConfigBodyDTO);
|
||||
|
||||
// 再更新供应器提供的模型
|
||||
ModelConfigEntity modelConfigEntity = ConvertUtils.sourceToTarget(modelConfigBodyDTO, ModelConfigEntity.class);
|
||||
modelConfigEntity.setId(id);
|
||||
modelConfigEntity.setModelType(modelType);
|
||||
|
||||
// 检查敏感字段是否有变化
|
||||
if (originalEntity.getConfigJson() != null && modelConfigBodyDTO.getConfigJson() != null) {
|
||||
boolean sensitiveEqual = SensitiveDataUtils.isSensitiveDataEqual(
|
||||
originalEntity.getConfigJson(),
|
||||
modelConfigBodyDTO.getConfigJson()
|
||||
);
|
||||
|
||||
if (sensitiveEqual) {
|
||||
// 敏感数据没有变化,使用原始的configJson值
|
||||
modelConfigEntity.setConfigJson(originalEntity.getConfigJson());
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 验证模型提供者
|
||||
validateModelProvider(modelType, provideCode);
|
||||
|
||||
// 3. 获取原始配置(不经过敏感数据处理)
|
||||
ModelConfigEntity originalEntity = getOriginalConfigFromDb(id);
|
||||
|
||||
// 4. 验证LLM配置
|
||||
validateLlmConfiguration(modelConfigBodyDTO);
|
||||
|
||||
// 5. 准备更新实体并处理敏感数据
|
||||
ModelConfigEntity modelConfigEntity = prepareUpdateEntity(modelConfigBodyDTO, originalEntity, modelType, id);
|
||||
|
||||
// 6. 执行数据库更新
|
||||
modelConfigDao.updateById(modelConfigEntity);
|
||||
|
||||
// 清除缓存
|
||||
redisUtils.delete(RedisKeys.getModelConfigById(modelConfigEntity.getId()));
|
||||
|
||||
// 返回数据前处理敏感字段
|
||||
ModelConfigDTO dto = ConvertUtils.sourceToTarget(modelConfigEntity, ModelConfigDTO.class);
|
||||
if (dto.getConfigJson() != null) {
|
||||
dto.setConfigJson(SensitiveDataUtils.maskSensitiveFields(dto.getConfigJson()));
|
||||
}
|
||||
|
||||
return dto;
|
||||
|
||||
// 7. 清除缓存
|
||||
clearModelCache(id);
|
||||
|
||||
// 8. 返回处理后的数据(包含敏感数据掩码)
|
||||
return buildResponseDTO(modelConfigEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelConfigDTO add(String modelType, String provideCode, ModelConfigBodyDTO modelConfigBodyDTO) {
|
||||
// 先验证有没有供应器
|
||||
if (StringUtils.isBlank(modelType) || StringUtils.isBlank(provideCode)) {
|
||||
throw new RenException(ErrorCode.MODEL_TYPE_PROVIDE_CODE_NOT_NULL);
|
||||
}
|
||||
List<ModelProviderDTO> providerList = modelProviderService.getList(modelType, provideCode);
|
||||
if (CollectionUtil.isEmpty(providerList)) {
|
||||
throw new RenException(ErrorCode.MODEL_PROVIDER_NOT_EXIST);
|
||||
}
|
||||
|
||||
// 保存供应器提供的模型
|
||||
ModelConfigEntity modelConfigEntity = ConvertUtils.sourceToTarget(modelConfigBodyDTO, ModelConfigEntity.class);
|
||||
modelConfigEntity.setModelType(modelType);
|
||||
modelConfigEntity.setIsDefault(0);
|
||||
validateAddParameters(modelType, provideCode, modelConfigBodyDTO);
|
||||
|
||||
validateModelProvider(modelType, provideCode);
|
||||
|
||||
ModelConfigEntity modelConfigEntity = prepareAddEntity(modelConfigBodyDTO, modelType);
|
||||
|
||||
modelConfigDao.insert(modelConfigEntity);
|
||||
|
||||
// 返回数据前处理敏感字段
|
||||
ModelConfigDTO dto = ConvertUtils.sourceToTarget(modelConfigEntity, ModelConfigDTO.class);
|
||||
if (dto.getConfigJson() != null) {
|
||||
dto.setConfigJson(SensitiveDataUtils.maskSensitiveFields(dto.getConfigJson()));
|
||||
}
|
||||
|
||||
return dto;
|
||||
|
||||
return buildResponseDTO(modelConfigEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String id) {
|
||||
// 查看是否是默认
|
||||
if (StringUtils.isBlank(id)) {
|
||||
throw new RenException(ErrorCode.IDENTIFIER_NOT_NULL);
|
||||
}
|
||||
|
||||
ModelConfigEntity modelConfig = modelConfigDao.selectById(id);
|
||||
if (modelConfig != null && modelConfig.getIsDefault() == 1) {
|
||||
throw new RenException(ErrorCode.DEFAULT_MODEL_DELETE_ERROR);
|
||||
}
|
||||
// 验证是否有引用
|
||||
|
||||
checkAgentReference(id);
|
||||
checkIntentConfigReference(id);
|
||||
|
||||
modelConfigDao.deleteById(id);
|
||||
|
||||
clearModelCache(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModelNameById(String id) {
|
||||
if (StringUtils.isBlank(id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String cacheKey = RedisKeys.getModelNameById(id);
|
||||
String cachedName = (String) redisUtils.get(cacheKey);
|
||||
if (StringUtils.isNotBlank(cachedName)) {
|
||||
return cachedName;
|
||||
}
|
||||
|
||||
ModelConfigEntity entity = modelConfigDao.selectById(id);
|
||||
if (entity != null) {
|
||||
String modelName = entity.getModelName();
|
||||
if (StringUtils.isNotBlank(modelName)) {
|
||||
redisUtils.set(cacheKey, modelName);
|
||||
}
|
||||
return modelName;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelConfigEntity selectById(Serializable id) {
|
||||
ModelConfigEntity entity = super.selectById(id);
|
||||
if (entity != null && entity.getConfigJson() != null) {
|
||||
entity.setConfigJson(maskSensitiveFields(entity.getConfigJson()));
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected <D> PageData<D> getPageData(IPage<?> page, Class<D> target) {
|
||||
List<?> records = page.getRecords();
|
||||
if (records != null && !records.isEmpty()) {
|
||||
for (Object record : records) {
|
||||
if (record instanceof ModelConfigEntity) {
|
||||
ModelConfigEntity entity = (ModelConfigEntity) record;
|
||||
if (entity.getConfigJson() != null) {
|
||||
entity.setConfigJson(maskSensitiveFields(entity.getConfigJson()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.getPageData(page, target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelConfigEntity getModelById(String id, boolean isCache) {
|
||||
if (StringUtils.isBlank(id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ModelConfigEntity entity = null;
|
||||
|
||||
if (isCache) {
|
||||
String cacheKey = RedisKeys.getModelConfigById(id);
|
||||
entity = (ModelConfigEntity) redisUtils.get(cacheKey);
|
||||
if (entity != null) {
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
// 从数据库获取数据
|
||||
entity = modelConfigDao.selectById(id);
|
||||
if (entity != null) {
|
||||
if (entity.getConfigJson() != null) {
|
||||
entity.setConfigJson(maskSensitiveFields(entity.getConfigJson()));
|
||||
}
|
||||
|
||||
if (isCache) {
|
||||
redisUtils.set(RedisKeys.getModelConfigById(id), entity);
|
||||
}
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证编辑参数
|
||||
*/
|
||||
private void validateEditParameters(String modelType, String provideCode, String id,
|
||||
ModelConfigBodyDTO modelConfigBodyDTO) {
|
||||
if (StringUtils.isBlank(modelType) || StringUtils.isBlank(provideCode)) {
|
||||
throw new RenException(ErrorCode.MODEL_TYPE_PROVIDE_CODE_NOT_NULL);
|
||||
}
|
||||
if (StringUtils.isBlank(id)) {
|
||||
throw new RenException(ErrorCode.IDENTIFIER_NOT_NULL);
|
||||
}
|
||||
if (modelConfigBodyDTO == null) {
|
||||
throw new RenException(ErrorCode.PARAMS_GET_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证添加参数
|
||||
*/
|
||||
private void validateAddParameters(String modelType, String provideCode, ModelConfigBodyDTO modelConfigBodyDTO) {
|
||||
if (StringUtils.isBlank(modelType) || StringUtils.isBlank(provideCode)) {
|
||||
throw new RenException(ErrorCode.MODEL_TYPE_PROVIDE_CODE_NOT_NULL);
|
||||
}
|
||||
if (modelConfigBodyDTO == null) {
|
||||
throw new RenException(ErrorCode.PARAMS_GET_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置默认模型
|
||||
*/
|
||||
@Override
|
||||
public void setDefaultModel(String modelType, int isDefault) {
|
||||
// 参数验证
|
||||
if (StringUtils.isBlank(modelType)) {
|
||||
throw new RenException(ErrorCode.MODEL_TYPE_PROVIDE_CODE_NOT_NULL);
|
||||
}
|
||||
|
||||
ModelConfigEntity entity = new ModelConfigEntity();
|
||||
entity.setIsDefault(isDefault);
|
||||
modelConfigDao.update(entity, new QueryWrapper<ModelConfigEntity>()
|
||||
.eq("model_type", modelType));
|
||||
|
||||
// 清除相关缓存
|
||||
clearModelCacheByType(modelType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证模型提供者
|
||||
*/
|
||||
private void validateModelProvider(String modelType, String provideCode) {
|
||||
List<ModelProviderDTO> providerList = modelProviderService.getList(modelType, provideCode);
|
||||
if (CollectionUtil.isEmpty(providerList)) {
|
||||
throw new RenException(ErrorCode.MODEL_PROVIDER_NOT_EXIST);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从数据库获取原始配置(不经过敏感数据处理)
|
||||
*/
|
||||
private ModelConfigEntity getOriginalConfigFromDb(String id) {
|
||||
ModelConfigEntity originalEntity = modelConfigDao.selectById(id);
|
||||
if (originalEntity == null) {
|
||||
throw new RenException(ErrorCode.RESOURCE_NOT_FOUND);
|
||||
}
|
||||
return originalEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证LLM配置
|
||||
*/
|
||||
private void validateLlmConfiguration(ModelConfigBodyDTO modelConfigBodyDTO) {
|
||||
if (modelConfigBodyDTO.getConfigJson() != null && modelConfigBodyDTO.getConfigJson().containsKey("llm")) {
|
||||
String llm = modelConfigBodyDTO.getConfigJson().get("llm").toString();
|
||||
ModelConfigEntity modelConfigEntity = modelConfigDao.selectOne(new LambdaQueryWrapper<ModelConfigEntity>()
|
||||
.eq(ModelConfigEntity::getId, llm));
|
||||
|
||||
if (modelConfigEntity == null) {
|
||||
throw new RenException(ErrorCode.LLM_NOT_EXIST);
|
||||
}
|
||||
|
||||
String modelType = modelConfigEntity.getModelType();
|
||||
if (modelType == null || !"LLM".equals(modelType.toUpperCase())) {
|
||||
throw new RenException(ErrorCode.LLM_NOT_EXIST);
|
||||
}
|
||||
|
||||
// 验证LLM类型
|
||||
JSONObject configJson = modelConfigEntity.getConfigJson();
|
||||
if (configJson != null && configJson.containsKey("type")) {
|
||||
String type = configJson.get("type").toString();
|
||||
if (!"openai".equals(type) && !"ollama".equals(type)) {
|
||||
throw new RenException(ErrorCode.INVALID_LLM_TYPE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 准备更新实体,处理敏感数据
|
||||
*/
|
||||
private ModelConfigEntity prepareUpdateEntity(ModelConfigBodyDTO modelConfigBodyDTO,
|
||||
ModelConfigEntity originalEntity,
|
||||
String modelType,
|
||||
String id) {
|
||||
// 1. 复制原始实体,保留所有原始数据(包括敏感信息)
|
||||
ModelConfigEntity modelConfigEntity = ConvertUtils.sourceToTarget(originalEntity, ModelConfigEntity.class);
|
||||
modelConfigEntity.setId(id);
|
||||
modelConfigEntity.setModelType(modelType);
|
||||
|
||||
// 2. 只更新非敏感字段
|
||||
modelConfigEntity.setModelName(modelConfigBodyDTO.getModelName());
|
||||
modelConfigEntity.setSort(modelConfigBodyDTO.getSort());
|
||||
modelConfigEntity.setIsEnabled(modelConfigBodyDTO.getIsEnabled());
|
||||
|
||||
// 3. 处理配置JSON,仅更新非敏感字段和明确修改的敏感字段
|
||||
if (modelConfigBodyDTO.getConfigJson() != null && originalEntity.getConfigJson() != null) {
|
||||
JSONObject originalJson = originalEntity.getConfigJson();
|
||||
JSONObject updatedJson = new JSONObject(originalJson); // 基于原始JSON进行修改
|
||||
|
||||
// 遍历更新的JSON,只更新非敏感字段或确实被修改的敏感字段
|
||||
for (String key : modelConfigBodyDTO.getConfigJson().keySet()) {
|
||||
Object value = modelConfigBodyDTO.getConfigJson().get(key);
|
||||
|
||||
// 如果是敏感字段,需要确认是否真的被修改(前端传入的可能是掩码后的值)
|
||||
if (SensitiveDataUtils.isSensitiveField(key)) {
|
||||
// 只有当传入的值不是掩码格式时,才认为是真实修改
|
||||
if (value instanceof String && !isMaskedValue((String) value)) {
|
||||
updatedJson.put(key, value);
|
||||
}
|
||||
} else if (value instanceof JSONObject) {
|
||||
// 递归处理嵌套JSON
|
||||
mergeJson(updatedJson, key, (JSONObject) value);
|
||||
} else {
|
||||
// 非敏感字段直接更新
|
||||
updatedJson.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
modelConfigEntity.setConfigJson(updatedJson);
|
||||
}
|
||||
|
||||
return modelConfigEntity;
|
||||
}
|
||||
|
||||
// 辅助方法:判断值是否是掩码格式
|
||||
private boolean isMaskedValue(String value) {
|
||||
if (value == null)
|
||||
return false;
|
||||
// 简单判断是否包含掩码的特征(***)
|
||||
return value.contains("***");
|
||||
}
|
||||
|
||||
// 辅助方法:递归合并JSON,保留原始敏感字段
|
||||
private void mergeJson(JSONObject original, String key, JSONObject updated) {
|
||||
if (!original.containsKey(key)) {
|
||||
original.put(key, new JSONObject());
|
||||
}
|
||||
JSONObject originalChild = original.getJSONObject(key);
|
||||
|
||||
for (String childKey : updated.keySet()) {
|
||||
Object childValue = updated.get(childKey);
|
||||
if (childValue instanceof JSONObject) {
|
||||
mergeJson(originalChild, childKey, (JSONObject) childValue);
|
||||
} else {
|
||||
if (!SensitiveDataUtils.isSensitiveField(childKey) ||
|
||||
(childValue instanceof String && !isMaskedValue((String) childValue))) {
|
||||
originalChild.put(childKey, childValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 准备新增实体
|
||||
*/
|
||||
private ModelConfigEntity prepareAddEntity(ModelConfigBodyDTO modelConfigBodyDTO, String modelType) {
|
||||
ModelConfigEntity modelConfigEntity = ConvertUtils.sourceToTarget(modelConfigBodyDTO, ModelConfigEntity.class);
|
||||
modelConfigEntity.setModelType(modelType);
|
||||
modelConfigEntity.setIsDefault(0);
|
||||
return modelConfigEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建返回的DTO,处理敏感数据
|
||||
*/
|
||||
private ModelConfigDTO buildResponseDTO(ModelConfigEntity entity) {
|
||||
ModelConfigDTO dto = ConvertUtils.sourceToTarget(entity, ModelConfigDTO.class);
|
||||
if (dto.getConfigJson() != null) {
|
||||
dto.setConfigJson(maskSensitiveFields(dto.getConfigJson()));
|
||||
}
|
||||
return dto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理敏感字段
|
||||
*/
|
||||
private JSONObject maskSensitiveFields(JSONObject configJson) {
|
||||
return SensitiveDataUtils.maskSensitiveFields(configJson);
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除模型缓存
|
||||
*/
|
||||
private void clearModelCache(String id) {
|
||||
redisUtils.delete(RedisKeys.getModelConfigById(id));
|
||||
redisUtils.delete(RedisKeys.getModelNameById(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* 按模型类型清除缓存
|
||||
*/
|
||||
private void clearModelCacheByType(String modelType) {
|
||||
List<ModelConfigEntity> entities = modelConfigDao.selectList(
|
||||
new QueryWrapper<ModelConfigEntity>().eq("model_type", modelType));
|
||||
for (ModelConfigEntity entity : entities) {
|
||||
clearModelCache(entity.getId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查智能体配置是否有引用
|
||||
*
|
||||
* @param modelId 模型ID
|
||||
*/
|
||||
private void checkAgentReference(String modelId) {
|
||||
List<AgentEntity> agents = agentDao.selectList(
|
||||
@@ -242,8 +488,6 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
|
||||
|
||||
/**
|
||||
* 检查意图识别配置是否有引用
|
||||
*
|
||||
* @param modelId 模型ID
|
||||
*/
|
||||
private void checkIntentConfigReference(String modelId) {
|
||||
ModelConfigEntity modelConfig = modelConfigDao.selectById(modelId);
|
||||
@@ -258,100 +502,4 @@ public class ModelConfigServiceImpl extends BaseServiceImpl<ModelConfigDao, Mode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModelNameById(String id) {
|
||||
if (StringUtils.isBlank(id)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String cachedName = (String) redisUtils.get(RedisKeys.getModelNameById(id));
|
||||
|
||||
if (StringUtils.isNotBlank(cachedName)) {
|
||||
return cachedName;
|
||||
}
|
||||
|
||||
ModelConfigEntity entity = modelConfigDao.selectById(id);
|
||||
if (entity != null) {
|
||||
String modelName = entity.getModelName();
|
||||
if (StringUtils.isNotBlank(modelName)) {
|
||||
redisUtils.set(RedisKeys.getModelNameById(id), modelName);
|
||||
}
|
||||
return modelName;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDefaultModel(String modelType, int isDefault) {
|
||||
ModelConfigEntity entity = new ModelConfigEntity();
|
||||
entity.setIsDefault(isDefault);
|
||||
modelConfigDao.update(entity, new QueryWrapper<ModelConfigEntity>()
|
||||
.eq("model_type", modelType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelConfigEntity selectById(Serializable id) {
|
||||
ModelConfigEntity entity = super.selectById(id);
|
||||
if (entity != null && entity.getConfigJson() != null) {
|
||||
// 对配置中的敏感数据进行隐藏处理
|
||||
JSONObject maskedConfigJson = SensitiveDataUtils.maskSensitiveFields(entity.getConfigJson());
|
||||
entity.setConfigJson(maskedConfigJson);
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
// 重写getPageData方法,添加敏感数据处理
|
||||
@Override
|
||||
protected <D> PageData<D> getPageData(IPage<?> page, Class<D> target) {
|
||||
List<?> records = page.getRecords();
|
||||
if (records != null && !records.isEmpty()) {
|
||||
for (Object record : records) {
|
||||
if (record instanceof ModelConfigEntity) {
|
||||
ModelConfigEntity entity = (ModelConfigEntity) record;
|
||||
if (entity.getConfigJson() != null) {
|
||||
// 对配置中的敏感数据进行隐藏处理
|
||||
JSONObject maskedConfigJson = SensitiveDataUtils.maskSensitiveFields(entity.getConfigJson());
|
||||
entity.setConfigJson(maskedConfigJson);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.getPageData(page, target);
|
||||
}
|
||||
|
||||
// 确保只有一个getModelById方法实现
|
||||
@Override
|
||||
public ModelConfigEntity getModelById(String id, boolean isCache) {
|
||||
ModelConfigEntity entity = null;
|
||||
if (isCache) {
|
||||
String cacheKey = RedisKeys.getModelConfigById(id);
|
||||
entity = (ModelConfigEntity) redisUtils.get(cacheKey);
|
||||
if (entity != null) {
|
||||
// 从缓存获取的数据也需要处理敏感信息
|
||||
if (entity.getConfigJson() != null) {
|
||||
JSONObject maskedConfigJson = SensitiveDataUtils.maskSensitiveFields(entity.getConfigJson());
|
||||
entity.setConfigJson(maskedConfigJson);
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
// 从数据库获取数据
|
||||
entity = modelConfigDao.selectById(id);
|
||||
if (entity != null) {
|
||||
// 处理敏感信息
|
||||
if (entity.getConfigJson() != null) {
|
||||
JSONObject maskedConfigJson = SensitiveDataUtils.maskSensitiveFields(entity.getConfigJson());
|
||||
entity.setConfigJson(maskedConfigJson);
|
||||
}
|
||||
|
||||
if (isCache) {
|
||||
// 缓存处理后的对象
|
||||
redisUtils.set(RedisKeys.getModelConfigById(id), entity);
|
||||
}
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user