package com.vincent.rsf.server.ai.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.vincent.rsf.framework.exception.CoolException; import com.vincent.rsf.server.ai.config.AiDefaults; import com.vincent.rsf.server.ai.dto.AiParamValidateResultDto; import com.vincent.rsf.server.ai.entity.AiParam; import com.vincent.rsf.server.ai.mapper.AiParamMapper; import com.vincent.rsf.server.ai.service.AiParamService; import com.vincent.rsf.server.system.enums.StatusType; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import java.text.SimpleDateFormat; import java.util.Date; @Service("aiParamService") @RequiredArgsConstructor public class AiParamServiceImpl extends ServiceImpl implements AiParamService { private final AiParamValidationSupport aiParamValidationSupport; @Override public AiParam getActiveParam(Long tenantId) { ensureTenantId(tenantId); AiParam aiParam = this.getOne(new LambdaQueryWrapper() .eq(AiParam::getTenantId, tenantId) .eq(AiParam::getStatus, StatusType.ENABLE.val) .eq(AiParam::getDeleted, 0) .last("limit 1")); if (aiParam == null) { throw new CoolException("未找到启用中的 AI 参数配置"); } return aiParam; } @Override public void validateBeforeSave(AiParam aiParam, Long tenantId) { ensureTenantId(tenantId); aiParam.setTenantId(tenantId); fillDefaults(aiParam); ensureBaseFields(aiParam); ensureSingleActive(tenantId, null, aiParam.getStatus()); applyValidation(aiParam); } @Override public void validateBeforeUpdate(AiParam aiParam, Long tenantId) { ensureTenantId(tenantId); fillDefaults(aiParam); if (aiParam.getId() == null) { throw new CoolException("AI 参数 ID 不能为空"); } AiParam current = requireOwnedRecord(aiParam.getId(), tenantId); aiParam.setTenantId(current.getTenantId()); ensureBaseFields(aiParam); ensureSingleActive(tenantId, aiParam.getId(), aiParam.getStatus()); applyValidation(aiParam); } @Override public AiParamValidateResultDto validateDraft(AiParam aiParam, Long tenantId) { ensureTenantId(tenantId); fillDefaults(aiParam); ensureBaseFields(aiParam); return aiParamValidationSupport.validate(aiParam); } private void ensureBaseFields(AiParam aiParam) { if (!StringUtils.hasText(aiParam.getName())) { throw new CoolException("AI 参数名称不能为空"); } if (!StringUtils.hasText(aiParam.getProviderType())) { aiParam.setProviderType(AiDefaults.PROVIDER_OPENAI_COMPATIBLE); } if (!StringUtils.hasText(aiParam.getBaseUrl())) { throw new CoolException("AI Base URL 不能为空"); } if (!StringUtils.hasText(aiParam.getApiKey())) { throw new CoolException("AI API Key 不能为空"); } if (!StringUtils.hasText(aiParam.getModel())) { throw new CoolException("AI 模型不能为空"); } } private void ensureSingleActive(Long tenantId, Long selfId, Integer status) { if (status == null || status != StatusType.ENABLE.val) { return; } LambdaQueryWrapper wrapper = new LambdaQueryWrapper() .eq(AiParam::getTenantId, tenantId) .eq(AiParam::getStatus, StatusType.ENABLE.val) .eq(AiParam::getDeleted, 0); if (selfId != null) { wrapper.ne(AiParam::getId, selfId); } if (this.count(wrapper) > 0) { throw new CoolException("同一租户仅允许一条启用中的 AI 参数配置"); } } private AiParam requireOwnedRecord(Long id, Long tenantId) { AiParam aiParam = this.getOne(new LambdaQueryWrapper() .eq(AiParam::getId, id) .eq(AiParam::getTenantId, tenantId) .eq(AiParam::getDeleted, 0) .last("limit 1")); if (aiParam == null) { throw new CoolException("AI 参数不存在或无权访问"); } return aiParam; } private void ensureTenantId(Long tenantId) { if (tenantId == null) { throw new CoolException("当前租户不存在"); } } private void fillDefaults(AiParam aiParam) { if (!StringUtils.hasText(aiParam.getProviderType())) { aiParam.setProviderType(AiDefaults.PROVIDER_OPENAI_COMPATIBLE); } if (aiParam.getTemperature() == null) { aiParam.setTemperature(AiDefaults.DEFAULT_TEMPERATURE); } if (aiParam.getTopP() == null) { aiParam.setTopP(AiDefaults.DEFAULT_TOP_P); } if (aiParam.getTimeoutMs() == null) { aiParam.setTimeoutMs(AiDefaults.DEFAULT_TIMEOUT_MS); } if (aiParam.getStreamingEnabled() == null) { aiParam.setStreamingEnabled(Boolean.TRUE); } if (!StringUtils.hasText(aiParam.getValidateStatus())) { aiParam.setValidateStatus(AiDefaults.PARAM_VALIDATE_NOT_TESTED); } if (aiParam.getStatus() == null) { aiParam.setStatus(StatusType.ENABLE.val); } } private void applyValidation(AiParam aiParam) { AiParamValidateResultDto validateResult = aiParamValidationSupport.validate(aiParam); aiParam.setValidateStatus(validateResult.getStatus()); aiParam.setLastValidateMessage(validateResult.getMessage()); aiParam.setLastValidateElapsedMs(validateResult.getElapsedMs()); aiParam.setLastValidateTime(parseDate(validateResult.getValidatedAt())); if (!AiDefaults.PARAM_VALIDATE_VALID.equals(validateResult.getStatus())) { throw new CoolException(validateResult.getMessage()); } } private Date parseDate(String dateTime) { if (!StringUtils.hasText(dateTime)) { return null; } try { return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(dateTime); } catch (Exception e) { throw new CoolException("解析校验时间失败: " + e.getMessage()); } } }