| | |
| | | package com.vincent.rsf.server.ai.controller; |
| | | |
| | | import com.vincent.rsf.framework.common.R; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.vincent.rsf.server.ai.dto.AiMcpToolTestRequest; |
| | | import com.vincent.rsf.server.ai.entity.AiMcpMount; |
| | | import com.vincent.rsf.server.ai.service.AiMcpMountService; |
| | |
| | | public R page(@RequestBody Map<String, Object> map) { |
| | | BaseParam baseParam = buildParam(map, BaseParam.class); |
| | | PageParam<AiMcpMount, BaseParam> pageParam = new PageParam<>(baseParam, AiMcpMount.class); |
| | | return R.ok().add(aiMcpMountService.page(pageParam, pageParam.buildWrapper(true))); |
| | | return R.ok().add(aiMcpMountService.page(pageParam, pageParam.buildWrapper(true) |
| | | .eq("tenant_id", getTenantId()) |
| | | .eq("deleted", 0))); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('system:aiMcpMount:list')") |
| | | @PostMapping("/aiMcpMount/list") |
| | | public R list(@RequestBody Map<String, Object> map) { |
| | | return R.ok().add(aiMcpMountService.list()); |
| | | return R.ok().add(aiMcpMountService.list(new LambdaQueryWrapper<AiMcpMount>() |
| | | .eq(AiMcpMount::getTenantId, getTenantId()) |
| | | .eq(AiMcpMount::getDeleted, 0) |
| | | .orderByAsc(AiMcpMount::getSort) |
| | | .orderByDesc(AiMcpMount::getId))); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('system:aiMcpMount:list')") |
| | | @PostMapping({"/aiMcpMount/many/{ids}", "/aiMcpMounts/many/{ids}"}) |
| | | public R many(@PathVariable Long[] ids) { |
| | | return R.ok().add(aiMcpMountService.listByIds(Arrays.asList(ids))); |
| | | return R.ok().add(aiMcpMountService.list(new LambdaQueryWrapper<AiMcpMount>() |
| | | .eq(AiMcpMount::getTenantId, getTenantId()) |
| | | .eq(AiMcpMount::getDeleted, 0) |
| | | .in(AiMcpMount::getId, Arrays.asList(ids)))); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('system:aiMcpMount:list')") |
| | | @GetMapping("/aiMcpMount/{id}") |
| | | public R get(@PathVariable("id") Long id) { |
| | | return R.ok().add(aiMcpMountService.getById(id)); |
| | | return R.ok().add(aiMcpMountService.getOne(new LambdaQueryWrapper<AiMcpMount>() |
| | | .eq(AiMcpMount::getId, id) |
| | | .eq(AiMcpMount::getTenantId, getTenantId()) |
| | | .eq(AiMcpMount::getDeleted, 0) |
| | | .last("limit 1"))); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('system:aiMcpMount:list')") |
| | |
| | | @OperationLog("Create AiMcpMount") |
| | | @PostMapping("/aiMcpMount/save") |
| | | public R save(@RequestBody AiMcpMount aiMcpMount) { |
| | | aiMcpMountService.validateBeforeSave(aiMcpMount); |
| | | aiMcpMount.setTenantId(getTenantId()); |
| | | aiMcpMountService.validateBeforeSave(aiMcpMount, getTenantId()); |
| | | aiMcpMount.setCreateBy(getLoginUserId()); |
| | | aiMcpMount.setCreateTime(new Date()); |
| | | aiMcpMount.setUpdateBy(getLoginUserId()); |
| | |
| | | @OperationLog("Update AiMcpMount") |
| | | @PostMapping("/aiMcpMount/update") |
| | | public R update(@RequestBody AiMcpMount aiMcpMount) { |
| | | aiMcpMountService.validateBeforeUpdate(aiMcpMount); |
| | | aiMcpMount.setTenantId(getTenantId()); |
| | | aiMcpMountService.validateBeforeUpdate(aiMcpMount, getTenantId()); |
| | | aiMcpMount.setUpdateBy(getLoginUserId()); |
| | | aiMcpMount.setUpdateTime(new Date()); |
| | | if (!aiMcpMountService.updateById(aiMcpMount)) { |
| | |
| | | @PreAuthorize("hasAuthority('system:aiMcpMount:list')") |
| | | @PostMapping("/aiMcpMount/export") |
| | | public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception { |
| | | ExcelUtil.build(ExcelUtil.create(aiMcpMountService.list(), AiMcpMount.class), response); |
| | | ExcelUtil.build(ExcelUtil.create(aiMcpMountService.list(new LambdaQueryWrapper<AiMcpMount>() |
| | | .eq(AiMcpMount::getTenantId, getTenantId()) |
| | | .eq(AiMcpMount::getDeleted, 0) |
| | | .orderByAsc(AiMcpMount::getSort) |
| | | .orderByDesc(AiMcpMount::getId)), AiMcpMount.class), response); |
| | | } |
| | | } |
| | |
| | | package com.vincent.rsf.server.ai.controller; |
| | | |
| | | import com.vincent.rsf.framework.common.R; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.vincent.rsf.server.ai.entity.AiParam; |
| | | import com.vincent.rsf.server.ai.service.AiParamService; |
| | | import com.vincent.rsf.server.common.annotation.OperationLog; |
| | |
| | | public R page(@RequestBody Map<String, Object> map) { |
| | | BaseParam baseParam = buildParam(map, BaseParam.class); |
| | | PageParam<AiParam, BaseParam> pageParam = new PageParam<>(baseParam, AiParam.class); |
| | | return R.ok().add(aiParamService.page(pageParam, pageParam.buildWrapper(true))); |
| | | return R.ok().add(aiParamService.page(pageParam, pageParam.buildWrapper(true) |
| | | .eq("tenant_id", getTenantId()) |
| | | .eq("deleted", 0))); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('system:aiParam:list')") |
| | | @PostMapping("/aiParam/list") |
| | | public R list(@RequestBody Map<String, Object> map) { |
| | | return R.ok().add(aiParamService.list()); |
| | | return R.ok().add(aiParamService.list(new LambdaQueryWrapper<AiParam>() |
| | | .eq(AiParam::getTenantId, getTenantId()) |
| | | .eq(AiParam::getDeleted, 0) |
| | | .orderByDesc(AiParam::getCreateTime) |
| | | .orderByDesc(AiParam::getId))); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('system:aiParam:list')") |
| | | @PostMapping({"/aiParam/many/{ids}", "/aiParams/many/{ids}"}) |
| | | public R many(@PathVariable Long[] ids) { |
| | | return R.ok().add(aiParamService.listByIds(Arrays.asList(ids))); |
| | | return R.ok().add(aiParamService.list(new LambdaQueryWrapper<AiParam>() |
| | | .eq(AiParam::getTenantId, getTenantId()) |
| | | .eq(AiParam::getDeleted, 0) |
| | | .in(AiParam::getId, Arrays.asList(ids)))); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('system:aiParam:list')") |
| | | @GetMapping("/aiParam/{id}") |
| | | public R get(@PathVariable("id") Long id) { |
| | | return R.ok().add(aiParamService.getById(id)); |
| | | return R.ok().add(aiParamService.getOne(new LambdaQueryWrapper<AiParam>() |
| | | .eq(AiParam::getId, id) |
| | | .eq(AiParam::getTenantId, getTenantId()) |
| | | .eq(AiParam::getDeleted, 0) |
| | | .last("limit 1"))); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('system:aiParam:save')") |
| | | @OperationLog("Create AiParam") |
| | | @PostMapping("/aiParam/save") |
| | | public R save(@RequestBody AiParam aiParam) { |
| | | aiParamService.validateBeforeSave(aiParam); |
| | | aiParam.setTenantId(getTenantId()); |
| | | aiParamService.validateBeforeSave(aiParam, getTenantId()); |
| | | aiParam.setCreateBy(getLoginUserId()); |
| | | aiParam.setCreateTime(new Date()); |
| | | aiParam.setUpdateBy(getLoginUserId()); |
| | |
| | | @OperationLog("Update AiParam") |
| | | @PostMapping("/aiParam/update") |
| | | public R update(@RequestBody AiParam aiParam) { |
| | | aiParamService.validateBeforeUpdate(aiParam); |
| | | aiParam.setTenantId(getTenantId()); |
| | | aiParamService.validateBeforeUpdate(aiParam, getTenantId()); |
| | | aiParam.setUpdateBy(getLoginUserId()); |
| | | aiParam.setUpdateTime(new Date()); |
| | | if (!aiParamService.updateById(aiParam)) { |
| | |
| | | @PreAuthorize("hasAuthority('system:aiParam:list')") |
| | | @PostMapping("/aiParam/export") |
| | | public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception { |
| | | ExcelUtil.build(ExcelUtil.create(aiParamService.list(), AiParam.class), response); |
| | | ExcelUtil.build(ExcelUtil.create(aiParamService.list(new LambdaQueryWrapper<AiParam>() |
| | | .eq(AiParam::getTenantId, getTenantId()) |
| | | .eq(AiParam::getDeleted, 0) |
| | | .orderByDesc(AiParam::getCreateTime) |
| | | .orderByDesc(AiParam::getId)), AiParam.class), response); |
| | | } |
| | | } |
| | |
| | | package com.vincent.rsf.server.ai.controller; |
| | | |
| | | import com.vincent.rsf.framework.common.R; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.vincent.rsf.server.ai.entity.AiPrompt; |
| | | import com.vincent.rsf.server.ai.service.AiPromptService; |
| | | import com.vincent.rsf.server.common.annotation.OperationLog; |
| | |
| | | public R page(@RequestBody Map<String, Object> map) { |
| | | BaseParam baseParam = buildParam(map, BaseParam.class); |
| | | PageParam<AiPrompt, BaseParam> pageParam = new PageParam<>(baseParam, AiPrompt.class); |
| | | return R.ok().add(aiPromptService.page(pageParam, pageParam.buildWrapper(true))); |
| | | return R.ok().add(aiPromptService.page(pageParam, pageParam.buildWrapper(true) |
| | | .eq("tenant_id", getTenantId()) |
| | | .eq("deleted", 0))); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('system:aiPrompt:list')") |
| | | @PostMapping("/aiPrompt/list") |
| | | public R list(@RequestBody Map<String, Object> map) { |
| | | return R.ok().add(aiPromptService.list()); |
| | | return R.ok().add(aiPromptService.list(new LambdaQueryWrapper<AiPrompt>() |
| | | .eq(AiPrompt::getTenantId, getTenantId()) |
| | | .eq(AiPrompt::getDeleted, 0) |
| | | .orderByDesc(AiPrompt::getCreateTime) |
| | | .orderByDesc(AiPrompt::getId))); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('system:aiPrompt:list')") |
| | | @PostMapping({"/aiPrompt/many/{ids}", "/aiPrompts/many/{ids}"}) |
| | | public R many(@PathVariable Long[] ids) { |
| | | return R.ok().add(aiPromptService.listByIds(Arrays.asList(ids))); |
| | | return R.ok().add(aiPromptService.list(new LambdaQueryWrapper<AiPrompt>() |
| | | .eq(AiPrompt::getTenantId, getTenantId()) |
| | | .eq(AiPrompt::getDeleted, 0) |
| | | .in(AiPrompt::getId, Arrays.asList(ids)))); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('system:aiPrompt:list')") |
| | | @GetMapping("/aiPrompt/{id}") |
| | | public R get(@PathVariable("id") Long id) { |
| | | return R.ok().add(aiPromptService.getById(id)); |
| | | return R.ok().add(aiPromptService.getOne(new LambdaQueryWrapper<AiPrompt>() |
| | | .eq(AiPrompt::getId, id) |
| | | .eq(AiPrompt::getTenantId, getTenantId()) |
| | | .eq(AiPrompt::getDeleted, 0) |
| | | .last("limit 1"))); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('system:aiPrompt:save')") |
| | | @OperationLog("Create AiPrompt") |
| | | @PostMapping("/aiPrompt/save") |
| | | public R save(@RequestBody AiPrompt aiPrompt) { |
| | | aiPromptService.validateBeforeSave(aiPrompt); |
| | | aiPrompt.setTenantId(getTenantId()); |
| | | aiPromptService.validateBeforeSave(aiPrompt, getTenantId()); |
| | | aiPrompt.setCreateBy(getLoginUserId()); |
| | | aiPrompt.setCreateTime(new Date()); |
| | | aiPrompt.setUpdateBy(getLoginUserId()); |
| | |
| | | @OperationLog("Update AiPrompt") |
| | | @PostMapping("/aiPrompt/update") |
| | | public R update(@RequestBody AiPrompt aiPrompt) { |
| | | aiPromptService.validateBeforeUpdate(aiPrompt); |
| | | aiPrompt.setTenantId(getTenantId()); |
| | | aiPromptService.validateBeforeUpdate(aiPrompt, getTenantId()); |
| | | aiPrompt.setUpdateBy(getLoginUserId()); |
| | | aiPrompt.setUpdateTime(new Date()); |
| | | if (!aiPromptService.updateById(aiPrompt)) { |
| | |
| | | @PreAuthorize("hasAuthority('system:aiPrompt:list')") |
| | | @PostMapping("/aiPrompt/export") |
| | | public void export(@RequestBody Map<String, Object> map, HttpServletResponse response) throws Exception { |
| | | ExcelUtil.build(ExcelUtil.create(aiPromptService.list(), AiPrompt.class), response); |
| | | ExcelUtil.build(ExcelUtil.create(aiPromptService.list(new LambdaQueryWrapper<AiPrompt>() |
| | | .eq(AiPrompt::getTenantId, getTenantId()) |
| | | .eq(AiPrompt::getDeleted, 0) |
| | | .orderByDesc(AiPrompt::getCreateTime) |
| | | .orderByDesc(AiPrompt::getId)), AiPrompt.class), response); |
| | | } |
| | | } |
| | |
| | | |
| | | public interface AiConfigResolverService { |
| | | |
| | | AiResolvedConfig resolve(String promptCode); |
| | | AiResolvedConfig resolve(String promptCode, Long tenantId); |
| | | } |
| | |
| | | |
| | | public interface AiMcpMountService extends IService<AiMcpMount> { |
| | | |
| | | List<AiMcpMount> listActiveMounts(); |
| | | List<AiMcpMount> listActiveMounts(Long tenantId); |
| | | |
| | | void validateBeforeSave(AiMcpMount aiMcpMount); |
| | | void validateBeforeSave(AiMcpMount aiMcpMount, Long tenantId); |
| | | |
| | | void validateBeforeUpdate(AiMcpMount aiMcpMount); |
| | | void validateBeforeUpdate(AiMcpMount aiMcpMount, Long tenantId); |
| | | |
| | | List<AiMcpToolPreviewDto> previewTools(Long mountId, Long userId, Long tenantId); |
| | | |
| | |
| | | |
| | | public interface AiParamService extends IService<AiParam> { |
| | | |
| | | AiParam getActiveParam(); |
| | | AiParam getActiveParam(Long tenantId); |
| | | |
| | | void validateBeforeSave(AiParam aiParam); |
| | | void validateBeforeSave(AiParam aiParam, Long tenantId); |
| | | |
| | | void validateBeforeUpdate(AiParam aiParam); |
| | | void validateBeforeUpdate(AiParam aiParam, Long tenantId); |
| | | } |
| | |
| | | |
| | | public interface AiPromptService extends IService<AiPrompt> { |
| | | |
| | | AiPrompt getActivePrompt(String code); |
| | | AiPrompt getActivePrompt(String code, Long tenantId); |
| | | |
| | | void validateBeforeSave(AiPrompt aiPrompt); |
| | | void validateBeforeSave(AiPrompt aiPrompt, Long tenantId); |
| | | |
| | | void validateBeforeUpdate(AiPrompt aiPrompt); |
| | | void validateBeforeUpdate(AiPrompt aiPrompt, Long tenantId); |
| | | } |
| | |
| | | |
| | | @Override |
| | | public AiChatRuntimeDto getRuntime(String promptCode, Long sessionId, Long userId, Long tenantId) { |
| | | AiResolvedConfig config = aiConfigResolverService.resolve(promptCode); |
| | | AiResolvedConfig config = aiConfigResolverService.resolve(promptCode, tenantId); |
| | | AiChatMemoryDto memory = aiChatMemoryService.getMemory(userId, tenantId, config.getPromptCode(), sessionId); |
| | | return AiChatRuntimeDto.builder() |
| | | .sessionId(memory.getSessionId()) |
| | |
| | | |
| | | @Override |
| | | public List<AiChatSessionDto> listSessions(String promptCode, Long userId, Long tenantId) { |
| | | AiResolvedConfig config = aiConfigResolverService.resolve(promptCode); |
| | | AiResolvedConfig config = aiConfigResolverService.resolve(promptCode, tenantId); |
| | | return aiChatMemoryService.listSessions(userId, tenantId, config.getPromptCode()); |
| | | } |
| | | |
| | |
| | | |
| | | private void doStream(AiChatRequest request, Long userId, Long tenantId, SseEmitter emitter) { |
| | | try { |
| | | AiResolvedConfig config = aiConfigResolverService.resolve(request.getPromptCode()); |
| | | AiResolvedConfig config = aiConfigResolverService.resolve(request.getPromptCode(), tenantId); |
| | | AiChatSession session = aiChatMemoryService.resolveSession(userId, tenantId, config.getPromptCode(), request.getSessionId(), resolveTitleSeed(request.getMessages())); |
| | | AiChatMemoryDto memory = aiChatMemoryService.getMemory(userId, tenantId, config.getPromptCode(), session.getId()); |
| | | List<AiChatMessageDto> mergedMessages = mergeMessages(memory.getPersistedMessages(), request.getMessages()); |
| | |
| | | import com.vincent.rsf.server.ai.service.AiMcpMountService; |
| | | import com.vincent.rsf.server.ai.service.AiParamService; |
| | | import com.vincent.rsf.server.ai.service.AiPromptService; |
| | | import com.vincent.rsf.framework.exception.CoolException; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.util.StringUtils; |
| | |
| | | private final AiMcpMountService aiMcpMountService; |
| | | |
| | | @Override |
| | | public AiResolvedConfig resolve(String promptCode) { |
| | | public AiResolvedConfig resolve(String promptCode, Long tenantId) { |
| | | if (tenantId == null) { |
| | | throw new CoolException("当前租户不存在"); |
| | | } |
| | | String finalPromptCode = StringUtils.hasText(promptCode) ? promptCode : AiDefaults.DEFAULT_PROMPT_CODE; |
| | | return AiResolvedConfig.builder() |
| | | .promptCode(finalPromptCode) |
| | | .aiParam(aiParamService.getActiveParam()) |
| | | .prompt(aiPromptService.getActivePrompt(finalPromptCode)) |
| | | .mcpMounts(aiMcpMountService.listActiveMounts()) |
| | | .aiParam(aiParamService.getActiveParam(tenantId)) |
| | | .prompt(aiPromptService.getActivePrompt(finalPromptCode, tenantId)) |
| | | .mcpMounts(aiMcpMountService.listActiveMounts(tenantId)) |
| | | .build(); |
| | | } |
| | | } |
| | |
| | | private final ObjectMapper objectMapper; |
| | | |
| | | @Override |
| | | public List<AiMcpMount> listActiveMounts() { |
| | | public List<AiMcpMount> listActiveMounts(Long tenantId) { |
| | | ensureTenantId(tenantId); |
| | | return this.list(new LambdaQueryWrapper<AiMcpMount>() |
| | | .eq(AiMcpMount::getTenantId, tenantId) |
| | | .eq(AiMcpMount::getStatus, StatusType.ENABLE.val) |
| | | .eq(AiMcpMount::getDeleted, 0) |
| | | .orderByAsc(AiMcpMount::getSort) |
| | | .orderByAsc(AiMcpMount::getId)); |
| | | } |
| | | |
| | | @Override |
| | | public void validateBeforeSave(AiMcpMount aiMcpMount) { |
| | | public void validateBeforeSave(AiMcpMount aiMcpMount, Long tenantId) { |
| | | ensureTenantId(tenantId); |
| | | aiMcpMount.setTenantId(tenantId); |
| | | fillDefaults(aiMcpMount); |
| | | ensureRequiredFields(aiMcpMount); |
| | | ensureRequiredFields(aiMcpMount, tenantId); |
| | | } |
| | | |
| | | @Override |
| | | public void validateBeforeUpdate(AiMcpMount aiMcpMount) { |
| | | public void validateBeforeUpdate(AiMcpMount aiMcpMount, Long tenantId) { |
| | | ensureTenantId(tenantId); |
| | | fillDefaults(aiMcpMount); |
| | | if (aiMcpMount.getId() == null) { |
| | | throw new CoolException("MCP 挂载 ID 不能为空"); |
| | | } |
| | | ensureRequiredFields(aiMcpMount); |
| | | AiMcpMount current = requireMount(aiMcpMount.getId(), tenantId); |
| | | aiMcpMount.setTenantId(current.getTenantId()); |
| | | ensureRequiredFields(aiMcpMount, tenantId); |
| | | } |
| | | |
| | | @Override |
| | | public List<AiMcpToolPreviewDto> previewTools(Long mountId, Long userId, Long tenantId) { |
| | | AiMcpMount mount = requireMount(mountId); |
| | | AiMcpMount mount = requireMount(mountId, tenantId); |
| | | try (McpMountRuntimeFactory.McpMountRuntime runtime = mcpMountRuntimeFactory.create(List.of(mount), userId)) { |
| | | List<AiMcpToolPreviewDto> tools = new ArrayList<>(); |
| | | for (ToolCallback callback : runtime.getToolCallbacks()) { |
| | |
| | | } catch (Exception e) { |
| | | throw new CoolException("工具输入 JSON 格式错误: " + e.getMessage()); |
| | | } |
| | | AiMcpMount mount = requireMount(mountId); |
| | | AiMcpMount mount = requireMount(mountId, tenantId); |
| | | try (McpMountRuntimeFactory.McpMountRuntime runtime = mcpMountRuntimeFactory.create(List.of(mount), userId)) { |
| | | ToolCallback callback = Arrays.stream(runtime.getToolCallbacks()) |
| | | .filter(item -> item != null && item.getToolDefinition() != null) |
| | |
| | | } |
| | | } |
| | | |
| | | private void ensureRequiredFields(AiMcpMount aiMcpMount) { |
| | | private void ensureRequiredFields(AiMcpMount aiMcpMount, Long tenantId) { |
| | | if (!StringUtils.hasText(aiMcpMount.getName())) { |
| | | throw new CoolException("MCP 挂载名称不能为空"); |
| | | } |
| | | if (AiDefaults.MCP_TRANSPORT_BUILTIN.equals(aiMcpMount.getTransportType())) { |
| | | builtinMcpToolRegistry.validateBuiltinCode(aiMcpMount.getBuiltinCode()); |
| | | ensureBuiltinConflictFree(aiMcpMount); |
| | | ensureBuiltinConflictFree(aiMcpMount, tenantId); |
| | | return; |
| | | } |
| | | if (AiDefaults.MCP_TRANSPORT_SSE_HTTP.equals(aiMcpMount.getTransportType())) { |
| | |
| | | throw new CoolException("不支持的 MCP 传输类型: " + aiMcpMount.getTransportType()); |
| | | } |
| | | |
| | | private AiMcpMount requireMount(Long mountId) { |
| | | private AiMcpMount requireMount(Long mountId, Long tenantId) { |
| | | ensureTenantId(tenantId); |
| | | if (mountId == null) { |
| | | throw new CoolException("MCP 挂载 ID 不能为空"); |
| | | } |
| | | AiMcpMount mount = this.getById(mountId); |
| | | if (mount == null || (mount.getDeleted() != null && mount.getDeleted() == 1)) { |
| | | AiMcpMount mount = this.getOne(new LambdaQueryWrapper<AiMcpMount>() |
| | | .eq(AiMcpMount::getId, mountId) |
| | | .eq(AiMcpMount::getTenantId, tenantId) |
| | | .eq(AiMcpMount::getDeleted, 0) |
| | | .last("limit 1")); |
| | | if (mount == null) { |
| | | throw new CoolException("MCP 挂载不存在"); |
| | | } |
| | | return mount; |
| | | } |
| | | |
| | | private void ensureBuiltinConflictFree(AiMcpMount aiMcpMount) { |
| | | private void ensureBuiltinConflictFree(AiMcpMount aiMcpMount, Long tenantId) { |
| | | if (aiMcpMount.getStatus() == null || aiMcpMount.getStatus() != StatusType.ENABLE.val) { |
| | | return; |
| | | } |
| | |
| | | return; |
| | | } |
| | | LambdaQueryWrapper<AiMcpMount> queryWrapper = new LambdaQueryWrapper<AiMcpMount>() |
| | | .eq(AiMcpMount::getTenantId, tenantId) |
| | | .eq(AiMcpMount::getTransportType, AiDefaults.MCP_TRANSPORT_BUILTIN) |
| | | .eq(AiMcpMount::getStatus, StatusType.ENABLE.val) |
| | | .eq(AiMcpMount::getDeleted, 0) |
| | | .in(AiMcpMount::getBuiltinCode, conflictCodes); |
| | | if (aiMcpMount.getId() != null) { |
| | | queryWrapper.ne(AiMcpMount::getId, aiMcpMount.getId()); |
| | |
| | | } |
| | | return codes; |
| | | } |
| | | |
| | | private void ensureTenantId(Long tenantId) { |
| | | if (tenantId == null) { |
| | | throw new CoolException("当前租户不存在"); |
| | | } |
| | | } |
| | | } |
| | |
| | | public class AiParamServiceImpl extends ServiceImpl<AiParamMapper, AiParam> implements AiParamService { |
| | | |
| | | @Override |
| | | public AiParam getActiveParam() { |
| | | public AiParam getActiveParam(Long tenantId) { |
| | | ensureTenantId(tenantId); |
| | | AiParam aiParam = this.getOne(new LambdaQueryWrapper<AiParam>() |
| | | .eq(AiParam::getTenantId, tenantId) |
| | | .eq(AiParam::getStatus, StatusType.ENABLE.val) |
| | | .eq(AiParam::getDeleted, 0) |
| | | .last("limit 1")); |
| | | if (aiParam == null) { |
| | | throw new CoolException("未找到启用中的 AI 参数配置"); |
| | |
| | | } |
| | | |
| | | @Override |
| | | public void validateBeforeSave(AiParam aiParam) { |
| | | public void validateBeforeSave(AiParam aiParam, Long tenantId) { |
| | | ensureTenantId(tenantId); |
| | | aiParam.setTenantId(tenantId); |
| | | fillDefaults(aiParam); |
| | | ensureBaseFields(aiParam); |
| | | ensureSingleActive(aiParam, null); |
| | | ensureSingleActive(tenantId, null, aiParam.getStatus()); |
| | | } |
| | | |
| | | @Override |
| | | public void validateBeforeUpdate(AiParam aiParam) { |
| | | 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(aiParam, aiParam.getId()); |
| | | ensureSingleActive(tenantId, aiParam.getId(), aiParam.getStatus()); |
| | | } |
| | | |
| | | private void ensureBaseFields(AiParam aiParam) { |
| | |
| | | } |
| | | } |
| | | |
| | | private void ensureSingleActive(AiParam aiParam, Long selfId) { |
| | | if (aiParam.getStatus() == null || aiParam.getStatus() != StatusType.ENABLE.val) { |
| | | private void ensureSingleActive(Long tenantId, Long selfId, Integer status) { |
| | | if (status == null || status != StatusType.ENABLE.val) { |
| | | return; |
| | | } |
| | | LambdaQueryWrapper<AiParam> wrapper = new LambdaQueryWrapper<AiParam>() |
| | | .eq(AiParam::getStatus, StatusType.ENABLE.val); |
| | | .eq(AiParam::getTenantId, tenantId) |
| | | .eq(AiParam::getStatus, StatusType.ENABLE.val) |
| | | .eq(AiParam::getDeleted, 0); |
| | | if (selfId != null) { |
| | | wrapper.ne(AiParam::getId, selfId); |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | private AiParam requireOwnedRecord(Long id, Long tenantId) { |
| | | AiParam aiParam = this.getOne(new LambdaQueryWrapper<AiParam>() |
| | | .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); |
| | |
| | | public class AiPromptServiceImpl extends ServiceImpl<AiPromptMapper, AiPrompt> implements AiPromptService { |
| | | |
| | | @Override |
| | | public AiPrompt getActivePrompt(String code) { |
| | | public AiPrompt getActivePrompt(String code, Long tenantId) { |
| | | ensureTenantId(tenantId); |
| | | AiPrompt aiPrompt = this.getOne(new LambdaQueryWrapper<AiPrompt>() |
| | | .eq(AiPrompt::getTenantId, tenantId) |
| | | .eq(AiPrompt::getCode, code) |
| | | .eq(AiPrompt::getStatus, StatusType.ENABLE.val) |
| | | .eq(AiPrompt::getDeleted, 0) |
| | | .last("limit 1")); |
| | | if (aiPrompt == null) { |
| | | throw new CoolException("未找到启用中的 Prompt:" + code); |
| | |
| | | } |
| | | |
| | | @Override |
| | | public void validateBeforeSave(AiPrompt aiPrompt) { |
| | | public void validateBeforeSave(AiPrompt aiPrompt, Long tenantId) { |
| | | ensureTenantId(tenantId); |
| | | aiPrompt.setTenantId(tenantId); |
| | | ensureRequiredFields(aiPrompt); |
| | | ensureUniqueCode(aiPrompt.getCode(), null); |
| | | ensureUniqueCode(aiPrompt.getCode(), tenantId, null); |
| | | } |
| | | |
| | | @Override |
| | | public void validateBeforeUpdate(AiPrompt aiPrompt) { |
| | | public void validateBeforeUpdate(AiPrompt aiPrompt, Long tenantId) { |
| | | ensureTenantId(tenantId); |
| | | if (aiPrompt.getId() == null) { |
| | | throw new CoolException("Prompt ID 不能为空"); |
| | | } |
| | | AiPrompt current = requireOwnedRecord(aiPrompt.getId(), tenantId); |
| | | aiPrompt.setTenantId(current.getTenantId()); |
| | | ensureRequiredFields(aiPrompt); |
| | | ensureUniqueCode(aiPrompt.getCode(), aiPrompt.getId()); |
| | | ensureUniqueCode(aiPrompt.getCode(), tenantId, aiPrompt.getId()); |
| | | } |
| | | |
| | | private void ensureRequiredFields(AiPrompt aiPrompt) { |
| | |
| | | } |
| | | } |
| | | |
| | | private void ensureUniqueCode(String code, Long selfId) { |
| | | private void ensureUniqueCode(String code, Long tenantId, Long selfId) { |
| | | LambdaQueryWrapper<AiPrompt> wrapper = new LambdaQueryWrapper<AiPrompt>() |
| | | .eq(AiPrompt::getCode, code); |
| | | .eq(AiPrompt::getTenantId, tenantId) |
| | | .eq(AiPrompt::getCode, code) |
| | | .eq(AiPrompt::getDeleted, 0); |
| | | if (selfId != null) { |
| | | wrapper.ne(AiPrompt::getId, selfId); |
| | | } |
| | |
| | | throw new CoolException("Prompt 编码已存在"); |
| | | } |
| | | } |
| | | |
| | | private AiPrompt requireOwnedRecord(Long id, Long tenantId) { |
| | | AiPrompt aiPrompt = this.getOne(new LambdaQueryWrapper<AiPrompt>() |
| | | .eq(AiPrompt::getId, id) |
| | | .eq(AiPrompt::getTenantId, tenantId) |
| | | .eq(AiPrompt::getDeleted, 0) |
| | | .last("limit 1")); |
| | | if (aiPrompt == null) { |
| | | throw new CoolException("Prompt 不存在或无权访问"); |
| | | } |
| | | return aiPrompt; |
| | | } |
| | | |
| | | private void ensureTenantId(Long tenantId) { |
| | | if (tenantId == null) { |
| | | throw new CoolException("当前租户不存在"); |
| | | } |
| | | } |
| | | } |