diff --git a/ruoyi-modules/hwmom-ai/pom.xml b/ruoyi-modules/hwmom-ai/pom.xml new file mode 100644 index 00000000..f1594102 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/pom.xml @@ -0,0 +1,260 @@ + + + + org.dromara + ruoyi-modules + ${revision} + + 4.0.0 + + hwmom-ai + + + hwmom-ai人工智能系统模块 + + + + 1.59.0 + + + + + + org.dromara + ruoyi-common-nacos + + + + org.dromara + ruoyi-common-sentinel + + + + + org.dromara + ruoyi-common-log + + + + org.dromara + ruoyi-common-dict + + + + org.dromara + ruoyi-common-doc + + + + org.dromara + ruoyi-common-web + + + + org.dromara + ruoyi-common-mybatis + + + + org.dromara + ruoyi-common-dubbo + + + + org.dromara + ruoyi-common-seata + + + + org.dromara + ruoyi-common-idempotent + + + + org.dromara + ruoyi-common-tenant + + + + org.dromara + ruoyi-common-security + + + + org.dromara + ruoyi-common-translation + + + + org.dromara + ruoyi-common-sensitive + + + + + org.dromara + ruoyi-api-resource + + + + + + + com.microsoft.sqlserver + mssql-jdbc + + + org.dromara + hwmom-common-mom + 2.2.2 + compile + + + + + + + + + + org.apache.httpcomponents + httpclient + 4.5.13 + + + + org.apache.httpcomponents + httpmime + 4.5.13 + + + + + com.theokanning.openai-gpt3-java + service + 0.18.0 + + + com.theokanning.openai-gpt3-java + client + 0.18.0 + + + + + org.tensorflow + tensorflow-core-platform + 0.5.0 + + + org.deeplearning4j + deeplearning4j-core + 1.0.0-M2.1 + + + + ai.djl + api + 0.28.0 + + + ai.djl + model-zoo + 0.25.0 + + + ai.djl.pytorch + pytorch-engine + 0.25.0 + runtime + + + + + + + com.fasterxml.jackson.core + jackson-databind + + + + + org.apache.tika + tika-core + 2.9.1 + + + + + + + + + + + + org.projectlombok + lombok + true + + + + + com.tencentcloudapi + tencentcloud-sdk-java-common + LATEST + + + com.tencentcloudapi + tencentcloud-sdk-java + LATEST + + + + com.github.jelmerk + hnswlib-core + 1.1.0 + + + org.springframework.security + spring-security-core + + + org.springframework + spring-webflux + + + + + io.milvus + milvus-sdk-java + 2.4.1 + + + org.dromara + ruoyi-common-encrypt + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + repackage + + + + + + + + diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/HwMomAiApplication.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/HwMomAiApplication.java new file mode 100644 index 00000000..9665d8cd --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/HwMomAiApplication.java @@ -0,0 +1,25 @@ +package org.dromara.ai; + +import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; +import org.dromara.common.properties.MesProperties; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup; +import org.springframework.boot.context.properties.EnableConfigurationProperties; + +/** + * 系统模块 + * + * @author ruoyi + */ +@EnableDubbo +@SpringBootApplication +@EnableConfigurationProperties(MesProperties.class) +public class HwMomAiApplication { + public static void main(String[] args) { + SpringApplication application = new SpringApplication(HwMomAiApplication.class); + application.setApplicationStartup(new BufferingApplicationStartup(2048)); + application.run(args); + System.out.println("(♥◠‿◠)ノ゙ AI模块启动成功 ლ(´ڡ`ლ)゙ "); + } +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/controller/AiAssistantController.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/controller/AiAssistantController.java new file mode 100644 index 00000000..93d867d7 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/controller/AiAssistantController.java @@ -0,0 +1,158 @@ +package org.dromara.ai.controller; + +/** + * @Author xins + * @Date 2025/7/17 14:26 + * @Description:AI助手Controller + */ + +import cn.dev33.satoken.annotation.SaCheckPermission; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.ai.domain.bo.AiChatMessageBo; +import org.dromara.ai.domain.bo.AiChatMessageTopicBo; +import org.dromara.ai.domain.bo.AiModelBo; +import org.dromara.ai.domain.vo.AiChatMessageVo; +import org.dromara.ai.domain.vo.AiModelVo; +import org.dromara.ai.knowledge.vectorization.factory.EmbeddingServiceFactory; +import org.dromara.ai.knowledge.vectorization.process.IEmbeddingProcessor; +import org.dromara.ai.process.dto.AIMessage; +import org.dromara.ai.process.dto.AIRequest; +import org.dromara.ai.process.dto.AIResponse; +import org.dromara.ai.process.provider.processor.AIProviderProcessorFactory; +import org.dromara.ai.process.provider.processor.IUnifiedAIProviderProcessor; +import org.dromara.ai.service.*; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.web.core.BaseController; +import org.dromara.system.api.model.LoginUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.List; + + +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/assistant") +public class AiAssistantController extends BaseController { + + private final AIProviderProcessorFactory aiProviderProcessorFactory; + + private final EmbeddingServiceFactory embeddingServiceFactory; + + private final IAIAssistantService aiAssistantService; + + private final IAiModelService aiModelService; + + private final IAiChatMessageService aiChatMessageService; + + private final IAiChatMessageDetailService aiChatMessageDetailService; + + /** + * 下拉框查询AI模型列表 + */ + @GetMapping("/getAiModelJoinList") + public R> getAiModelJoinList(AiModelBo bo) { + List list = aiModelService.queryJoinList(bo); + return R.ok(list); + } + + /** + * 下拉框查询AI模型列表 + */ + @GetMapping("/getAiChatMessageList") + public R> getAiChatMessageList(AiChatMessageBo bo) { + List list = aiChatMessageService.queryList(bo); + return R.ok(list); + } + + /** + * 下拉框查询AI模型列表 + */ + @GetMapping("/getAiChatMessages/{sessionId}") + public R> getAiChatMessages(@PathVariable("sessionId") String sessionId) { + List list = aiChatMessageDetailService.getAIChatMessages(sessionId); + return R.ok(list); + } + + /** + * 更新message topic + */ + @Log(title = "会话标题", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PostMapping("/updateAiChatMessageTopic") + public R updateAiChatMessageTopic(@Validated(EditGroup.class) @RequestBody AiChatMessageTopicBo bo) { + return toAjax(aiChatMessageService.updateMessageTopic(bo)); + } + + /** + * 删除聊天消息 + * + * @param sessionId 会话ID + */ + @SaCheckPermission("ai:aiChatMessage:remove") + @Log(title = "聊天消息", businessType = BusinessType.DELETE) + @DeleteMapping("/{sessionId}") + public R remove(@NotEmpty(message = "会话ID不能为空") + @PathVariable String sessionId) { + return toAjax(aiChatMessageService.deleteWithValidBySessionId(sessionId, true)); + } + + /** + * 流式聊天接口 + */ + @PostMapping(value = "/chatStream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) + public Flux chatStream( + @RequestParam("provider") String provider, @RequestBody AIRequest request) { + LoginUser loginUser = LoginHelper.getLoginUser(); + IUnifiedAIProviderProcessor processor = aiProviderProcessorFactory.getProcessor(provider); + return processor.chatStream(request,loginUser); + } + + /** + * 非流式聊天接口 + */ + @PostMapping("/chat") + public Mono chat( + @RequestParam("provider") String provider, @RequestBody AIRequest request) { + + IUnifiedAIProviderProcessor processor = aiProviderProcessorFactory.getProcessor(provider); + return processor.chat(request); + } + + + + /** + * 向量化 + */ + @PostMapping("/embedding") + public String embedding( + @RequestParam("provider") String provider) { + + IEmbeddingProcessor embeddingProcessor = embeddingServiceFactory.getProcessor(provider); + embeddingProcessor.getEmbedding("你好"); + return ""; + } + + + + + + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/controller/AiChatMessageController.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/controller/AiChatMessageController.java new file mode 100644 index 00000000..756253b4 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/controller/AiChatMessageController.java @@ -0,0 +1,117 @@ +package org.dromara.ai.controller; + +import java.util.List; + +import lombok.RequiredArgsConstructor; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.web.core.BaseController; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.ai.domain.vo.AiChatMessageVo; +import org.dromara.ai.domain.bo.AiChatMessageBo; +import org.dromara.ai.service.IAiChatMessageService; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +/** + * 聊天消息 + * 前端访问路由地址为:/ai/aiChatMessage + * + * @author xins + * @date 2025-08-14 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/aiChatMessage") +public class AiChatMessageController extends BaseController { + + private final IAiChatMessageService aiChatMessageService; + + /** + * 查询聊天消息列表 + */ + @SaCheckPermission("ai:aiChatMessage:list") + @GetMapping("/list") + public TableDataInfo list(AiChatMessageBo bo, PageQuery pageQuery) { + return aiChatMessageService.queryPageList(bo, pageQuery); + } + + /** + * 导出聊天消息列表 + */ + @SaCheckPermission("ai:aiChatMessage:export") + @Log(title = "聊天消息", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(AiChatMessageBo bo, HttpServletResponse response) { + List list = aiChatMessageService.queryList(bo); + ExcelUtil.exportExcel(list, "聊天消息", AiChatMessageVo.class, response); + } + + /** + * 获取聊天消息详细信息 + * + * @param chatMessageId 主键 + */ + @SaCheckPermission("ai:aiChatMessage:query") + @GetMapping("/{chatMessageId}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long chatMessageId) { + return R.ok(aiChatMessageService.queryById(chatMessageId)); + } + + /** + * 新增聊天消息 + */ + @SaCheckPermission("ai:aiChatMessage:add") + @Log(title = "聊天消息", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody AiChatMessageBo bo) { + return toAjax(aiChatMessageService.insertByBo(bo)); + } + + /** + * 修改聊天消息 + */ + @SaCheckPermission("ai:aiChatMessage:edit") + @Log(title = "聊天消息", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody AiChatMessageBo bo) { + return toAjax(aiChatMessageService.updateByBo(bo)); + } + + /** + * 删除聊天消息 + * + * @param chatMessageIds 主键串 + */ + @SaCheckPermission("ai:aiChatMessage:remove") + @Log(title = "聊天消息", businessType = BusinessType.DELETE) + @DeleteMapping("/{chatMessageIds}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] chatMessageIds) { + return toAjax(aiChatMessageService.deleteWithValidByIds(List.of(chatMessageIds), true)); + } + + + /** + * 下拉框查询聊天消息列表 + */ + + @GetMapping("/getAiChatMessageList") + public R> getAiChatMessageList(AiChatMessageBo bo) { + List list = aiChatMessageService.queryList(bo); + return R.ok(list); + } +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/controller/AiKnowledgeBaseTypeController.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/controller/AiKnowledgeBaseTypeController.java new file mode 100644 index 00000000..afec7ccf --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/controller/AiKnowledgeBaseTypeController.java @@ -0,0 +1,117 @@ +package org.dromara.ai.controller; + +import java.util.List; + +import lombok.RequiredArgsConstructor; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.web.core.BaseController; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.ai.domain.vo.AiKnowledgeBaseTypeVo; +import org.dromara.ai.domain.bo.AiKnowledgeBaseTypeBo; +import org.dromara.ai.service.IAiKnowledgeBaseTypeService; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +/** + * AI知识库类型 + * 前端访问路由地址为:/ai/aiKnowledgeBaseType + * + * @author xins + * @date 2025-08-06 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/aiKnowledgeBaseType") +public class AiKnowledgeBaseTypeController extends BaseController { + + private final IAiKnowledgeBaseTypeService aiKnowledgeBaseTypeService; + + /** + * 查询AI知识库类型列表 + */ + @SaCheckPermission("ai:aiKnowledgeBaseType:list") + @GetMapping("/list") + public TableDataInfo list(AiKnowledgeBaseTypeBo bo, PageQuery pageQuery) { + return aiKnowledgeBaseTypeService.queryPageList(bo, pageQuery); + } + + /** + * 导出AI知识库类型列表 + */ + @SaCheckPermission("ai:aiKnowledgeBaseType:export") + @Log(title = "AI知识库类型", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(AiKnowledgeBaseTypeBo bo, HttpServletResponse response) { + List list = aiKnowledgeBaseTypeService.queryList(bo); + ExcelUtil.exportExcel(list, "AI知识库类型", AiKnowledgeBaseTypeVo.class, response); + } + + /** + * 获取AI知识库类型详细信息 + * + * @param knowledgeBaseTypeId 主键 + */ + @SaCheckPermission("ai:aiKnowledgeBaseType:query") + @GetMapping("/{knowledgeBaseTypeId}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long knowledgeBaseTypeId) { + return R.ok(aiKnowledgeBaseTypeService.queryById(knowledgeBaseTypeId)); + } + + /** + * 新增AI知识库类型 + */ + @SaCheckPermission("ai:aiKnowledgeBaseType:add") + @Log(title = "AI知识库类型", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody AiKnowledgeBaseTypeBo bo) { + return toAjax(aiKnowledgeBaseTypeService.insertByBo(bo)); + } + + /** + * 修改AI知识库类型 + */ + @SaCheckPermission("ai:aiKnowledgeBaseType:edit") + @Log(title = "AI知识库类型", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody AiKnowledgeBaseTypeBo bo) { + return toAjax(aiKnowledgeBaseTypeService.updateByBo(bo)); + } + + /** + * 删除AI知识库类型 + * + * @param knowledgeBaseTypeIds 主键串 + */ + @SaCheckPermission("ai:aiKnowledgeBaseType:remove") + @Log(title = "AI知识库类型", businessType = BusinessType.DELETE) + @DeleteMapping("/{knowledgeBaseTypeIds}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] knowledgeBaseTypeIds) { + return toAjax(aiKnowledgeBaseTypeService.deleteWithValidByIds(List.of(knowledgeBaseTypeIds), true)); + } + + + /** + * 下拉框查询AI知识库类型列表 + */ + + @GetMapping("/getAiKnowledgeBaseTypeList") + public R> getAiKnowledgeBaseTypeList(AiKnowledgeBaseTypeBo bo) { + List list = aiKnowledgeBaseTypeService.queryList(bo); + return R.ok(list); + } +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/controller/AiModelController.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/controller/AiModelController.java new file mode 100644 index 00000000..9dca7076 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/controller/AiModelController.java @@ -0,0 +1,181 @@ +package org.dromara.ai.controller; + +import java.time.Duration; +import java.util.Collections; +import java.util.List; + +import lombok.RequiredArgsConstructor; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.dromara.ai.domain.bo.AiBaseModelBo; +import org.dromara.ai.domain.bo.AiPlatformBo; +import org.dromara.ai.domain.vo.AiBaseModelVo; +import org.dromara.ai.domain.vo.AiPlatformVo; +import org.dromara.ai.process.dto.AIMessage; +import org.dromara.ai.process.dto.AIRequest; +import org.dromara.ai.process.dto.AIResponse; +import org.dromara.ai.process.provider.processor.AIProviderProcessorFactory; +import org.dromara.ai.process.provider.processor.IUnifiedAIProviderProcessor; +import org.dromara.ai.service.IAIAssistantService; +import org.dromara.ai.service.IAiBaseModelService; +import org.dromara.ai.service.IAiPlatformService; +import org.dromara.common.encrypt.utils.EncryptUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.web.core.BaseController; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.ai.domain.vo.AiModelVo; +import org.dromara.ai.domain.bo.AiModelBo; +import org.dromara.ai.service.IAiModelService; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +/** + * AI模型 + * 前端访问路由地址为:/ai/aiModel + * + * @author xins + * @date 2025-08-07 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/aiModel") +public class AiModelController extends BaseController { + + private final IAiModelService aiModelService; + + private final IAiPlatformService aiPlatformService; + + private final IAiBaseModelService baseModelService; + + private final IAIAssistantService assistantService; + + @Autowired + private AIProviderProcessorFactory aiProviderProcessorFactory; + + /** + * 查询AI模型列表 + */ + @SaCheckPermission("ai:aiModel:list") + @GetMapping("/list") + public R> list(AiModelBo bo) { + return R.ok(aiModelService.queryJoinList(bo)); + } + + /** + * 导出AI模型列表 + */ + @SaCheckPermission("ai:aiModel:export") + @Log(title = "AI模型", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(AiModelBo bo, HttpServletResponse response) { + List list = aiModelService.queryList(bo); + ExcelUtil.exportExcel(list, "AI模型", AiModelVo.class, response); + } + + /** + * 获取AI模型详细信息 + * + * @param modelId 主键 + */ + @SaCheckPermission("ai:aiModel:query") + @GetMapping("/{modelId}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long modelId) { + return R.ok(aiModelService.queryById(modelId)); + } + + /** + * 新增AI模型 + */ + @SaCheckPermission("ai:aiModel:add") + @Log(title = "AI模型", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody AiModelBo bo) { + return toAjax(aiModelService.insertByBo(bo)); + } + + /** + * 修改AI模型 + */ + @SaCheckPermission("ai:aiModel:edit") + @Log(title = "AI模型", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody AiModelBo bo) { + return toAjax(aiModelService.updateByBo(bo)); + } + + /** + * 删除AI模型 + * + * @param modelIds 主键串 + */ + @SaCheckPermission("ai:aiModel:remove") + @Log(title = "AI模型", businessType = BusinessType.DELETE) + @DeleteMapping("/{modelIds}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] modelIds) { + return toAjax(aiModelService.deleteWithValidByIds(List.of(modelIds), true)); + } + + + /** + * 下拉框查询AI模型列表 + */ + + @GetMapping("/getAiModelList") + public R> getAiModelList(AiModelBo bo) { + List list = aiModelService.queryList(bo); + return R.ok(list); + } + + + /** + * 测试配置是否正确 + */ + @PostMapping("/testAIModel") + public R testAIModel(@RequestParam("provider") String provider, @RequestBody AIRequest request) { + IUnifiedAIProviderProcessor processor = aiProviderProcessorFactory.getProcessor(provider); + + String modelType = request.getModelType(); + switch (modelType) { + case "LLM": + AIResponse aiResponse = processor.chatTest(request).block(Duration.ofSeconds(10)); + if (aiResponse != null && aiResponse.isSuccess()) { + System.out.println(aiResponse.getContent()); + System.out.println(aiResponse.getRawResponse()); + return R.ok(); + } else { + System.out.println(aiResponse.getContent()); + System.out.println(aiResponse.getErrorMessage()); +// return R.fail(aiResponse.getErrorMessage()); + throw new RuntimeException(aiResponse.getErrorMessage()); + } + case "Embedding": + List embedding = processor.getEmbeddingTest(request); + if (embedding != null && !embedding.isEmpty()) { + System.out.println(embedding); + return R.ok(); + } else { +// return R.fail(aiResponse.getErrorMessage()); + } + break; + + } + + return R.fail("模型类型错误"); + + } + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/controller/AiPlatformController.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/controller/AiPlatformController.java new file mode 100644 index 00000000..81622a61 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/controller/AiPlatformController.java @@ -0,0 +1,144 @@ +package org.dromara.ai.controller; + +import java.util.List; + +import lombok.RequiredArgsConstructor; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.dromara.ai.domain.AiBaseModel; +import org.dromara.ai.domain.bo.AiBaseModelBo; +import org.dromara.ai.service.IAiBaseModelService; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.web.core.BaseController; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.ai.domain.vo.AiPlatformVo; +import org.dromara.ai.domain.bo.AiPlatformBo; +import org.dromara.ai.service.IAiPlatformService; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +/** + * AI平台 + * 前端访问路由地址为:/ai/base/aiPlatform + * + * @author xins + * @date 2025-08-06 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/base/aiPlatform") +public class AiPlatformController extends BaseController { + + private final IAiPlatformService aiPlatformService; + + private final IAiBaseModelService aiBaseModelService; + +// /** +// * 查询AI平台列表 +// */ +// @SaCheckPermission("ai/base:aiPlatform:list") +// @GetMapping("/list") +// public TableDataInfo list(AiPlatformBo bo, PageQuery pageQuery) { +// return aiPlatformService.queryPageList(bo, pageQuery); +// } + + /** + * 查询AI平台列表 + */ + @SaCheckPermission("ai/base:aiPlatform:list") + @GetMapping("/list") + public R> list(AiPlatformBo bo) { + return R.ok(aiPlatformService.queryList(bo)); + } + + + @GetMapping("/getAiBaseModelList") + public R> getAiBaseModelList(AiBaseModelBo bo) { + List list = aiBaseModelService.queryJoinList(bo); + return R.ok(list); + } + + + + + + + + /** + * 导出AI平台列表 + */ + @SaCheckPermission("ai/base:aiPlatform:export") + @Log(title = "AI平台", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(AiPlatformBo bo, HttpServletResponse response) { + List list = aiPlatformService.queryList(bo); + ExcelUtil.exportExcel(list, "AI平台", AiPlatformVo.class, response); + } + + /** + * 获取AI平台详细信息 + * + * @param platformId 主键 + */ + @SaCheckPermission("ai/base:aiPlatform:query") + @GetMapping("/{platformId}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long platformId) { + return R.ok(aiPlatformService.queryById(platformId)); + } + + /** + * 新增AI平台 + */ + @SaCheckPermission("ai/base:aiPlatform:add") + @Log(title = "AI平台", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody AiPlatformBo bo) { + return toAjax(aiPlatformService.insertByBo(bo)); + } + + /** + * 修改AI平台 + */ + @SaCheckPermission("ai/base:aiPlatform:edit") + @Log(title = "AI平台", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody AiPlatformBo bo) { + return toAjax(aiPlatformService.updateByBo(bo)); + } + + /** + * 删除AI平台 + * + * @param platformIds 主键串 + */ + @SaCheckPermission("ai/base:aiPlatform:remove") + @Log(title = "AI平台", businessType = BusinessType.DELETE) + @DeleteMapping("/{platformIds}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] platformIds) { + return toAjax(aiPlatformService.deleteWithValidByIds(List.of(platformIds), true)); + } + + + /** + * 下拉框查询AI平台列表 + */ + + @GetMapping("/getAiPlatformList") + public R> getAiPlatformList(AiPlatformBo bo) { + List list = aiPlatformService.queryList(bo); + return R.ok(list); + } +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/AiApiEndpoint.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/AiApiEndpoint.java new file mode 100644 index 00000000..7617e1aa --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/AiApiEndpoint.java @@ -0,0 +1,65 @@ +package org.dromara.ai.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * API端点对象 ai_api_endpoint + * + * @author xins + * @date 2025-08-06 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("ai_api_endpoint") +public class AiApiEndpoint extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + private Long apiEndpointId; + + /** + * 基础模型ID,关联ai_base_model + */ + private Long baseModelId; + + /** + * api端点名称,例如: chat/completions, embeddings + */ + private String apiEndpointName; + + /** + * api版本 + */ + private String apiVersion; + + /** + * GET, POST + */ + private String httpMethod; + + /** + * api端点路径,例如: /v1/chat/completions + */ + private String apiEndpointPath; + + /** + * 默认标识(1是,0否) + */ + private String defaultFlag; + + /** + * 每分钟请求限制 + */ + private Long rateLimit; + + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/AiBaseModel.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/AiBaseModel.java new file mode 100644 index 00000000..c5a956b6 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/AiBaseModel.java @@ -0,0 +1,82 @@ +package org.dromara.ai.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.util.List; + +/** + * 基础模型对象 ai_base_model + * + * @author xins + * @date 2025-08-06 + */ +@Data +@TableName("ai_base_model") +public class AiBaseModel { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + private Long baseModelId; + + /** + * 平台ID,关联ai_platform + */ + private Long platformId; + + /** + * 模型类型ID,关联ai_model_type + */ + private Long modelTypeId; + + /** + * 模型类型编码,同ai_model_type中的编码 + */ + private Long modelTypeCode; + + /** + * 基础模型名称,例如: DeepSeek-V3, Qwen1.5-72B, ChatGLM3 + */ + private String baseModelName; + + /** + * 基础模型描述 + */ + private String baseModelDescription; + + /** + * 上下文长度(如32k) + */ + private Long contextLength; + + /** + * 激活标识(1是,0否) + */ + private String activeFlag; + + /** + * 模型能力列表 + */ + @TableField(exist = false) + private List aiModelCapabilityList; + + /** + * api端点列表 + */ + @TableField(exist = false) + private List aiApiEndpointList; + + + /** + * 模型类型名称 + */ + @TableField(exist = false) + private String modelTypeName; +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/AiChatMessage.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/AiChatMessage.java new file mode 100644 index 00000000..53e69b65 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/AiChatMessage.java @@ -0,0 +1,66 @@ +package org.dromara.ai.domain; + +import org.dromara.common.tenant.core.TenantEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 聊天消息对象 ai_chat_message + * + * @author xins + * @date 2025-08-14 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("ai_chat_message") +public class AiChatMessage extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "chat_message_id", type = IdType.AUTO) + private Long chatMessageId; + + /** + * 会话ID + */ + private String sessionId; + + /** + * 聊天标题 + */ + private String messageTopic; + + /** + * 扣除金额(暂时不用) + */ + private Long deductCost; + + /** + * 累计 Tokens + */ + private Long totalToken; + + /** + * 模型ID,关联ai_model + */ + private Long modelId; + + /** + * 知识库ID,关联ai_knowledge_base + */ + private Long knowledgeBaseId; + + /** + * 聊天类型(1AI问答,2知识库问答,3AI生成SQL,4AI填报) + */ + private String messageType; + + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/AiChatMessageDetail.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/AiChatMessageDetail.java new file mode 100644 index 00000000..d50c9562 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/AiChatMessageDetail.java @@ -0,0 +1,86 @@ +package org.dromara.ai.domain; + +import org.dromara.common.tenant.core.TenantEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 聊天消息详情对象 ai_chat_message_detail + * + * @author xins + * @date 2025-08-14 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("ai_chat_message_detail") +public class AiChatMessageDetail extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "message_detail_id", type = IdType.AUTO) + private Long messageDetailId; + + /** + * 聊天信息ID,关联ai_chat_message + */ + private Long chatMessageId; + + /** + * 会话ID + */ + private String sessionId; + + /** + * 提问内容 + */ + private String questionContent; + + /** + * 回答内容 + */ + private String answerContent; + + /** + * 提问token数量 + */ + private Long promptToken; + + /** + * 回复token数量 + */ + private Long completionToken; + + /** + * 累计 Token数量 + */ + private Long totalToken; + + /** + * 模型ID,关联ai_model + */ + private Long modelId; + + /** + * 知识库ID,关联ai_knowledge_base + */ + private Long knowledgeBaseId; + + /** + * 是否携带历史内容(1是,0否) + */ + private String takeFlag; + + /** + * 完整标识(1是,0否),代表回复信息是否完整回复,中间可以暂停继续。 + */ + private String completeFlag; + + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/AiKnowledgeBaseType.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/AiKnowledgeBaseType.java new file mode 100644 index 00000000..4fecce3c --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/AiKnowledgeBaseType.java @@ -0,0 +1,47 @@ +package org.dromara.ai.domain; + +import org.dromara.common.tenant.core.TenantEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * AI知识库类型对象 ai_knowledge_base_type + * + * @author xins + * @date 2025-08-06 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("ai_knowledge_base_type") +public class AiKnowledgeBaseType extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "knowledge_base_type_id", type = IdType.AUTO) + private Long knowledgeBaseTypeId; + + /** + * 知识库类型名称 + */ + private String knowledgeBaseTypeName; + + /** + * 备注 + */ + private String remark; + + /** + * 删除标志(0代表存在 2代表删除) + */ + @TableLogic + private String delFlag; + + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/AiModel.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/AiModel.java new file mode 100644 index 00000000..c1a0b074 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/AiModel.java @@ -0,0 +1,162 @@ +package org.dromara.ai.domain; + +import org.dromara.common.tenant.core.TenantEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * AI模型对象 ai_model + * + * @author xins + * @date 2025-08-07 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("ai_model") +public class AiModel extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "model_id", type = IdType.AUTO) + private Long modelId; + + /** + * 模型名称 + */ + private String modelName; + + /** + * AI平台ID,关联ai_platform + */ + private Long platformId; + + /** + * 模型类型ID,关联ai_model_type + */ + private Long modelTypeId; + + /** + * 基础模型ID,关联ai_base_model + */ + private Long baseModelId; + + /** + * 计费类型(1token计费,2次数计费) + */ + private String chargeType; + + /** + * 模型价格 + */ + private Long modelPrice; + + /** + * 请求地址 + */ + private String apiEndpoint; + + /** + * 秘钥 + */ + private String apiKey; + + /** + * 密钥 + */ + private String apiSecret; + + /** + * 向量库(1milvus,2weaviate,) + */ + private String vectorLibrary; + + /** + * 描述 + */ + private String description; + + /** + * 高级配置—模型温度(0-1);值越大,回复内容越赋有多样性创造性、随机性;设为0根据事实回答,希望得到精准答案应该降低该参数;日常聊天建议0.5-0.8 + */ + private Long modelTemperature; + + /** + * 高级配置—词汇属性(0-1);值越小,AI生成的内容越单调也越容易理解;值越大,AI回复的词汇范围越大,越多样化 + */ + private Long vocabularyAttribute; + + /** + * 高级配置—话题属性(-2.0-2.0);值越大,越能够让AI更好的控制新话题的引入,建议微调或不变 + */ + private Long topicAttribute; + + /** + * 高级配置—重复属性(-2.0-2.0);值越大,越能够让AI更好的避免重复之前说过的话,建议微调或不变 + */ + private Long duplicateAttribute; + + /** + * 高级配置—最大回复(1-16000);设置AI最大回复内容大小,会影响返回结果内容的长度;普通聊天建议500-800;短文生成建议800-2000;代码生成建议2000-3600;长文生成建议4000左右(或选择长回复模型) + */ + private Long maxReply; + + /** + * 默认标识(1是,0否) + */ + private String defaultFlag; + + /** + * 激活标识(1是,0否) + */ + private String activeFlag; + + /** + * 删除标志(0代表存在 2代表删除) + */ + @TableLogic + private String delFlag; + + /** + * AI平台编码 + */ + @TableField(exist = false) + private String platformCode; + + /** + * AI平台名称 + */ + @TableField(exist = false) + private String platformName; + + /** + * AI平台图标 + */ + @TableField(exist = false) + private String platformIcon; + + /** + * 模型类型编码 + */ + @TableField(exist = false) + private String modelTypeCode; + + /** + * 模型类型名称 + */ + @TableField(exist = false) + private String modelTypeName; + + /** + * 基础模型名称 + */ + @TableField(exist = false) + private String baseModelName; + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/AiModelCapability.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/AiModelCapability.java new file mode 100644 index 00000000..dd12c331 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/AiModelCapability.java @@ -0,0 +1,45 @@ +package org.dromara.ai.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 模型能力对象 ai_model_capability + * + * @author xins + * @date 2025-08-06 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("ai_model_capability") +public class AiModelCapability extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + private Long modelCapabilityId; + + /** + * 基础模型ID,关联ai_base_model + */ + private Long baseModelId; + + /** + * 模型能力名称,例如: 多轮对话, 函数调用, 视觉理解 + */ + private String modelCapabilityName; + + /** + * 模型能力描述 + */ + private String modelCapabilityDescription; + + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/AiModelType.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/AiModelType.java new file mode 100644 index 00000000..7f526027 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/AiModelType.java @@ -0,0 +1,45 @@ +package org.dromara.ai.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 模型类型对象 ai_model_type + * + * @author xins + * @date 2025-08-06 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("ai_model_type") +public class AiModelType extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + private Long modelTypeId; + + /** + * 模型类型编码(LLM、Embedding) + */ + private String modelTypeCode; + + /** + * 模型类型名称(语言模型、向量模型) + */ + private String modelTypeName; + + /** + * 模型类型描述 + */ + private String modelTypeDescription; + + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/AiPlatform.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/AiPlatform.java new file mode 100644 index 00000000..d959eca9 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/AiPlatform.java @@ -0,0 +1,66 @@ +package org.dromara.ai.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * AI平台对象 ai_platform + * + * @author xins + * @date 2025-08-06 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("ai_platform") +public class AiPlatform extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 平台ID,主键 + */ + private Long platformId; + + /** + * 平台编码,如DeepSeek, TongYi-QianWen, Tencent-LKE, WenXin-YiYan + */ + private String platformCode; + + /** + * 平台名称,如DeepSeek, 通义千问, 智普AI + */ + private String platformName; + + /** + * 平台描述 + */ + private String platformDescription; + + /** + * base api url地址 + */ + private String baseApiUrl; + + /** + * 平台图标 + */ + private String platformIcon; + + /** + * 授权方法(1,API_KEY,2,OAUTH2),先支持API_KEY + */ + private String authMethod; + + /** + * 删除标志(0代表存在 2代表删除) + */ + @TableLogic + private String delFlag; + + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/bo/AiBaseModelBo.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/bo/AiBaseModelBo.java new file mode 100644 index 00000000..65727f7f --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/bo/AiBaseModelBo.java @@ -0,0 +1,72 @@ +package org.dromara.ai.domain.bo; + +import com.alibaba.excel.annotation.ExcelProperty; +import org.dromara.ai.domain.AiBaseModel; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +/** + * 基础模型业务对象 ai_base_model + * + * @author xins + * @date 2025-08-06 + */ +@Data +@AutoMapper(target = AiBaseModel.class, reverseConvertGenerate = false) +public class AiBaseModelBo { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long baseModelId; + + /** + * 平台ID,关联ai_platform + */ + @NotNull(message = "平台ID,关联ai_platform不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long platformId; + + /** + * 模型类型ID,关联ai_model_type + */ + @NotNull(message = "模型类型ID,关联ai_model_type不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long modelTypeId; + + /** + * 模型类型编码,同ai_model_type中的编码 + */ + @NotNull(message = "模型类型编码不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long modelTypeCode; + + /** + * 基础模型名称,例如: DeepSeek-V3, Qwen1.5-72B, ChatGLM3 + */ + @NotBlank(message = "基础模型名称,例如: DeepSeek-V3, Qwen1.5-72B, ChatGLM3不能为空", groups = { AddGroup.class, EditGroup.class }) + private String baseModelName; + + /** + * 基础模型描述 + */ + @NotBlank(message = "基础模型描述不能为空", groups = { AddGroup.class, EditGroup.class }) + private String baseModelDescription; + + /** + * 上下文长度(如32k) + */ + @NotNull(message = "上下文长度(如32k)不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long contextLength; + + /** + * 激活标识(1是,0否) + */ + @NotBlank(message = "激活标识(1是,0否)不能为空", groups = { AddGroup.class, EditGroup.class }) + private String activeFlag; + + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/bo/AiChatMessageBo.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/bo/AiChatMessageBo.java new file mode 100644 index 00000000..ff6906b9 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/bo/AiChatMessageBo.java @@ -0,0 +1,73 @@ +package org.dromara.ai.domain.bo; + +import com.alibaba.excel.annotation.ExcelProperty; +import org.dromara.ai.domain.AiChatMessage; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +/** + * 聊天消息业务对象 ai_chat_message + * + * @author xins + * @date 2025-08-14 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = AiChatMessage.class, reverseConvertGenerate = false) +public class AiChatMessageBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long chatMessageId; + + /** + * 会话ID + */ + @NotNull(message = "会话ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private String sessionId; + + /** + * 聊天标题 + */ + @NotBlank(message = "聊天标题不能为空", groups = { AddGroup.class, EditGroup.class }) + private String messageTopic; + + /** + * 扣除金额(暂时不用) + */ + @NotNull(message = "扣除金额(暂时不用)不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long deductCost; + + /** + * 累计 Tokens + */ + @NotNull(message = "累计 Tokens不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long totalToken; + + /** + * 模型ID,关联ai_model + */ + @NotNull(message = "模型ID,关联ai_model不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long modelId; + + /** + * 知识库ID,关联ai_knowledge_base + */ + @NotNull(message = "知识库ID,关联ai_knowledge_base不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long knowledgeBaseId; + + /** + * 聊天类型(1AI问答,2知识库问答,3AI生成SQL,4AI填报) + */ + @NotBlank(message = "聊天类型(1AI问答,2知识库问答,3AI生成SQL,4AI填报)不能为空", groups = { AddGroup.class, EditGroup.class }) + private String messageType; + + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/bo/AiChatMessageDetailBo.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/bo/AiChatMessageDetailBo.java new file mode 100644 index 00000000..13cb0677 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/bo/AiChatMessageDetailBo.java @@ -0,0 +1,96 @@ +package org.dromara.ai.domain.bo; + +import org.dromara.ai.domain.AiChatMessageDetail; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +/** + * 聊天消息详情业务对象 ai_chat_message_detail + * + * @author xins + * @date 2025-08-14 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = AiChatMessageDetail.class, reverseConvertGenerate = false) +public class AiChatMessageDetailBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long messageDetailId; + + /** + * 聊天信息ID,关联ai_chat_message + */ + @NotNull(message = "聊天信息ID,关联ai_chat_message不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long chatMessageId; + + /** + * 会话ID + */ + @NotNull(message = "会话ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private String sessionId; + + /** + * 提问内容 + */ + @NotBlank(message = "提问内容不能为空", groups = { AddGroup.class, EditGroup.class }) + private String questionContent; + + /** + * 回答内容 + */ + @NotBlank(message = "回答内容不能为空", groups = { AddGroup.class, EditGroup.class }) + private String answerContent; + + /** + * 提问token数量 + */ + @NotNull(message = "提问token数量不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long promptToken; + + /** + * 回复token数量 + */ + @NotNull(message = "回复token数量不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long completionToken; + + /** + * 累计 Token数量 + */ + @NotNull(message = "累计 Token数量不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long totalToken; + + /** + * 模型ID,关联ai_model + */ + @NotNull(message = "模型ID,关联ai_model不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long modelId; + + /** + * 知识库ID,关联ai_knowledge_base + */ + @NotNull(message = "知识库ID,关联ai_knowledge_base不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long knowledgeBaseId; + + /** + * 是否携带历史内容(1是,0否) + */ + @NotBlank(message = "是否携带历史内容(1是,0否)不能为空", groups = { AddGroup.class, EditGroup.class }) + private String takeFlag; + + /** + * 完整标识(1是,0否),代表回复信息是否完整回复,中间可以暂停继续。 + */ + @NotBlank(message = "完整标识(1是,0否),代表回复信息是否完整回复,中间可以暂停继续。不能为空", groups = { AddGroup.class, EditGroup.class }) + private String completeFlag; + + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/bo/AiChatMessageTopicBo.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/bo/AiChatMessageTopicBo.java new file mode 100644 index 00000000..7dedd287 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/bo/AiChatMessageTopicBo.java @@ -0,0 +1,38 @@ +package org.dromara.ai.domain.bo; + +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.ai.domain.AiChatMessage; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +/** + * 聊天消息业务对象 ai_chat_message + * + * @author xins + * @date 2025-08-18 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = AiChatMessage.class, reverseConvertGenerate = false) +public class AiChatMessageTopicBo extends BaseEntity { + + /** + * 会话ID + */ + @NotNull(message = "会话ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private String sessionId; + + /** + * 聊天标题 + */ + @NotBlank(message = "聊天标题不能为空", groups = { AddGroup.class, EditGroup.class }) + private String messageTopic; + + + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/bo/AiKnowledgeBaseTypeBo.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/bo/AiKnowledgeBaseTypeBo.java new file mode 100644 index 00000000..bb85287e --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/bo/AiKnowledgeBaseTypeBo.java @@ -0,0 +1,41 @@ +package org.dromara.ai.domain.bo; + +import org.dromara.ai.domain.AiKnowledgeBaseType; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +/** + * AI知识库类型业务对象 ai_knowledge_base_type + * + * @author xins + * @date 2025-08-06 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = AiKnowledgeBaseType.class, reverseConvertGenerate = false) +public class AiKnowledgeBaseTypeBo extends BaseEntity { + + /** + * 主键 + */ + private Long knowledgeBaseTypeId; + + /** + * 知识库类型名称 + */ + @NotBlank(message = "知识库类型名称不能为空", groups = { AddGroup.class, EditGroup.class }) + private String knowledgeBaseTypeName; + + /** + * 备注 + */ + @NotBlank(message = "备注不能为空", groups = { AddGroup.class, EditGroup.class }) + private String remark; + + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/bo/AiModelBo.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/bo/AiModelBo.java new file mode 100644 index 00000000..1faf719c --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/bo/AiModelBo.java @@ -0,0 +1,125 @@ +package org.dromara.ai.domain.bo; + +import com.alibaba.excel.annotation.ExcelProperty; +import org.dromara.ai.domain.AiModel; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +/** + * AI模型业务对象 ai_model + * + * @author xins + * @date 2025-08-07 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = AiModel.class, reverseConvertGenerate = false) +public class AiModelBo extends BaseEntity { + + /** + * 主键 + */ + private Long modelId; + + /** + * 模型名称 + */ + @NotBlank(message = "模型名称不能为空", groups = { AddGroup.class, EditGroup.class }) + private String modelName; + + /** + * AI平台ID,关联ai_platform + */ + @NotNull(message = "AI平台ID,关联ai_platform不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long platformId; + + /** + * 模型类型ID,关联ai_model_type + */ + @NotNull(message = "模型类型ID,关联ai_model_type不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long modelTypeId; + + /** + * 基础模型ID,关联ai_base_model + */ + @NotNull(message = "基础模型ID,关联ai_base_model不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long baseModelId; + + /** + * 计费类型(1token计费,2次数计费) + */ + private String chargeType; + + /** + * 模型价格 + */ + private Long modelPrice; + + /** + * 请求地址 + */ + private String apiEndpoint; + + /** + * 秘钥 + */ + private String apiKey; + + /** + * 密钥 + */ + private String apiSecret; + + /** + * 向量库(1milvus,2weaviate,) + */ + private String vectorLibrary; + + /** + * 描述 + */ + private String description; + + /** + * 高级配置—模型温度(0-1);值越大,回复内容越赋有多样性创造性、随机性;设为0根据事实回答,希望得到精准答案应该降低该参数;日常聊天建议0.5-0.8 + */ + private Long modelTemperature; + + /** + * 高级配置—词汇属性(0-1);值越小,AI生成的内容越单调也越容易理解;值越大,AI回复的词汇范围越大,越多样化 + */ + private Long vocabularyAttribute; + + /** + * 高级配置—话题属性(-2.0-2.0);值越大,越能够让AI更好的控制新话题的引入,建议微调或不变 + */ + private Long topicAttribute; + + /** + * 高级配置—重复属性(-2.0-2.0);值越大,越能够让AI更好的避免重复之前说过的话,建议微调或不变 + */ + private Long duplicateAttribute; + + /** + * 高级配置—最大回复(1-16000);设置AI最大回复内容大小,会影响返回结果内容的长度;普通聊天建议500-800;短文生成建议800-2000;代码生成建议2000-3600;长文生成建议4000左右(或选择长回复模型) + */ + private Long maxReply; + + /** + * 默认标识(1是,0否) + */ + private String defaultFlag; + + /** + * 激活标识(1是,0否) + */ +// @NotBlank(message = "激活标识(1是,0否)不能为空", groups = { AddGroup.class, EditGroup.class }) + private String activeFlag; + + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/bo/AiModelTypeBo.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/bo/AiModelTypeBo.java new file mode 100644 index 00000000..b98f80d4 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/bo/AiModelTypeBo.java @@ -0,0 +1,51 @@ +package org.dromara.ai.domain.bo; + +import com.alibaba.excel.annotation.ExcelProperty; +import org.dromara.ai.domain.AiModelType; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +/** + * 模型类型业务对象 ai_model_type + * + * @author xins + * @date 2025-08-06 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = AiModelType.class, reverseConvertGenerate = false) +public class AiModelTypeBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long modelTypeId; + + /** + * 模型类型编码(LLM、Embedding) + */ + @NotBlank(message = "模型类型编码不能为空", groups = { AddGroup.class, EditGroup.class }) + private String modelTypeCode; + + /** + * 模型类型名称(语言模型、向量模型) + */ + @NotBlank(message = "模型类型名称不能为空", groups = { AddGroup.class, EditGroup.class }) + private String modelTypeName; + + /** + * 模型类型描述 + */ + @NotBlank(message = "模型类型描述不能为空", groups = { AddGroup.class, EditGroup.class }) + private String modelTypeDescription; + + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/bo/AiPlatformBo.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/bo/AiPlatformBo.java new file mode 100644 index 00000000..89d07eee --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/bo/AiPlatformBo.java @@ -0,0 +1,67 @@ +package org.dromara.ai.domain.bo; + +import com.alibaba.excel.annotation.ExcelProperty; +import org.dromara.ai.domain.AiPlatform; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +/** + * AI平台业务对象 ai_platform + * + * @author xins + * @date 2025-08-06 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = AiPlatform.class, reverseConvertGenerate = false) +public class AiPlatformBo extends BaseEntity { + + /** + * 平台ID,主键 + */ + @NotNull(message = "平台ID,主键不能为空", groups = { AddGroup.class, EditGroup.class }) + private Long platformId; + + /** + * 平台编码,如DeepSeek, TongYi-QianWen, Tencent-LKE, WenXin-YiYan + */ + @NotBlank(message = "平台编码不能为空", groups = { AddGroup.class, EditGroup.class }) + private String platformCode; + + /** + * 平台名称,如DeepSeek, 通义千问, 智普AI + */ + @NotBlank(message = "平台名称不能为空", groups = { AddGroup.class, EditGroup.class }) + private String platformName; + + /** + * 平台描述 + */ + @NotBlank(message = "平台描述不能为空", groups = { AddGroup.class, EditGroup.class }) + private String platformDescription; + + /** + * base api url地址 + */ + @NotBlank(message = "base api url地址不能为空", groups = { AddGroup.class, EditGroup.class }) + private String baseApiUrl; + + /** + * 平台图标 + */ + @NotBlank(message = "平台图标不能为空", groups = { AddGroup.class, EditGroup.class }) + private String platformIcon; + + /** + * 授权方法(1,API_KEY,2,OAUTH2),先支持API_KEY + */ + @NotBlank(message = "授权方法(1,API_KEY,2,OAUTH2),先支持API_KEY不能为空", groups = { AddGroup.class, EditGroup.class }) + private String authMethod; + + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/vo/AiBaseModelVo.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/vo/AiBaseModelVo.java new file mode 100644 index 00000000..5d5bb357 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/vo/AiBaseModelVo.java @@ -0,0 +1,95 @@ +package org.dromara.ai.domain.vo; + +import com.baomidou.mybatisplus.annotation.TableField; +import org.dromara.ai.domain.AiApiEndpoint; +import org.dromara.ai.domain.AiBaseModel; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import org.dromara.ai.domain.AiModelCapability; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + + +/** + * 基础模型视图对象 ai_base_model + * + * @author xins + * @date 2025-08-06 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = AiBaseModel.class) +public class AiBaseModelVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long baseModelId; + + /** + * 平台ID,关联ai_platform + */ + @ExcelProperty(value = "平台ID,关联ai_platform") + private Long platformId; + + /** + * 模型类型ID,关联ai_model_type + */ + @ExcelProperty(value = "模型类型ID,关联ai_model_type") + private Long modelTypeId; + + /** + * 模型类型编码,同ai_model_type中的编码 + */ + @ExcelProperty(value = "模型类型编码") + private Long modelTypeCode; + + /** + * 基础模型名称,例如: DeepSeek-V3, Qwen1.5-72B, ChatGLM3 + */ + @ExcelProperty(value = "基础模型名称,例如: DeepSeek-V3, Qwen1.5-72B, ChatGLM3") + private String baseModelName; + + /** + * 基础模型描述 + */ + @ExcelProperty(value = "基础模型描述") + private String baseModelDescription; + + /** + * 上下文长度(如32k) + */ + @ExcelProperty(value = "上下文长度(如32k)") + private Long contextLength; + + /** + * 激活标识(1是,0否) + */ + @ExcelProperty(value = "激活标识(1是,0否)") + private String activeFlag; + + + /** + * 模型能力列表 + */ + @TableField(exist = false) + private List aiModelCapabilityList; + + /** + * api端点列表 + */ + @TableField(exist = false) + private List aiApiEndpointList; + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/vo/AiChatMessageDetailVo.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/vo/AiChatMessageDetailVo.java new file mode 100644 index 00000000..19d28e43 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/vo/AiChatMessageDetailVo.java @@ -0,0 +1,105 @@ +package org.dromara.ai.domain.vo; + +import org.dromara.ai.domain.AiChatMessageDetail; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + + +/** + * 聊天消息详情视图对象 ai_chat_message_detail + * + * @author xins + * @date 2025-08-14 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = AiChatMessageDetail.class) +public class AiChatMessageDetailVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long messageDetailId; + + /** + * 聊天信息ID,关联ai_chat_message + */ + @ExcelProperty(value = "聊天信息ID,关联ai_chat_message") + private Long chatMessageId; + + /** + * 会话ID + */ + @ExcelProperty(value = "会话ID") + private String sessionId; + + /** + * 提问内容 + */ + @ExcelProperty(value = "提问内容") + private String questionContent; + + /** + * 回答内容 + */ + @ExcelProperty(value = "回答内容") + private String answerContent; + + /** + * 提问token数量 + */ + @ExcelProperty(value = "提问token数量") + private Long promptToken; + + /** + * 回复token数量 + */ + @ExcelProperty(value = "回复token数量") + private Long completionToken; + + /** + * 累计 Token数量 + */ + @ExcelProperty(value = "累计 Token数量") + private Long totalToken; + + /** + * 模型ID,关联ai_model + */ + @ExcelProperty(value = "模型ID,关联ai_model") + private Long modelId; + + /** + * 知识库ID,关联ai_knowledge_base + */ + @ExcelProperty(value = "知识库ID,关联ai_knowledge_base") + private Long knowledgeBaseId; + + /** + * 是否携带历史内容(1是,0否) + */ + @ExcelProperty(value = "是否携带历史内容", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "1=是,0否") + private String takeFlag; + + /** + * 完整标识(1是,0否),代表回复信息是否完整回复,中间可以暂停继续。 + */ + @ExcelProperty(value = "完整标识(1是,0否),代表回复信息是否完整回复,中间可以暂停继续。") + private String completeFlag; + + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/vo/AiChatMessageVo.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/vo/AiChatMessageVo.java new file mode 100644 index 00000000..12747a8d --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/vo/AiChatMessageVo.java @@ -0,0 +1,91 @@ +package org.dromara.ai.domain.vo; + +import org.dromara.ai.domain.AiChatMessage; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.common.log.annotation.Log; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 聊天消息视图对象 ai_chat_message + * + * @author xins + * @date 2025-08-14 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = AiChatMessage.class) +public class AiChatMessageVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long chatMessageId; + + /** + * 会话ID + */ + @ExcelProperty(value = "会话ID") + private String sessionId; + + /** + * 聊天标题 + */ + @ExcelProperty(value = "聊天标题") + private String messageTopic; + + /** + * 扣除金额(暂时不用) + */ + @ExcelProperty(value = "扣除金额", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "暂=时不用") + private Long deductCost; + + /** + * 累计 Tokens + */ + @ExcelProperty(value = "累计 Tokens") + private Long totalToken; + + /** + * 模型ID,关联ai_model + */ + @ExcelProperty(value = "模型ID,关联ai_model") + private Long modelId; + + /** + * 知识库ID,关联ai_knowledge_base + */ + @ExcelProperty(value = "知识库ID,关联ai_knowledge_base") + private Long knowledgeBaseId; + + /** + * 聊天类型(1AI问答,2知识库问答,3AI生成SQL,4AI填报) + */ + @ExcelProperty(value = "聊天类型(1AI问答,2知识库问答,3AI生成SQL,4AI填报)") + private String messageType; + + private Date createTime; + + private Date updateTime; + + public Long getCreatedAt() { + return getCreateTime() == null ? 0L : getCreateTime().getTime(); + } + + public Long getUpdatedAt() { + return getUpdateTime() == null ? 0L : getUpdateTime().getTime(); + } +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/vo/AiKnowledgeBaseTypeVo.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/vo/AiKnowledgeBaseTypeVo.java new file mode 100644 index 00000000..42397b3e --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/vo/AiKnowledgeBaseTypeVo.java @@ -0,0 +1,50 @@ +package org.dromara.ai.domain.vo; + +import org.dromara.ai.domain.AiKnowledgeBaseType; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + + +/** + * AI知识库类型视图对象 ai_knowledge_base_type + * + * @author xins + * @date 2025-08-06 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = AiKnowledgeBaseType.class) +public class AiKnowledgeBaseTypeVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long knowledgeBaseTypeId; + + /** + * 知识库类型名称 + */ + @ExcelProperty(value = "知识库类型名称") + private String knowledgeBaseTypeName; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/vo/AiModelTypeVo.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/vo/AiModelTypeVo.java new file mode 100644 index 00000000..78cddbc8 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/vo/AiModelTypeVo.java @@ -0,0 +1,58 @@ +package org.dromara.ai.domain.vo; + +import org.dromara.ai.domain.AiModelType; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + + +/** + * 模型类型视图对象 ai_model_type + * + * @author xins + * @date 2025-08-06 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = AiModelType.class) +public class AiModelTypeVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long modelTypeId; + + /** + * 模型类型编码(LLM、Embedding) + */ + @ExcelProperty(value = "模型类型编码", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "LLM、Embedding") + private String modelTypeCode; + + /** + * 模型类型名称(语言模型、向量模型) + */ + @ExcelProperty(value = "模型类型名称", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "语言模型、向量模型") + private String modelTypeName; + + /** + * 模型类型描述 + */ + @ExcelProperty(value = "模型类型描述") + private String modelTypeDescription; + + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/vo/AiModelVo.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/vo/AiModelVo.java new file mode 100644 index 00000000..93a346c1 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/vo/AiModelVo.java @@ -0,0 +1,147 @@ +package org.dromara.ai.domain.vo; + +import org.dromara.ai.domain.AiModel; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + + +/** + * AI模型视图对象 ai_model + * + * @author xins + * @date 2025-08-07 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = AiModel.class) +public class AiModelVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long modelId; + + /** + * 模型名称 + */ + @ExcelProperty(value = "模型名称") + private String modelName; + + /** + * AI平台ID,关联ai_platform + */ + @ExcelProperty(value = "AI平台ID,关联ai_platform") + private Long platformId; + + /** + * 模型类型ID,关联ai_model_type + */ + @ExcelProperty(value = "模型类型ID,关联ai_model_type") + private Long modelTypeId; + + /** + * 基础模型ID,关联ai_base_model + */ + @ExcelProperty(value = "基础模型ID,关联ai_base_model") + private Long baseModelId; + + /** + * 计费类型(1token计费,2次数计费) + */ + @ExcelProperty(value = "计费类型(1token计费,2次数计费)") + private String chargeType; + + /** + * 模型价格 + */ + @ExcelProperty(value = "模型价格") + private Long modelPrice; + + /** + * 请求地址 + */ + @ExcelProperty(value = "请求地址") + private String apiEndpoint; + + /** + * 秘钥 + */ + @ExcelProperty(value = "秘钥") + private String apiKey; + + /** + * 密钥 + */ + @ExcelProperty(value = "密钥") + private String apiSecret; + + /** + * 向量库(1milvus,2weaviate,) + */ + @ExcelProperty(value = "向量库(1milvus,2weaviate,)") + private String vectorLibrary; + + /** + * 描述 + */ + @ExcelProperty(value = "描述") + private String description; + + /** + * 高级配置—模型温度(0-1);值越大,回复内容越赋有多样性创造性、随机性;设为0根据事实回答,希望得到精准答案应该降低该参数;日常聊天建议0.5-0.8 + */ + @ExcelProperty(value = "高级配置—模型温度(0-1);值越大,回复内容越赋有多样性创造性、随机性;设为0根据事实回答,希望得到精准答案应该降低该参数;日常聊天建议0.5-0.8") + private Long modelTemperature; + + /** + * 高级配置—词汇属性(0-1);值越小,AI生成的内容越单调也越容易理解;值越大,AI回复的词汇范围越大,越多样化 + */ + @ExcelProperty(value = "高级配置—词汇属性(0-1);值越小,AI生成的内容越单调也越容易理解;值越大,AI回复的词汇范围越大,越多样化") + private Long vocabularyAttribute; + + /** + * 高级配置—话题属性(-2.0-2.0);值越大,越能够让AI更好的控制新话题的引入,建议微调或不变 + */ + @ExcelProperty(value = "高级配置—话题属性(-2.0-2.0);值越大,越能够让AI更好的控制新话题的引入,建议微调或不变") + private Long topicAttribute; + + /** + * 高级配置—重复属性(-2.0-2.0);值越大,越能够让AI更好的避免重复之前说过的话,建议微调或不变 + */ + @ExcelProperty(value = "高级配置—重复属性(-2.0-2.0);值越大,越能够让AI更好的避免重复之前说过的话,建议微调或不变") + private Long duplicateAttribute; + + /** + * 高级配置—最大回复(1-16000);设置AI最大回复内容大小,会影响返回结果内容的长度;普通聊天建议500-800;短文生成建议800-2000;代码生成建议2000-3600;长文生成建议4000左右(或选择长回复模型) + */ + @ExcelProperty(value = "高级配置—最大回复(1-16000);设置AI最大回复内容大小,会影响返回结果内容的长度;普通聊天建议500-800;短文生成建议800-2000;代码生成建议2000-3600;长文生成建议4000左右", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "或=选择长回复模型") + private Long maxReply; + + /** + * 默认标识(1是,0否) + */ + @ExcelProperty(value = "默认标识(1是,0否)") + private String defaultFlag; + + /** + * 激活标识(1是,0否) + */ + @ExcelProperty(value = "激活标识(1是,0否)") + private String activeFlag; + + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/vo/AiPlatformVo.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/vo/AiPlatformVo.java new file mode 100644 index 00000000..402ed880 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/domain/vo/AiPlatformVo.java @@ -0,0 +1,75 @@ +package org.dromara.ai.domain.vo; + +import org.dromara.ai.domain.AiPlatform; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + + +/** + * AI平台视图对象 ai_platform + * + * @author xins + * @date 2025-08-06 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = AiPlatform.class) +public class AiPlatformVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 平台ID,主键 + */ + @ExcelProperty(value = "平台ID,主键") + private Long platformId; + + /** + * 平台编码,如DeepSeek, TongYi-QianWen, Tencent-LKE, WenXin-YiYan + */ + @ExcelProperty(value = "平台编码,如DeepSeek, TongYi-QianWen, Tencent-LKE, WenXin-YiYan") + private String platformCode; + + /** + * 平台名称,如DeepSeek, 通义千问, 智普AI + */ + @ExcelProperty(value = "平台名称,如DeepSeek, 通义千问, 智普AI") + private String platformName; + + /** + * 平台描述 + */ + @ExcelProperty(value = "平台描述") + private String platformDescription; + + /** + * base api url地址 + */ + @ExcelProperty(value = "base api url地址") + private String baseApiUrl; + + /** + * 平台图标 + */ + @ExcelProperty(value = "平台图标") + private String platformIcon; + + /** + * 授权方法(1,API_KEY,2,OAUTH2),先支持API_KEY + */ + @ExcelProperty(value = "授权方法", converter = ExcelDictConvert.class) + @ExcelDictFormat(readConverterExp = "1=,API_KEY,2,OAUTH2") + private String authMethod; + + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/mapper/AiBaseModelMapper.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/mapper/AiBaseModelMapper.java new file mode 100644 index 00000000..153dab6b --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/mapper/AiBaseModelMapper.java @@ -0,0 +1,28 @@ +package org.dromara.ai.mapper; + +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import org.dromara.ai.domain.AiBaseModel; +import org.dromara.ai.domain.bo.AiBaseModelBo; +import org.dromara.ai.domain.vo.AiBaseModelVo; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +import java.util.List; + +/** + * 基础模型Mapper接口 + * + * @author xins + * @date 2025-08-06 + */ +@InterceptorIgnore(dataPermission = "true", tenantLine = "true") +public interface AiBaseModelMapper extends BaseMapperPlus { + + /** + * 查询基础模型信息,join apiendpoint和ai_model_capability + * + * @param aiBaseModel 基础模型 + * @return 工序信息集合 + */ + public List selectAiBaseModelJoinList(AiBaseModelBo aiBaseModel); + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/mapper/AiChatMessageDetailMapper.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/mapper/AiChatMessageDetailMapper.java new file mode 100644 index 00000000..9e4118bd --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/mapper/AiChatMessageDetailMapper.java @@ -0,0 +1,15 @@ +package org.dromara.ai.mapper; + +import org.dromara.ai.domain.AiChatMessageDetail; +import org.dromara.ai.domain.vo.AiChatMessageDetailVo; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 聊天消息详情Mapper接口 + * + * @author xins + * @date 2025-08-14 + */ +public interface AiChatMessageDetailMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/mapper/AiChatMessageMapper.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/mapper/AiChatMessageMapper.java new file mode 100644 index 00000000..12d3051d --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/mapper/AiChatMessageMapper.java @@ -0,0 +1,15 @@ +package org.dromara.ai.mapper; + +import org.dromara.ai.domain.AiChatMessage; +import org.dromara.ai.domain.vo.AiChatMessageVo; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 聊天消息Mapper接口 + * + * @author xins + * @date 2025-08-14 + */ +public interface AiChatMessageMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/mapper/AiKnowledgeBaseTypeMapper.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/mapper/AiKnowledgeBaseTypeMapper.java new file mode 100644 index 00000000..8078d69f --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/mapper/AiKnowledgeBaseTypeMapper.java @@ -0,0 +1,15 @@ +package org.dromara.ai.mapper; + +import org.dromara.ai.domain.AiKnowledgeBaseType; +import org.dromara.ai.domain.vo.AiKnowledgeBaseTypeVo; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * AI知识库类型Mapper接口 + * + * @author xins + * @date 2025-08-06 + */ +public interface AiKnowledgeBaseTypeMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/mapper/AiModelMapper.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/mapper/AiModelMapper.java new file mode 100644 index 00000000..3bffcfaa --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/mapper/AiModelMapper.java @@ -0,0 +1,25 @@ +package org.dromara.ai.mapper; + +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.github.yulichang.wrapper.MPJLambdaWrapper; +import org.apache.ibatis.annotations.Param; +import org.dromara.ai.domain.AiBaseModel; +import org.dromara.ai.domain.AiModel; +import org.dromara.ai.domain.bo.AiBaseModelBo; +import org.dromara.ai.domain.vo.AiModelVo; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +import java.util.List; + +/** + * AI模型Mapper接口 + * + * @author xins + * @date 2025-08-07 + */ +public interface AiModelMapper extends BaseMapperPlus { + + @InterceptorIgnore(tenantLine = "true") // leftJoin 的表不拼接租户 + List selectJoinList(@Param(Constants.WRAPPER) MPJLambdaWrapper wrapper); +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/mapper/AiModelTypeMapper.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/mapper/AiModelTypeMapper.java new file mode 100644 index 00000000..aa6188be --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/mapper/AiModelTypeMapper.java @@ -0,0 +1,17 @@ +package org.dromara.ai.mapper; + +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import org.dromara.ai.domain.AiModelType; +import org.dromara.ai.domain.vo.AiModelTypeVo; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 模型类型Mapper接口 + * + * @author xins + * @date 2025-08-06 + */ +@InterceptorIgnore(dataPermission = "true", tenantLine = "true") +public interface AiModelTypeMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/mapper/AiPlatformMapper.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/mapper/AiPlatformMapper.java new file mode 100644 index 00000000..d80ace64 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/mapper/AiPlatformMapper.java @@ -0,0 +1,17 @@ +package org.dromara.ai.mapper; + +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import org.dromara.ai.domain.AiPlatform; +import org.dromara.ai.domain.vo.AiPlatformVo; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * AI平台Mapper接口 + * + * @author xins + * @date 2025-08-06 + */ +@InterceptorIgnore(dataPermission = "true", tenantLine = "true") +public interface AiPlatformMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/mapper/DatabaseMetaMapper.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/mapper/DatabaseMetaMapper.java new file mode 100644 index 00000000..3181c3d9 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/mapper/DatabaseMetaMapper.java @@ -0,0 +1,44 @@ +package org.dromara.ai.mapper; + +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Select; + +import java.util.List; +import java.util.Map; + +/** + * @Author xins + * @Date 2025/7/8 9:22 + * @Description: + */ +@InterceptorIgnore(dataPermission = "true", tenantLine = "true") +@Mapper +public interface DatabaseMetaMapper { + + /** + * 获取所有用户表名 + */ + @Select("SELECT name FROM hwmom.sys.tables where name = 'sys_user'ORDER BY name") + List getAllTableNames(); + + /** + * 获取指定表的结构信息 + */ + List> getTableStructure(@Param("tableName") String tableName); + + /** + * 获取所有表的结构信息 + */ + List> getAllTablesStructure(); + + /** + * 获取表的主键信息 + */ + @Select("SELECT COL_NAME(ic.object_id, ic.column_id) AS columnName " + + "FROM sys.indexes i " + + "INNER JOIN sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id " + + "WHERE i.is_primary_key = 1 AND OBJECT_NAME(ic.object_id) = #{tableName}") + List getPrimaryKeys(@Param("tableName") String tableName); +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/dto/AIMessage.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/dto/AIMessage.java new file mode 100644 index 00000000..ca056cda --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/dto/AIMessage.java @@ -0,0 +1,29 @@ +package org.dromara.ai.process.dto; + +/** + * @description AI message dto + * @author xins + * @date 2025/7/29 14:44 + */ + +import lombok.Data; + +@Data +public class AIMessage { + /** + * 角色:system/user/assistant + */ + private String role; + + /** + * 消息内容 + */ + private String content; + + + /** + * 时间戳 + */ + private Long timestamp; +} + diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/dto/AIRequest.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/dto/AIRequest.java new file mode 100644 index 00000000..f40eda0c --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/dto/AIRequest.java @@ -0,0 +1,98 @@ +package org.dromara.ai.process.dto; + +/** + * @description AI request dto 聊天请求参数 + * @author xins + * @date 2025/7/29 14:44 + */ + +import lombok.Data; +import org.dromara.ai.process.enums.AIProviderEnum; + +import java.util.List; + +@Data +public class AIRequest { + /** + * 模型提供商 + */ + private AIProviderEnum aiModelEnum; + + /** + * 模型名称/ID(例如deepseek-chat、tongyi-qianwen、tencent-lke、wenxin-yiyan) + */ + private String model; + + /** + * AI模型ID + */ + private Long modelId; + + /** + * 会话ID + */ + private String sessionId; + + /** + * 模型类型(LLM、Embedding) + */ + private String modelType; + + /** + * 是否携带历史记录(1是,0否) + */ + private String carryHistoryFlag; + + + /** + * 对话历史记录 + */ + private List messages; + + /** + * 温度参数,控制随机性 + */ + private Double temperature; + + /** + * 最大生成长度 + */ + private Integer maxTokens; + + /** + * API密钥 + */ + private String apiKey; + + /** + * 是否加密标识(从前端传的数据是否加密) + */ + private String apiKeyEncryptFlag; + + /** + * API密钥 + */ + private String apiSecret; + + /** + * 是否加密标识(从前端传的数据是否加密) + */ + private String apiSecretEncryptFlag; + + /** + * 需转化向量的文字 + */ + private String text; + + /** + * 需转化向量的文字 + */ + private String[] texts; + + + + /** + * 其他自定义参数 + */ + private Object customParams; +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/dto/AIResponse.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/dto/AIResponse.java new file mode 100644 index 00000000..a2c01649 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/dto/AIResponse.java @@ -0,0 +1,74 @@ +package org.dromara.ai.process.dto; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Data; +import lombok.RequiredArgsConstructor; + +import java.util.List; + +/** + * @description AI聊天回复响应 + * @author xins + * @date 2025/7/29 16:20 + */ +@Data +@RequiredArgsConstructor +public class AIResponse { + /** + * 是否成功 + */ + private boolean success; + + /** + * 错误信息 + */ + private String errorMessage; + + /** + * 生成的回复内容 + */ + private String content; + + /** + * 完整的响应数据 + */ + private Object rawResponse; + + /** + * 使用token情况 + */ + private Usage usage; + + public AIResponse(boolean success, String errorMessage, String content, Usage usage) { + this.success = success; + this.errorMessage = errorMessage; + this.content = content; + this.usage = usage; + } + + // 成功响应的便捷构造函数 + public AIResponse(String content, Usage usage) { + this(true, null, content, usage); + } + + // 错误响应的便捷构造函数 + public AIResponse(String errorMessage) { + this(false, errorMessage, null, null); + } + + /** + * Token使用情况类 + */ + @Data + public static class Usage { + private int promptTokens; + private int completionTokens; + private int totalTokens; + + public Usage(int promptTokens, int completionTokens, int totalTokens) { + this.promptTokens = promptTokens; + this.completionTokens = completionTokens; + this.totalTokens = totalTokens; + } + } +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/enums/AIChatMessageTypeEnum.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/enums/AIChatMessageTypeEnum.java new file mode 100644 index 00000000..6a778e7c --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/enums/AIChatMessageTypeEnum.java @@ -0,0 +1,36 @@ +package org.dromara.ai.process.enums; + +public enum AIChatMessageTypeEnum { + AI_CHAT("1","ai-chat"), //AI问答 + KNOWLEDGE_CHAT("2","knowledge-chat");//知识库问答 + + + private final String code; + private final String name; + + AIChatMessageTypeEnum(String code,String name) { + this.code = code; + this.name = name; + } + + public String getCode() { + return code; + } + + public String getName() { + return name; + } + + /** + * 从字符串转换为枚举 + */ + public static AIChatMessageTypeEnum fromString(String text) { + for (AIChatMessageTypeEnum provider : AIChatMessageTypeEnum.values()) { + if (provider.name().equalsIgnoreCase(text) || + provider.getName().equalsIgnoreCase(text)) { + return provider; + } + } + throw new IllegalArgumentException("未知的大模型提供商: " + text); + } +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/enums/AIModelTypeEnum.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/enums/AIModelTypeEnum.java new file mode 100644 index 00000000..7c5b85c0 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/enums/AIModelTypeEnum.java @@ -0,0 +1,29 @@ +package org.dromara.ai.process.enums; + +public enum AIModelTypeEnum { + LLM("LLM"),//大语言对话模型 + EMBEDDING("Embedding");//向量模型 + + private final String name; + + AIModelTypeEnum(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + /** + * 从字符串转换为枚举 + */ + public static AIModelTypeEnum fromString(String text) { + for (AIModelTypeEnum provider : AIModelTypeEnum.values()) { + if (provider.name().equalsIgnoreCase(text) || + provider.getName().equalsIgnoreCase(text)) { + return provider; + } + } + throw new IllegalArgumentException("未知的大模型提供商: " + text); + } +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/enums/AIProviderEnum.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/enums/AIProviderEnum.java new file mode 100644 index 00000000..0dee18e9 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/enums/AIProviderEnum.java @@ -0,0 +1,31 @@ +package org.dromara.ai.process.enums; + +public enum AIProviderEnum { + DEEPSEEK("DeepSeek"), + TONGYI_QIANWEN("TongYi-QianWen"),//阿里通义千问 + TENCENT_LKE("Tencent-LKE"),//腾讯云智能体开发平台 + WENXIN_YIYAN("WenXin-YiYan");//百度文心一言 + + private final String name; + + AIProviderEnum(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + /** + * 从字符串转换为枚举 + */ + public static AIProviderEnum fromString(String text) { + for (AIProviderEnum provider : AIProviderEnum.values()) { + if (provider.name().equalsIgnoreCase(text) || + provider.getName().equalsIgnoreCase(text)) { + return provider; + } + } + throw new IllegalArgumentException("未知的大模型提供商: " + text); + } +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/provider/processor/AIProviderProcessorFactory.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/provider/processor/AIProviderProcessorFactory.java new file mode 100644 index 00000000..b03f9d2c --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/provider/processor/AIProviderProcessorFactory.java @@ -0,0 +1,41 @@ +package org.dromara.ai.process.provider.processor; + +import org.dromara.ai.process.enums.AIProviderEnum; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @Author xins + * @Date 2025/7/21 9:26 + * @Description:AI提供商处理器工厂 + */ +@Component +public class AIProviderProcessorFactory { + + private final Map processorMap; + + @Autowired + public AIProviderProcessorFactory(List processors) { + this.processorMap = processors.stream() + .collect(Collectors.toMap(IUnifiedAIProviderProcessor::supportedProvider, Function.identity())); + } + + public IUnifiedAIProviderProcessor getProcessor(AIProviderEnum provider) { + return Optional.ofNullable(processorMap.get(provider)) + .orElseThrow(() -> new IllegalArgumentException("不支持的AI大模型提供商:" + provider)); + } + + /** + * 根据provider名称获取对应的API实现 + */ + public IUnifiedAIProviderProcessor getProcessor(String providerName) { + AIProviderEnum provider = AIProviderEnum.fromString(providerName); + return getProcessor(provider); + } +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/provider/processor/IUnifiedAIProviderProcessor.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/provider/processor/IUnifiedAIProviderProcessor.java new file mode 100644 index 00000000..ecefa8da --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/provider/processor/IUnifiedAIProviderProcessor.java @@ -0,0 +1,60 @@ +package org.dromara.ai.process.provider.processor; + + +import org.dromara.ai.process.dto.AIRequest; +import org.dromara.ai.process.dto.AIResponse; +import org.dromara.ai.process.enums.AIProviderEnum; +import org.dromara.system.api.model.LoginUser; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.util.List; + +/** + * 统一大模型处理接口 + */ +public interface IUnifiedAIProviderProcessor { + + public Mono chatTest(AIRequest request); + + /** + * 发送聊天请求(非流式) + * @param request 聊天请求参数 + * @return Mono包装的聊天响应 + */ + Mono chat(AIRequest request); + + /** + * 发送聊天请求(流式) + * @param request 聊天请求参数 + * @return 流式内容Flux + */ + Flux chatStream(AIRequest request, LoginUser loginUser); + + /** + * 支持的提供商 + * @return 提供商枚举 + */ + AIProviderEnum supportedProvider(); + + + public List getEmbeddingTest(AIRequest aiRequest); + + + /** + * 获取单个文本的向量表示 + * @param text 输入文本 + * @return 向量表示列表(Double类型) + * @throws RuntimeException 如果向量化过程中发生错误 + */ + public List getEmbedding(AIRequest aiRequest); + + /** + * 获取多个文本的向量表示 + * @param texts 输入文本数组 + * @return 向量表示列表的列表(每个文本对应一个Double列表) + * @throws RuntimeException 如果向量化过程中发生错误 + */ + public List> getEmbeddings(AIRequest aiRequest); + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/provider/processor/impl/BaseAIProviderProcessor.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/provider/processor/impl/BaseAIProviderProcessor.java new file mode 100644 index 00000000..f1c538e4 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/provider/processor/impl/BaseAIProviderProcessor.java @@ -0,0 +1,172 @@ +package org.dromara.ai.process.provider.processor.impl; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.dromara.ai.process.dto.AIResponse; +import org.dromara.ai.process.provider.processor.IUnifiedAIProviderProcessor; +import org.dromara.ai.test.ChatRequest; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; +import reactor.util.retry.Retry; + +import java.time.Duration; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @Author xins + * @Date 2025/7/29 15:54 + * @Description:大模型API基础抽象类 + */ +public abstract class BaseAIProviderProcessor implements IUnifiedAIProviderProcessor { + + protected final ObjectMapper objectMapper; + protected final WebClient webClient; + + // 用于解析流式JSON块的模式 + private static final Pattern JSON_PATTERN = Pattern.compile("\\{(?:[^{}]|\\{(?:[^{}]|\\{[^{}]*\\})*\\})*\\}"); + + public BaseAIProviderProcessor() { + this.objectMapper = new ObjectMapper(); + this.webClient = WebClient.builder() + .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(16 * 1024 * 1024)) + .build(); + } + + /** + * 构建错误响应 + */ + protected Mono buildErrorResponse(String errorMessage) { + AIResponse aiResponse = new AIResponse(); + aiResponse.setSuccess(false); + aiResponse.setErrorMessage(errorMessage); + return Mono.just(aiResponse); + } + + /** + * 处理流式响应的JSON块 + */ + protected String parseStreamChunk(String jsonChunk) { + try { + return extractContentFromStreamJson(jsonChunk); + } catch (Exception e) { + return null; + } + } + + /** + * 从流式JSON中提取内容(由子类实现) + */ + protected abstract String extractContentFromStreamJson(String jsonChunk) throws Exception; + + /** + * 从普通响应JSON中提取内容(由子类实现) + */ + protected abstract AIResponse extractAIResponse(String json) throws Exception; + + /** + * 处理SSE流中的JSON块 + */ + protected Flux extractJsonChunks(String rawChunk) { + // 简单实现,假设每个事件都是完整的JSON +// return Flux.just(data); + System.out.println("RawChunk: " + rawChunk); + // 从原始块中提取完整的JSON对象 + Matcher matcher = JSON_PATTERN.matcher(rawChunk); + Flux chunks = Flux.empty(); + while (matcher.find()) { + chunks = chunks.concatWithValues(matcher.group()); + } + return chunks; + } + + + + /** + * 普通http请求(非流式请求),直接返回完整回复 + * @param url + * @param requestBody + * @param apiKey + * @return AIResponse + */ + public Mono standardRequest(String url,String requestBody, String apiKey) { + return webClient.post() + .uri(url) + .header(HttpHeaders.AUTHORIZATION, "Bearer " + apiKey) + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .bodyValue(requestBody) + .retrieve() + .bodyToMono(String.class) + .flatMap(response -> { + try { + return Mono.just(extractAIResponse(response)); + } catch (Exception e) { + return buildErrorResponse("解析响应失败: " + e.getMessage()); + } + }) + .onErrorResume(e -> buildErrorResponse("API调用失败: " + e.getMessage())); +// .timeout(Duration.ofSeconds(30)); + } + + + /** + * 执行流式HTTP请求 + */ + protected Flux executeStreamRequest(String url, String requestBody, String apiKey) { +// String prompt = "你好"; +// requestBody = String.format( +// "{\"model\":\"deepseek-chat\",\"stream\":true,\"messages\":[{\"role\":\"user\",\"content\":\"%s\"}]}", +// prompt); + return webClient.post() + .uri(url) + .header(HttpHeaders.AUTHORIZATION, "Bearer " + apiKey) + .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .header(HttpHeaders.ACCEPT, MediaType.TEXT_EVENT_STREAM_VALUE) + .bodyValue(requestBody) + .retrieve() + .bodyToFlux(String.class) + .timeout(Duration.ofSeconds(60)) + .retryWhen(Retry.backoff(3, Duration.ofSeconds(1))) // 重试逻辑 + .flatMap(this::extractJsonChunks) + .filter(chunk -> chunk != null && !chunk.isBlank()) + .filter(chunk -> !chunk.equals("[DONE]")) + .filter(chunk -> chunk.startsWith("{") && chunk.endsWith("}")) + .map(this::parseStreamChunk) + .filter(content -> content != null && !content.isEmpty()) + + .onErrorResume(e -> Flux.error(new RuntimeException("流式请求失败: " + e.getMessage()))); + } + +// /** +// * 流式回复 +// * @param request +// * @return +// */ +// @Override +// public Flux streamChatCompletion(ChatRequest request) { +//// request.setStream(true); +// +// String requestBody = String.format( +// "{\"model\":\"deepseek-chat\",\"stream\":true,\"messages\":[{\"role\":\"user\",\"content\":\"%s\"}]}", +// escapeJson(request.getPrompt())); +// return webClient.post() +// .uri("/chat/completions") +// .header(HttpHeaders.AUTHORIZATION, "Bearer " + apiKey) +// .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) +// .header(HttpHeaders.ACCEPT, MediaType.TEXT_EVENT_STREAM_VALUE) +// .bodyValue(requestBody) +// .retrieve() +// .bodyToFlux(String.class) +// .flatMap(this::extractJsonChunks) +// .filter(chunk -> chunk != null && !chunk.isBlank()) +// .filter(chunk -> !chunk.equals("[DONE]")) +// .filter(chunk -> chunk.startsWith("{") && chunk.endsWith("}")) // 确保完整JSON对象 +// .map(this::parseJsonChunk) +// .filter(content -> content != null && !content.isEmpty()); +// } + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/provider/processor/impl/DeepSeekProcessor.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/provider/processor/impl/DeepSeekProcessor.java new file mode 100644 index 00000000..6aaaea35 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/process/provider/processor/impl/DeepSeekProcessor.java @@ -0,0 +1,280 @@ +package org.dromara.ai.process.provider.processor.impl; + +//import com.example.deepseek.dto.EmbeddingResponse; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.dromara.ai.domain.AiChatMessage; +import org.dromara.ai.domain.AiChatMessageDetail; +import org.dromara.ai.domain.vo.AiModelVo; +import org.dromara.ai.mapper.AiChatMessageDetailMapper; +import org.dromara.ai.mapper.AiChatMessageMapper; +import org.dromara.ai.mapper.AiModelMapper; +import org.dromara.ai.process.dto.AIMessage; +import org.dromara.ai.process.dto.AIRequest; +import org.dromara.ai.process.dto.AIResponse; +import org.dromara.ai.process.enums.AIChatMessageTypeEnum; +import org.dromara.ai.process.enums.AIProviderEnum; +import org.dromara.ai.service.IAiChatMessageService; +import org.dromara.common.encrypt.utils.EncryptUtils; +import org.dromara.system.api.model.LoginUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @Author xins + * @Date 2025/7/18 17:12 + * @Description:DeepSeek深度求索大模型处理类 + */ + +@Component +public class DeepSeekProcessor extends BaseAIProviderProcessor { + + // 用于解析流式JSON块的模式 + private static final Pattern JSON_PATTERN = Pattern.compile("\\{(?:[^{}]|\\{(?:[^{}]|\\{[^{}]*\\})*\\})*\\}"); + + // private final String apiKey = "sk-e1df7a607644479e8ebad3be233ddafa"; + private final String apiUrl = "https://api.deepseek.com/v1/"; + + private static final String API_URL = "https://api.deepseek.com/v1/chat/completions"; + private final String deepSeekChatModel = "deepseek-chat"; + + @Autowired + private AiModelMapper aiModelMapper; + + @Autowired + private AiChatMessageMapper aiChatMessageMapper; + @Autowired + private AiChatMessageDetailMapper aiChatMessageDetailMapper; + + + public Mono chatTest(AIRequest request) { + AIMessage aiMessage = new AIMessage(); + aiMessage.setRole("user"); + aiMessage.setContent("这是一个测试请求,请回复'测试成功'"); + request.setMessages(Collections.singletonList( + aiMessage + )); + String apiEncryptFlag = request.getApiKeyEncryptFlag(); + if (apiEncryptFlag.equals("1")) { + request.setApiKey(EncryptUtils.decryptByBase64(request.getApiKey())); + } + + return chat(request); + } + + + @Override + public Mono chat(AIRequest request) { + try { + ObjectNode rootNode = objectMapper.createObjectNode(); + rootNode.put("model", deepSeekChatModel); + rootNode.set("messages", objectMapper.valueToTree(request.getMessages())); + + if (request.getTemperature() != null) { + rootNode.put("temperature", request.getTemperature()); + } + if (request.getMaxTokens() != null) { + rootNode.put("max_tokens", request.getMaxTokens()); + } + + String requestBody = objectMapper.writeValueAsString(rootNode); + return standardRequest(API_URL, requestBody, request.getApiKey()); + } catch (IOException e) { + return buildErrorResponse("构建请求失败: " + e.getMessage()); + } + } + + @Override + public Flux chatStream(AIRequest request, LoginUser loginUser) { + try { + ObjectNode rootNode = objectMapper.createObjectNode(); + rootNode.put("model", deepSeekChatModel); + rootNode.set("messages", objectMapper.valueToTree(request.getMessages())); + rootNode.put("stream", true); + + if (request.getTemperature() != null) { + rootNode.put("temperature", request.getTemperature()); + } + +// request.setApiKey(); + // 执行流式请求并收集完整响应 + StringBuilder fullResponseBuilder = new StringBuilder(); + String requestBody = objectMapper.writeValueAsString(rootNode); + + setApiKey(request); + + return executeStreamRequest(API_URL, requestBody, request.getApiKey()).doOnNext(chunk -> { + // 收集每个chunk + fullResponseBuilder.append(chunk); + }) + .doOnComplete(() -> { + // 流完成后保存到数据库 + saveChatMessage(request, fullResponseBuilder.toString(), loginUser); + }) + .doOnError(error -> { + // 错误处理 +// log.error("流式请求出错", error); + }); + } catch (IOException e) { + return Flux.error(new RuntimeException("构建请求失败: " + e.getMessage())); + } + } + + @Override + protected String extractContentFromStreamJson(String jsonChunk) throws Exception { + try { + // 使用更宽松的解析方式(内容中也有包含{}等特殊字符的) + JsonNode node = new ObjectMapper() + .configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true) + .configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true) + .readTree(jsonChunk); + + // 尝试多种方式获取content + String content = node.path("delta").path("content").asText(); + if (content.isEmpty()) { + JsonNode choices = node.path("choices"); + if (choices.isArray() && choices.size() > 0) { + content = choices.get(0).path("delta").path("content").asText(); + } + } + + // 2. 提取 Token 计数(如果有) + JsonNode usage = node.path("usage"); + if (!usage.isMissingNode()) { + int promptTokens = usage.path("prompt_tokens").asInt(); + int completionTokens = usage.path("completion_tokens").asInt(); + int totalTokens = usage.path("total_tokens").asInt(); + System.out.printf("[Token Usage] Prompt: %d, Completion: %d, Total: %d%n", + promptTokens, completionTokens, totalTokens); + } + + return content; + } catch (Exception e) { + // 尝试提取content的原始文本(最后手段) + return extractContentRaw(jsonChunk); + } + } + + @Override + protected AIResponse extractAIResponse(String json) throws Exception { + JsonNode node = objectMapper.readTree(json); + JsonNode choices = node.path("choices"); + String content = ""; + if (choices.isArray() && choices.size() > 0) { + content = choices.get(0).path("message").path("content").asText(); + } + + JsonNode usageNode = node.path("usage"); + AIResponse.Usage usage = new AIResponse.Usage( + usageNode.path("prompt_tokens").asInt(), + usageNode.path("completion_tokens").asInt(), + usageNode.path("total_tokens").asInt() + ); + + return new AIResponse(content, usage); + } + + @Override + public AIProviderEnum supportedProvider() { + return AIProviderEnum.DEEPSEEK; + } + + @Override + public List getEmbeddingTest(AIRequest aiRequest) { + return List.of(); + } + + @Override + public List getEmbedding(AIRequest aiRequest) { + return List.of(); + } + + @Override + public List> getEmbeddings(AIRequest aiRequest) { + return List.of(); + } + + + private String extractContentRaw(String jsonStr) { + // 简单的正则提取,作为最后手段 + Pattern pattern = Pattern.compile("\"content\":\"(.*?)(?() + .eq(AiChatMessage::getSessionId, sessionId)); + List messages = request.getMessages(); + if (aiChatMessage == null) { + aiChatMessage = new AiChatMessage(); + aiChatMessage.setSessionId(request.getSessionId()); + aiChatMessage.setMessageTopic(messages.get(messages.size() - 1).getContent()); + aiChatMessage.setMessageType(AIChatMessageTypeEnum.AI_CHAT.getCode()); + aiChatMessage.setModelId(request.getModelId()); +// aiChatMessage.setTotalToken(); + aiChatMessage.setTenantId(loginUser.getTenantId()); + aiChatMessage.setCreateBy(loginUser.getUserId()); + aiChatMessage.setCreateDept(loginUser.getDeptId()); + + aiChatMessageMapper.insert(aiChatMessage); + } else { + + } + + AiChatMessageDetail aiChatMessageDetail = new AiChatMessageDetail(); + aiChatMessageDetail.setChatMessageId(aiChatMessage.getChatMessageId()); + aiChatMessageDetail.setSessionId(request.getSessionId()); + aiChatMessageDetail.setQuestionContent(messages.get(messages.size() - 1).getContent()); + aiChatMessageDetail.setAnswerContent(fullResponse); +// aiChatMessageDetail.setPromptToken(1L); +// aiChatMessageDetail.setCompletionToken(1L); +// aiChatMessageDetail.setTotalToken(1L); + aiChatMessageDetail.setModelId(request.getModelId()); +// aiChatMessageDetail.setKnowledgeBaseId(1L); + aiChatMessageDetail.setTakeFlag(request.getCarryHistoryFlag()); + aiChatMessageDetail.setCompleteFlag("1"); + aiChatMessageDetail.setTenantId(loginUser.getTenantId()); + aiChatMessageDetail.setCreateBy(loginUser.getUserId()); + aiChatMessageDetail.setCreateDept(loginUser.getDeptId()); + + aiChatMessageDetailMapper.insert(aiChatMessageDetail); + +// log.info("聊天记录已保存,ID: {}", record.getId()); + + } + + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/IAIAssistantService.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/IAIAssistantService.java new file mode 100644 index 00000000..090a797f --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/IAIAssistantService.java @@ -0,0 +1,8 @@ +package org.dromara.ai.service; + +import org.dromara.ai.process.dto.AIRequest; + +public interface IAIAssistantService { + + public boolean testAIModel(String provider, AIRequest aiRequest); +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/IAiBaseModelService.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/IAiBaseModelService.java new file mode 100644 index 00000000..86419d02 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/IAiBaseModelService.java @@ -0,0 +1,78 @@ +package org.dromara.ai.service; + +import org.dromara.ai.domain.AiBaseModel; +import org.dromara.ai.domain.vo.AiBaseModelVo; +import org.dromara.ai.domain.bo.AiBaseModelBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 基础模型Service接口 + * + * @author xins + * @date 2025-08-06 + */ +public interface IAiBaseModelService { + + /** + * 查询基础模型 + * + * @param baseModelId 主键 + * @return 基础模型 + */ + AiBaseModelVo queryById(Long baseModelId); + + /** + * 分页查询基础模型列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 基础模型分页列表 + */ + TableDataInfo queryPageList(AiBaseModelBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的基础模型列表 + * + * @param bo 查询条件 + * @return 基础模型列表 + */ + List queryList(AiBaseModelBo bo); + + /** + * 新增基础模型 + * + * @param bo 基础模型 + * @return 是否新增成功 + */ + Boolean insertByBo(AiBaseModelBo bo); + + /** + * 修改基础模型 + * + * @param bo 基础模型 + * @return 是否修改成功 + */ + Boolean updateByBo(AiBaseModelBo bo); + + /** + * 校验并批量删除基础模型信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + + /** + * 查询符合条件的基础模型列表 + * + * @param queryBaseModel 查询条件 + * @return 基础模型列表 + */ + public List queryJoinList(AiBaseModelBo queryBaseModel); +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/IAiChatMessageDetailService.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/IAiChatMessageDetailService.java new file mode 100644 index 00000000..ca0bc660 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/IAiChatMessageDetailService.java @@ -0,0 +1,77 @@ +package org.dromara.ai.service; + +import org.dromara.ai.domain.AiChatMessageDetail; +import org.dromara.ai.domain.vo.AiChatMessageDetailVo; +import org.dromara.ai.domain.bo.AiChatMessageDetailBo; +import org.dromara.ai.process.dto.AIMessage; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 聊天消息详情Service接口 + * + * @author xins + * @date 2025-08-14 + */ +public interface IAiChatMessageDetailService { + + /** + * 查询聊天消息详情 + * + * @param messageDetailId 主键 + * @return 聊天消息详情 + */ + AiChatMessageDetailVo queryById(Long messageDetailId); + + /** + * 分页查询聊天消息详情列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 聊天消息详情分页列表 + */ + TableDataInfo queryPageList(AiChatMessageDetailBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的聊天消息详情列表 + * + * @param bo 查询条件 + * @return 聊天消息详情列表 + */ + List queryList(AiChatMessageDetailBo bo); + + /** + * 新增聊天消息详情 + * + * @param bo 聊天消息详情 + * @return 是否新增成功 + */ + Boolean insertByBo(AiChatMessageDetailBo bo); + + /** + * 修改聊天消息详情 + * + * @param bo 聊天消息详情 + * @return 是否修改成功 + */ + Boolean updateByBo(AiChatMessageDetailBo bo); + + /** + * 校验并批量删除聊天消息详情信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 根据sessionId查询聊天消息详情列表 + * @param sessionId + * @return LIST + */ + public List getAIChatMessages(String sessionId); +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/IAiChatMessageService.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/IAiChatMessageService.java new file mode 100644 index 00000000..8d5f1bec --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/IAiChatMessageService.java @@ -0,0 +1,91 @@ +package org.dromara.ai.service; + +import org.dromara.ai.domain.AiChatMessage; +import org.dromara.ai.domain.bo.AiChatMessageTopicBo; +import org.dromara.ai.domain.vo.AiChatMessageVo; +import org.dromara.ai.domain.bo.AiChatMessageBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Collection; +import java.util.List; + +/** + * 聊天消息Service接口 + * + * @author xins + * @date 2025-08-14 + */ +public interface IAiChatMessageService { + + /** + * 查询聊天消息 + * + * @param chatMessageId 主键 + * @return 聊天消息 + */ + AiChatMessageVo queryById(Long chatMessageId); + + /** + * 分页查询聊天消息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 聊天消息分页列表 + */ + TableDataInfo queryPageList(AiChatMessageBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的聊天消息列表 + * + * @param bo 查询条件 + * @return 聊天消息列表 + */ + List queryList(AiChatMessageBo bo); + + /** + * 新增聊天消息 + * + * @param bo 聊天消息 + * @return 是否新增成功 + */ + Boolean insertByBo(AiChatMessageBo bo); + + /** + * 修改聊天消息 + * + * @param bo 聊天消息 + * @return 是否修改成功 + */ + Boolean updateByBo(AiChatMessageBo bo); + + /** + * 校验并批量删除聊天消息信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + + /** + * 修改聊天消息标题 + * + * @param bo 聊天消息 + * @return 是否修改成功 + */ + public Boolean updateMessageTopic(AiChatMessageTopicBo bo); + + + /** + * 校验并批量删除聊天消息信息 + * + * @param sessionId 待删除的会话ID + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Transactional(rollbackFor = Exception.class) + public Boolean deleteWithValidBySessionId(String sessionId, Boolean isValid); +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/IAiKnowledgeBaseTypeService.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/IAiKnowledgeBaseTypeService.java new file mode 100644 index 00000000..26e28f66 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/IAiKnowledgeBaseTypeService.java @@ -0,0 +1,69 @@ +package org.dromara.ai.service; + +import org.dromara.ai.domain.AiKnowledgeBaseType; +import org.dromara.ai.domain.vo.AiKnowledgeBaseTypeVo; +import org.dromara.ai.domain.bo.AiKnowledgeBaseTypeBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * AI知识库类型Service接口 + * + * @author xins + * @date 2025-08-06 + */ +public interface IAiKnowledgeBaseTypeService { + + /** + * 查询AI知识库类型 + * + * @param knowledgeBaseTypeId 主键 + * @return AI知识库类型 + */ + AiKnowledgeBaseTypeVo queryById(Long knowledgeBaseTypeId); + + /** + * 分页查询AI知识库类型列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return AI知识库类型分页列表 + */ + TableDataInfo queryPageList(AiKnowledgeBaseTypeBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的AI知识库类型列表 + * + * @param bo 查询条件 + * @return AI知识库类型列表 + */ + List queryList(AiKnowledgeBaseTypeBo bo); + + /** + * 新增AI知识库类型 + * + * @param bo AI知识库类型 + * @return 是否新增成功 + */ + Boolean insertByBo(AiKnowledgeBaseTypeBo bo); + + /** + * 修改AI知识库类型 + * + * @param bo AI知识库类型 + * @return 是否修改成功 + */ + Boolean updateByBo(AiKnowledgeBaseTypeBo bo); + + /** + * 校验并批量删除AI知识库类型信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/IAiModelService.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/IAiModelService.java new file mode 100644 index 00000000..6bfce9e8 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/IAiModelService.java @@ -0,0 +1,77 @@ +package org.dromara.ai.service; + +import org.dromara.ai.domain.AiModel; +import org.dromara.ai.domain.vo.AiModelVo; +import org.dromara.ai.domain.bo.AiModelBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * AI模型Service接口 + * + * @author xins + * @date 2025-08-07 + */ +public interface IAiModelService { + + /** + * 查询AI模型 + * + * @param modelId 主键 + * @return AI模型 + */ + AiModelVo queryById(Long modelId); + + /** + * 分页查询AI模型列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return AI模型分页列表 + */ + TableDataInfo queryPageList(AiModelBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的AI模型列表 + * + * @param bo 查询条件 + * @return AI模型列表 + */ + List queryList(AiModelBo bo); + + /** + * 查询符合条件的AI模型列表,Join basemodel,modeltype,platform + * + * @param bo 查询条件 + * @return AI模型列表 + */ + public List queryJoinList(AiModelBo bo); + + /** + * 新增AI模型 + * + * @param bo AI模型 + * @return 是否新增成功 + */ + Boolean insertByBo(AiModelBo bo); + + /** + * 修改AI模型 + * + * @param bo AI模型 + * @return 是否修改成功 + */ + Boolean updateByBo(AiModelBo bo); + + /** + * 校验并批量删除AI模型信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/IAiPlatformService.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/IAiPlatformService.java new file mode 100644 index 00000000..ce6d736f --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/IAiPlatformService.java @@ -0,0 +1,69 @@ +package org.dromara.ai.service; + +import org.dromara.ai.domain.AiPlatform; +import org.dromara.ai.domain.vo.AiPlatformVo; +import org.dromara.ai.domain.bo.AiPlatformBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * AI平台Service接口 + * + * @author xins + * @date 2025-08-06 + */ +public interface IAiPlatformService { + + /** + * 查询AI平台 + * + * @param platformId 主键 + * @return AI平台 + */ + AiPlatformVo queryById(Long platformId); + + /** + * 分页查询AI平台列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return AI平台分页列表 + */ + TableDataInfo queryPageList(AiPlatformBo bo, PageQuery pageQuery); + + /** + * 查询符合条件的AI平台列表 + * + * @param bo 查询条件 + * @return AI平台列表 + */ + List queryList(AiPlatformBo bo); + + /** + * 新增AI平台 + * + * @param bo AI平台 + * @return 是否新增成功 + */ + Boolean insertByBo(AiPlatformBo bo); + + /** + * 修改AI平台 + * + * @param bo AI平台 + * @return 是否修改成功 + */ + Boolean updateByBo(AiPlatformBo bo); + + /** + * 校验并批量删除AI平台信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/impl/AIAssistantServiceImpl.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/impl/AIAssistantServiceImpl.java new file mode 100644 index 00000000..3918cac5 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/impl/AIAssistantServiceImpl.java @@ -0,0 +1,97 @@ +package org.dromara.ai.service.impl; + +import org.dromara.ai.knowledge.vectorization.process.IEmbeddingProcessor; +import org.dromara.ai.mapper.DatabaseMetaMapper; +import org.dromara.ai.process.dto.AIMessage; +import org.dromara.ai.process.dto.AIRequest; +import org.dromara.ai.process.dto.AIResponse; +import org.dromara.ai.process.provider.processor.AIProviderProcessorFactory; +import org.dromara.ai.process.provider.processor.IUnifiedAIProviderProcessor; +import org.dromara.ai.service.IAIAssistantService; +import org.dromara.common.encrypt.utils.EncryptUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.time.Duration; +import java.util.Collections; +import java.util.Map; + +/** + * @Author xins + * @Date 2025/7/17 14:31 + * @Description: + */ +@Service +public class AIAssistantServiceImpl implements IAIAssistantService { + + @Autowired + private DatabaseMetaMapper databaseMetaMapper; + + @Autowired + private Map aiProviderProcessorMap; + + @Autowired + private Map embeddingProcessorMap; + + + @Autowired + private AIProviderProcessorFactory aiProviderProcessorFactory; + + + + + public String chatStream(String provider, AIRequest aiRequest){ + IUnifiedAIProviderProcessor processor = aiProviderProcessorFactory.getProcessor(provider); +// processor.chatStream(aiRequest); + + return ""; + } + + + + public String aiFillForm(){ + String aiClient = "deepSeek"; + IUnifiedAIProviderProcessor service = aiProviderProcessorMap.get(aiClient); + if (service == null) { + throw new IllegalArgumentException("Unsupported payment type"); + } + + return ""; + } + + + public String uploadContent(){ + String embeddingProcessor = "tencentlke"; + IEmbeddingProcessor service = embeddingProcessorMap.get(embeddingProcessor); + + return ""; + } + + + + @Override + public boolean testAIModel(String provider,AIRequest aiRequest){ + IUnifiedAIProviderProcessor processor = aiProviderProcessorMap.get(provider); + if (processor == null) { + throw new IllegalArgumentException("Unsupported processor type"); + } + aiRequest.setModel("deepseek-chat"); + String decryptApiKey = EncryptUtils.decryptByBase64(aiRequest.getApiKey()); + aiRequest.setApiKey(decryptApiKey); + AIMessage aiMessage = new AIMessage(); + aiMessage.setRole("user"); + aiMessage.setContent("这是一个测试请求,请回复'测试成功'"); + aiRequest.setMessages(Collections.singletonList( + aiMessage + )); + AIResponse aiResponse = processor.chat(aiRequest).block(Duration.ofSeconds(10)); + if (aiResponse != null && aiResponse.isSuccess()) { + System.out.println(aiResponse.getContent()); + System.out.println(aiResponse.getUsage()); + } + + return true; + } + + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/impl/AiBaseModelServiceImpl.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/impl/AiBaseModelServiceImpl.java new file mode 100644 index 00000000..f25432ae --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/impl/AiBaseModelServiceImpl.java @@ -0,0 +1,155 @@ +package org.dromara.ai.service.impl; + +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.github.yulichang.toolkit.JoinWrappers; +import com.github.yulichang.wrapper.MPJLambdaWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.dromara.ai.domain.bo.AiBaseModelBo; +import org.dromara.ai.domain.vo.AiBaseModelVo; +import org.dromara.ai.domain.AiBaseModel; +import org.dromara.ai.mapper.AiBaseModelMapper; +import org.dromara.ai.service.IAiBaseModelService; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 基础模型Service业务层处理 + * + * @author xins + * @date 2025-08-06 + */ +@RequiredArgsConstructor +@Service +public class AiBaseModelServiceImpl implements IAiBaseModelService { + + private final AiBaseModelMapper baseMapper; + + /** + * 查询基础模型 + * + * @param baseModelId 主键 + * @return 基础模型 + */ + @Override + public AiBaseModelVo queryById(Long baseModelId){ + return baseMapper.selectVoById(baseModelId); + } + + /** + * 分页查询基础模型列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 基础模型分页列表 + */ + @Override + public TableDataInfo queryPageList(AiBaseModelBo bo, PageQuery pageQuery) { + MPJLambdaWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的基础模型列表 + * + * @param bo 查询条件 + * @return 基础模型列表 + */ + @Override + public List queryList(AiBaseModelBo bo) { + MPJLambdaWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private MPJLambdaWrapper buildQueryWrapper(AiBaseModelBo bo) { + MPJLambdaWrapper lqw = JoinWrappers.lambda(AiBaseModel.class) + .selectAll(AiBaseModel.class) + .eq(bo.getBaseModelId() != null, AiBaseModel::getBaseModelId, bo.getBaseModelId()) + .eq(bo.getPlatformId() != null, AiBaseModel::getPlatformId, bo.getPlatformId()) + .eq(bo.getModelTypeId() != null, AiBaseModel::getModelTypeId, bo.getModelTypeId()) + .like(StringUtils.isNotBlank(bo.getBaseModelName()), AiBaseModel::getBaseModelName, bo.getBaseModelName()) + .eq(StringUtils.isNotBlank(bo.getBaseModelDescription()), AiBaseModel::getBaseModelDescription, bo.getBaseModelDescription()) + .eq(bo.getContextLength() != null, AiBaseModel::getContextLength, bo.getContextLength()) + .eq(StringUtils.isNotBlank(bo.getActiveFlag()), AiBaseModel::getActiveFlag, bo.getActiveFlag()); + return lqw; + } + + /** + * 新增基础模型 + * + * @param bo 基础模型 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(AiBaseModelBo bo) { + AiBaseModel add = MapstructUtils.convert(bo, AiBaseModel.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setBaseModelId(add.getBaseModelId()); + } + return flag; + } + + /** + * 修改基础模型 + * + * @param bo 基础模型 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(AiBaseModelBo bo) { + AiBaseModel update = MapstructUtils.convert(bo, AiBaseModel.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(AiBaseModel entity){ + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除基础模型信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if(isValid){ + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + + + + + /** + * 查询符合条件的基础模型列表 + * + * @param queryBaseModel 查询条件 + * @return 基础模型列表 + */ + @Override + public List queryJoinList(AiBaseModelBo queryBaseModel) { + List aiBaseModelList = baseMapper.selectAiBaseModelJoinList(queryBaseModel); + + return aiBaseModelList; + } + + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/impl/AiChatMessageDetailServiceImpl.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/impl/AiChatMessageDetailServiceImpl.java new file mode 100644 index 00000000..6ee47888 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/impl/AiChatMessageDetailServiceImpl.java @@ -0,0 +1,178 @@ +package org.dromara.ai.service.impl; + +import org.dromara.ai.process.dto.AIMessage; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.github.yulichang.toolkit.JoinWrappers; +import com.github.yulichang.wrapper.MPJLambdaWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.dromara.ai.domain.bo.AiChatMessageDetailBo; +import org.dromara.ai.domain.vo.AiChatMessageDetailVo; +import org.dromara.ai.domain.AiChatMessageDetail; +import org.dromara.ai.mapper.AiChatMessageDetailMapper; +import org.dromara.ai.service.IAiChatMessageDetailService; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 聊天消息详情Service业务层处理 + * + * @author xins + * @date 2025-08-14 + */ +@RequiredArgsConstructor +@Service +public class AiChatMessageDetailServiceImpl implements IAiChatMessageDetailService { + + private final AiChatMessageDetailMapper baseMapper; + + /** + * 查询聊天消息详情 + * + * @param messageDetailId 主键 + * @return 聊天消息详情 + */ + @Override + public AiChatMessageDetailVo queryById(Long messageDetailId) { + return baseMapper.selectVoById(messageDetailId); + } + + /** + * 分页查询聊天消息详情列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 聊天消息详情分页列表 + */ + @Override + public TableDataInfo queryPageList(AiChatMessageDetailBo bo, PageQuery pageQuery) { + MPJLambdaWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的聊天消息详情列表 + * + * @param bo 查询条件 + * @return 聊天消息详情列表 + */ + @Override + public List queryList(AiChatMessageDetailBo bo) { + MPJLambdaWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private MPJLambdaWrapper buildQueryWrapper(AiChatMessageDetailBo bo) { + Map params = bo.getParams(); + MPJLambdaWrapper lqw = JoinWrappers.lambda(AiChatMessageDetail.class) + .selectAll(AiChatMessageDetail.class) + .eq(bo.getMessageDetailId() != null, AiChatMessageDetail::getMessageDetailId, bo.getMessageDetailId()) + .eq(bo.getChatMessageId() != null, AiChatMessageDetail::getChatMessageId, bo.getChatMessageId()) + .eq(bo.getSessionId() != null, AiChatMessageDetail::getSessionId, bo.getSessionId()) + .eq(StringUtils.isNotBlank(bo.getQuestionContent()), AiChatMessageDetail::getQuestionContent, bo.getQuestionContent()) + .eq(StringUtils.isNotBlank(bo.getAnswerContent()), AiChatMessageDetail::getAnswerContent, bo.getAnswerContent()) + .eq(bo.getPromptToken() != null, AiChatMessageDetail::getPromptToken, bo.getPromptToken()) + .eq(bo.getCompletionToken() != null, AiChatMessageDetail::getCompletionToken, bo.getCompletionToken()) + .eq(bo.getTotalToken() != null, AiChatMessageDetail::getTotalToken, bo.getTotalToken()) + .eq(bo.getModelId() != null, AiChatMessageDetail::getModelId, bo.getModelId()) + .eq(bo.getKnowledgeBaseId() != null, AiChatMessageDetail::getKnowledgeBaseId, bo.getKnowledgeBaseId()) + .eq(StringUtils.isNotBlank(bo.getTakeFlag()), AiChatMessageDetail::getTakeFlag, bo.getTakeFlag()) + .eq(StringUtils.isNotBlank(bo.getCompleteFlag()), AiChatMessageDetail::getCompleteFlag, bo.getCompleteFlag()) + .orderByDesc(AiChatMessageDetail::getCreateTime); + return lqw; + } + + /** + * 新增聊天消息详情 + * + * @param bo 聊天消息详情 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(AiChatMessageDetailBo bo) { + AiChatMessageDetail add = MapstructUtils.convert(bo, AiChatMessageDetail.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setMessageDetailId(add.getMessageDetailId()); + } + return flag; + } + + /** + * 修改聊天消息详情 + * + * @param bo 聊天消息详情 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(AiChatMessageDetailBo bo) { + AiChatMessageDetail update = MapstructUtils.convert(bo, AiChatMessageDetail.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(AiChatMessageDetail entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除聊天消息详情信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + + /** + * 根据sessionId查询聊天消息详情列表 + * @param sessionId + * @return LIST + */ + @Override + public List getAIChatMessages(String sessionId) { + AiChatMessageDetailBo bo = new AiChatMessageDetailBo(); + bo.setSessionId(sessionId); + MPJLambdaWrapper lqw = buildQueryWrapper(bo); + List aiChatMessageDetails = baseMapper.selectList(lqw); + List aiMessages = new ArrayList<>(); + //获取数据是根据时间倒序排序的,下面需要按照时间升序添加 + for (int i = aiChatMessageDetails.size() - 1; i >= 0; i--) { + AiChatMessageDetail aiChatMessageDetail = aiChatMessageDetails.get(i); + AIMessage aiUserMessage = new AIMessage(); + aiUserMessage.setRole("user"); + aiUserMessage.setContent(aiChatMessageDetail.getQuestionContent()); + aiUserMessage.setTimestamp(aiChatMessageDetail.getCreateTime().getTime()); + aiMessages.add(aiUserMessage); + + AIMessage aiAssistantMessage = new AIMessage(); + aiAssistantMessage.setRole("assistant"); + aiAssistantMessage.setContent(aiChatMessageDetail.getAnswerContent()); + aiAssistantMessage.setTimestamp(aiChatMessageDetail.getCreateTime().getTime()); + aiMessages.add(aiAssistantMessage); + } + + return aiMessages; + } + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/impl/AiChatMessageServiceImpl.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/impl/AiChatMessageServiceImpl.java new file mode 100644 index 00000000..723f1478 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/impl/AiChatMessageServiceImpl.java @@ -0,0 +1,187 @@ +package org.dromara.ai.service.impl; + +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; +import com.github.yulichang.interfaces.MPJBaseJoin; +import org.dromara.ai.domain.AiChatMessageDetail; +import org.dromara.ai.domain.bo.AiChatMessageTopicBo; +import org.dromara.ai.mapper.AiChatMessageDetailMapper; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.github.yulichang.toolkit.JoinWrappers; +import com.github.yulichang.wrapper.MPJLambdaWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.dromara.ai.domain.bo.AiChatMessageBo; +import org.dromara.ai.domain.vo.AiChatMessageVo; +import org.dromara.ai.domain.AiChatMessage; +import org.dromara.ai.mapper.AiChatMessageMapper; +import org.dromara.ai.service.IAiChatMessageService; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * 聊天消息Service业务层处理 + * + * @author xins + * @date 2025-08-14 + */ +@RequiredArgsConstructor +@Service +public class AiChatMessageServiceImpl implements IAiChatMessageService { + + private final AiChatMessageMapper baseMapper; + + private final AiChatMessageDetailMapper aiChatMessageDetailMapper; + + /** + * 查询聊天消息 + * + * @param chatMessageId 主键 + * @return 聊天消息 + */ + @Override + public AiChatMessageVo queryById(Long chatMessageId) { + return baseMapper.selectVoById(chatMessageId); + } + + /** + * 分页查询聊天消息列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return 聊天消息分页列表 + */ + @Override + public TableDataInfo queryPageList(AiChatMessageBo bo, PageQuery pageQuery) { + MPJLambdaWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的聊天消息列表 + * + * @param bo 查询条件 + * @return 聊天消息列表 + */ + @Override + public List queryList(AiChatMessageBo bo) { + MPJLambdaWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private MPJLambdaWrapper buildQueryWrapper(AiChatMessageBo bo) { + Map params = bo.getParams(); + MPJLambdaWrapper lqw = JoinWrappers.lambda(AiChatMessage.class) + .selectAll(AiChatMessage.class) + .eq(bo.getChatMessageId() != null, AiChatMessage::getChatMessageId, bo.getChatMessageId()) + .eq(StringUtils.isNotBlank(bo.getMessageTopic()), AiChatMessage::getMessageTopic, bo.getMessageTopic()) + .eq(bo.getDeductCost() != null, AiChatMessage::getDeductCost, bo.getDeductCost()) + .eq(bo.getTotalToken() != null, AiChatMessage::getTotalToken, bo.getTotalToken()) + .eq(bo.getModelId() != null, AiChatMessage::getModelId, bo.getModelId()) + .eq(bo.getKnowledgeBaseId() != null, AiChatMessage::getKnowledgeBaseId, bo.getKnowledgeBaseId()) + .eq(StringUtils.isNotBlank(bo.getMessageType()), AiChatMessage::getMessageType, bo.getMessageType()) + .orderByDesc(AiChatMessage::getCreateTime); + return lqw; + } + + /** + * 新增聊天消息 + * + * @param bo 聊天消息 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(AiChatMessageBo bo) { + AiChatMessage add = MapstructUtils.convert(bo, AiChatMessage.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setChatMessageId(add.getChatMessageId()); + } + return flag; + } + + /** + * 修改聊天消息 + * + * @param bo 聊天消息 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(AiChatMessageBo bo) { + AiChatMessage update = MapstructUtils.convert(bo, AiChatMessage.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(AiChatMessage entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除聊天消息信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + + /** + * 修改聊天消息标题 + * + * @param bo 聊天消息 + * @return 是否修改成功 + */ + @Override + public Boolean updateMessageTopic(AiChatMessageTopicBo bo) { + UpdateWrapper updateWrapper = new UpdateWrapper(); + updateWrapper.eq("session_id", bo.getSessionId()) + .set("message_topic", bo.getMessageTopic()); + + baseMapper.update(updateWrapper); + return true; + } + + + /** + * 校验并批量删除聊天消息信息 + * + * @param sessionId 待删除的会话ID + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteWithValidBySessionId(String sessionId, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + + // 先删除AI会话明细 + aiChatMessageDetailMapper.delete(Wrappers.lambdaQuery() + .eq(AiChatMessageDetail::getSessionId, sessionId)); + + return baseMapper.delete(Wrappers.lambdaQuery() + .eq(AiChatMessage::getSessionId, sessionId)) > 0; + } + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/impl/AiKnowledgeBaseTypeServiceImpl.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/impl/AiKnowledgeBaseTypeServiceImpl.java new file mode 100644 index 00000000..fbc2193a --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/impl/AiKnowledgeBaseTypeServiceImpl.java @@ -0,0 +1,136 @@ +package org.dromara.ai.service.impl; + +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.github.yulichang.toolkit.JoinWrappers; +import com.github.yulichang.wrapper.MPJLambdaWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.dromara.ai.domain.bo.AiKnowledgeBaseTypeBo; +import org.dromara.ai.domain.vo.AiKnowledgeBaseTypeVo; +import org.dromara.ai.domain.AiKnowledgeBaseType; +import org.dromara.ai.mapper.AiKnowledgeBaseTypeMapper; +import org.dromara.ai.service.IAiKnowledgeBaseTypeService; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * AI知识库类型Service业务层处理 + * + * @author xins + * @date 2025-08-06 + */ +@RequiredArgsConstructor +@Service +public class AiKnowledgeBaseTypeServiceImpl implements IAiKnowledgeBaseTypeService { + + private final AiKnowledgeBaseTypeMapper baseMapper; + + /** + * 查询AI知识库类型 + * + * @param knowledgeBaseTypeId 主键 + * @return AI知识库类型 + */ + @Override + public AiKnowledgeBaseTypeVo queryById(Long knowledgeBaseTypeId) { + return baseMapper.selectVoById(knowledgeBaseTypeId); + } + + /** + * 分页查询AI知识库类型列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return AI知识库类型分页列表 + */ + @Override + public TableDataInfo queryPageList(AiKnowledgeBaseTypeBo bo, PageQuery pageQuery) { + MPJLambdaWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的AI知识库类型列表 + * + * @param bo 查询条件 + * @return AI知识库类型列表 + */ + @Override + public List queryList(AiKnowledgeBaseTypeBo bo) { + MPJLambdaWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private MPJLambdaWrapper buildQueryWrapper(AiKnowledgeBaseTypeBo bo) { + Map params = bo.getParams(); + MPJLambdaWrapper lqw = JoinWrappers.lambda(AiKnowledgeBaseType.class) + .selectAll(AiKnowledgeBaseType.class) + .eq(bo.getKnowledgeBaseTypeId() != null, AiKnowledgeBaseType::getKnowledgeBaseTypeId, bo.getKnowledgeBaseTypeId()) + .like(StringUtils.isNotBlank(bo.getKnowledgeBaseTypeName()), AiKnowledgeBaseType::getKnowledgeBaseTypeName, bo.getKnowledgeBaseTypeName()) + .like(StringUtils.isNotBlank(bo.getRemark()), AiKnowledgeBaseType::getRemark, bo.getRemark()) + .orderByDesc(AiKnowledgeBaseType::getCreateTime); + return lqw; + } + + /** + * 新增AI知识库类型 + * + * @param bo AI知识库类型 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(AiKnowledgeBaseTypeBo bo) { + AiKnowledgeBaseType add = MapstructUtils.convert(bo, AiKnowledgeBaseType.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setKnowledgeBaseTypeId(add.getKnowledgeBaseTypeId()); + } + return flag; + } + + /** + * 修改AI知识库类型 + * + * @param bo AI知识库类型 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(AiKnowledgeBaseTypeBo bo) { + AiKnowledgeBaseType update = MapstructUtils.convert(bo, AiKnowledgeBaseType.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(AiKnowledgeBaseType entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除AI知识库类型信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + + } +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/impl/AiModelServiceImpl.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/impl/AiModelServiceImpl.java new file mode 100644 index 00000000..52937dd2 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/impl/AiModelServiceImpl.java @@ -0,0 +1,241 @@ +package org.dromara.ai.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import lombok.AllArgsConstructor; +import org.dromara.ai.domain.AiBaseModel; +import org.dromara.ai.domain.AiModelType; +import org.dromara.ai.domain.AiPlatform; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.encrypt.annotation.EncryptField; +import org.dromara.common.encrypt.core.EncryptContext; +import org.dromara.common.encrypt.core.EncryptorManager; +import org.dromara.common.encrypt.enumd.AlgorithmType; +import org.dromara.common.encrypt.enumd.EncodeType; +import org.dromara.common.encrypt.properties.EncryptorProperties; +import org.dromara.common.encrypt.utils.EncryptUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.github.yulichang.toolkit.JoinWrappers; +import com.github.yulichang.wrapper.MPJLambdaWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.dromara.ai.domain.bo.AiModelBo; +import org.dromara.ai.domain.vo.AiModelVo; +import org.dromara.ai.domain.AiModel; +import org.dromara.ai.mapper.AiModelMapper; +import org.dromara.ai.service.IAiModelService; + +import java.lang.reflect.Field; +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * AI模型Service业务层处理 + * + * @author xins + * @date 2025-08-07 + */ +@RequiredArgsConstructor +@Service +public class AiModelServiceImpl implements IAiModelService { + + private final AiModelMapper baseMapper; + + /** + * 查询AI模型 + * + * @param modelId 主键 + * @return AI模型 + */ + @Override + public AiModelVo queryById(Long modelId) { + return baseMapper.selectVoById(modelId); + } + + /** + * 分页查询AI模型列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return AI模型分页列表 + */ + @Override + public TableDataInfo queryPageList(AiModelBo bo, PageQuery pageQuery) { + MPJLambdaWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的AI模型列表 + * + * @param bo 查询条件 + * @return AI模型列表 + */ + @Override + public List queryList(AiModelBo bo) { + MPJLambdaWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + /** + * 查询符合条件的AI模型列表,Join basemodel,modeltype,platform + * + * @param bo 查询条件 + * @return AI模型列表 + */ + @Override + public List queryJoinList(AiModelBo bo) { + MPJLambdaWrapper lqw = buildJoinQueryWrapper(bo); + return baseMapper.selectJoinList(lqw); + } + + private MPJLambdaWrapper buildQueryWrapper(AiModelBo bo) { + Map params = bo.getParams(); + MPJLambdaWrapper lqw = JoinWrappers.lambda(AiModel.class) + .selectAll(AiModel.class) + .eq(bo.getModelId() != null, AiModel::getModelId, bo.getModelId()) + .like(StringUtils.isNotBlank(bo.getModelName()), AiModel::getModelName, bo.getModelName()) + .eq(bo.getPlatformId() != null, AiModel::getPlatformId, bo.getPlatformId()) + .eq(bo.getModelTypeId() != null, AiModel::getModelTypeId, bo.getModelTypeId()) + .eq(bo.getBaseModelId() != null, AiModel::getBaseModelId, bo.getBaseModelId()) + .eq(StringUtils.isNotBlank(bo.getChargeType()), AiModel::getChargeType, bo.getChargeType()) + .eq(bo.getModelPrice() != null, AiModel::getModelPrice, bo.getModelPrice()) + .eq(StringUtils.isNotBlank(bo.getApiEndpoint()), AiModel::getApiEndpoint, bo.getApiEndpoint()) + .eq(StringUtils.isNotBlank(bo.getApiKey()), AiModel::getApiKey, bo.getApiKey()) + .eq(StringUtils.isNotBlank(bo.getApiSecret()), AiModel::getApiSecret, bo.getApiSecret()) + .eq(StringUtils.isNotBlank(bo.getVectorLibrary()), AiModel::getVectorLibrary, bo.getVectorLibrary()) + .eq(StringUtils.isNotBlank(bo.getDescription()), AiModel::getDescription, bo.getDescription()) + .eq(bo.getModelTemperature() != null, AiModel::getModelTemperature, bo.getModelTemperature()) + .eq(bo.getVocabularyAttribute() != null, AiModel::getVocabularyAttribute, bo.getVocabularyAttribute()) + .eq(bo.getTopicAttribute() != null, AiModel::getTopicAttribute, bo.getTopicAttribute()) + .eq(bo.getDuplicateAttribute() != null, AiModel::getDuplicateAttribute, bo.getDuplicateAttribute()) + .eq(bo.getMaxReply() != null, AiModel::getMaxReply, bo.getMaxReply()) + .eq(StringUtils.isNotBlank(bo.getActiveFlag()), AiModel::getActiveFlag, bo.getActiveFlag()) + .orderByDesc(AiModel::getCreateTime); + return lqw; + } + + private MPJLambdaWrapper buildJoinQueryWrapper(AiModelBo bo) { + Map params = bo.getParams(); + MPJLambdaWrapper lqw = JoinWrappers.lambda(AiModel.class) + .selectAll(AiModel.class) + .select(AiPlatform::getPlatformCode) + .select(AiPlatform::getPlatformName) + .select(AiPlatform::getPlatformIcon) + .leftJoin(AiPlatform.class, AiPlatform::getPlatformId, AiModel::getPlatformId) + .select(AiModelType::getModelTypeCode) + .select(AiModelType::getModelTypeName) + .leftJoin(AiModelType.class, AiModelType::getModelTypeId, AiModel::getModelTypeId) + .select(AiBaseModel::getBaseModelName) + .leftJoin(AiBaseModel.class, AiBaseModel::getBaseModelId, AiModel::getBaseModelId) + .eq(bo.getModelId() != null, AiModel::getModelId, bo.getModelId()) + .like(StringUtils.isNotBlank(bo.getModelName()), AiModel::getModelName, bo.getModelName()) + .eq(bo.getPlatformId() != null, AiModel::getPlatformId, bo.getPlatformId()) + .eq(bo.getModelTypeId() != null, AiModel::getModelTypeId, bo.getModelTypeId()) + .eq(bo.getBaseModelId() != null, AiModel::getBaseModelId, bo.getBaseModelId()) + .eq(StringUtils.isNotBlank(bo.getChargeType()), AiModel::getChargeType, bo.getChargeType()) + .eq(bo.getModelPrice() != null, AiModel::getModelPrice, bo.getModelPrice()) + .eq(StringUtils.isNotBlank(bo.getApiEndpoint()), AiModel::getApiEndpoint, bo.getApiEndpoint()) + .eq(StringUtils.isNotBlank(bo.getApiKey()), AiModel::getApiKey, bo.getApiKey()) + .eq(StringUtils.isNotBlank(bo.getApiSecret()), AiModel::getApiSecret, bo.getApiSecret()) + .eq(StringUtils.isNotBlank(bo.getVectorLibrary()), AiModel::getVectorLibrary, bo.getVectorLibrary()) + .eq(StringUtils.isNotBlank(bo.getDescription()), AiModel::getDescription, bo.getDescription()) + .eq(bo.getModelTemperature() != null, AiModel::getModelTemperature, bo.getModelTemperature()) + .eq(bo.getVocabularyAttribute() != null, AiModel::getVocabularyAttribute, bo.getVocabularyAttribute()) + .eq(bo.getTopicAttribute() != null, AiModel::getTopicAttribute, bo.getTopicAttribute()) + .eq(bo.getDuplicateAttribute() != null, AiModel::getDuplicateAttribute, bo.getDuplicateAttribute()) + .eq(bo.getMaxReply() != null, AiModel::getMaxReply, bo.getMaxReply()) + .eq(StringUtils.isNotBlank(bo.getActiveFlag()), AiModel::getActiveFlag, bo.getActiveFlag()) + .orderByDesc(AiModel::getCreateTime); + return lqw; + } + + /** + * 新增AI模型 + * + * @param bo AI模型 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(AiModelBo bo) { + AiModel add = MapstructUtils.convert(bo, AiModel.class); + add.setActiveFlag("1"); + + String apiKey = add.getApiKey(); + String apiSecret = add.getApiSecret(); + if (StringUtils.isNotEmpty(apiKey)) { + String encryptApiKey = EncryptUtils.encryptByBase64(add.getApiKey()); +// String decryptApiKey = EncryptUtils.decryptByBase64(encryptApiKey); + add.setApiKey(encryptApiKey); + } + if (StringUtils.isNotEmpty(apiSecret)) { + String encryptApiSecret = EncryptUtils.encryptByBase64(add.getApiSecret()); +// String decryptApiSecret = EncryptUtils.decryptByBase64(encryptApiSecret); + add.setApiSecret(encryptApiSecret); + } + + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setModelId(add.getModelId()); + } + return flag; + } + + /** + * 修改AI模型 + * + * @param bo AI模型 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(AiModelBo bo) { + AiModel update = MapstructUtils.convert(bo, AiModel.class); + validEntityBeforeSave(update); + String apiKey = update.getApiKey(); + String apiSecret = update.getApiSecret(); + //todo:前端显示什么,如何修改 + if (StringUtils.isNotEmpty(apiKey)) { + String encryptApiKey = EncryptUtils.encryptByBase64(update.getApiKey()); +// String decryptApiKey = EncryptUtils.decryptByBase64(encryptApiKey); + update.setApiKey(encryptApiKey); + } + if (StringUtils.isNotEmpty(apiSecret)) { + String encryptApiSecret = EncryptUtils.encryptByBase64(update.getApiSecret()); +// String decryptApiSecret = EncryptUtils.decryptByBase64(encryptApiSecret); + update.setApiSecret(encryptApiSecret); + } + + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(AiModel entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除AI模型信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } + + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/impl/AiPlatformServiceImpl.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/impl/AiPlatformServiceImpl.java new file mode 100644 index 00000000..7d294806 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/impl/AiPlatformServiceImpl.java @@ -0,0 +1,137 @@ +package org.dromara.ai.service.impl; + +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.github.yulichang.toolkit.JoinWrappers; +import com.github.yulichang.wrapper.MPJLambdaWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.dromara.ai.domain.bo.AiPlatformBo; +import org.dromara.ai.domain.vo.AiPlatformVo; +import org.dromara.ai.domain.AiPlatform; +import org.dromara.ai.mapper.AiPlatformMapper; +import org.dromara.ai.service.IAiPlatformService; + +import java.util.List; +import java.util.Map; +import java.util.Collection; + +/** + * AI平台Service业务层处理 + * + * @author xins + * @date 2025-08-06 + */ +@RequiredArgsConstructor +@Service +public class AiPlatformServiceImpl implements IAiPlatformService { + + private final AiPlatformMapper baseMapper; + + /** + * 查询AI平台 + * + * @param platformId 主键 + * @return AI平台 + */ + @Override + public AiPlatformVo queryById(Long platformId){ + return baseMapper.selectVoById(platformId); + } + + /** + * 分页查询AI平台列表 + * + * @param bo 查询条件 + * @param pageQuery 分页参数 + * @return AI平台分页列表 + */ + @Override + public TableDataInfo queryPageList(AiPlatformBo bo, PageQuery pageQuery) { + MPJLambdaWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + /** + * 查询符合条件的AI平台列表 + * + * @param bo 查询条件 + * @return AI平台列表 + */ + @Override + public List queryList(AiPlatformBo bo) { + MPJLambdaWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private MPJLambdaWrapper buildQueryWrapper(AiPlatformBo bo) { + Map params = bo.getParams(); + MPJLambdaWrapper lqw = JoinWrappers.lambda(AiPlatform.class) + .selectAll(AiPlatform.class) + .eq(bo.getPlatformId() != null, AiPlatform::getPlatformId, bo.getPlatformId()) + .like(StringUtils.isNotBlank(bo.getPlatformName()), AiPlatform::getPlatformName, bo.getPlatformName()) + .like(StringUtils.isNotBlank(bo.getPlatformDescription()), AiPlatform::getPlatformDescription, bo.getPlatformDescription()) + .eq(StringUtils.isNotBlank(bo.getBaseApiUrl()), AiPlatform::getBaseApiUrl, bo.getBaseApiUrl()) + .eq(StringUtils.isNotBlank(bo.getPlatformIcon()), AiPlatform::getPlatformIcon, bo.getPlatformIcon()) + .eq(StringUtils.isNotBlank(bo.getAuthMethod()), AiPlatform::getAuthMethod, bo.getAuthMethod()) + .orderByDesc(AiPlatform::getCreateTime); + return lqw; + } + + /** + * 新增AI平台 + * + * @param bo AI平台 + * @return 是否新增成功 + */ + @Override + public Boolean insertByBo(AiPlatformBo bo) { + AiPlatform add = MapstructUtils.convert(bo, AiPlatform.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setPlatformId(add.getPlatformId()); + } + return flag; + } + + /** + * 修改AI平台 + * + * @param bo AI平台 + * @return 是否修改成功 + */ + @Override + public Boolean updateByBo(AiPlatformBo bo) { + AiPlatform update = MapstructUtils.convert(bo, AiPlatform.class); + validEntityBeforeSave(update); + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(AiPlatform entity){ + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 校验并批量删除AI平台信息 + * + * @param ids 待删除的主键集合 + * @param isValid 是否进行有效性校验 + * @return 是否删除成功 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if(isValid){ + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteByIds(ids) > 0; + } +} diff --git a/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/impl/DatabaseMetaServiceImpl.java b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/impl/DatabaseMetaServiceImpl.java new file mode 100644 index 00000000..0012eedc --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/java/org/dromara/ai/service/impl/DatabaseMetaServiceImpl.java @@ -0,0 +1,190 @@ +package org.dromara.ai.service.impl; + +import org.dromara.ai.mapper.DatabaseMetaMapper; +import org.dromara.ai.process.provider.processor.impl.DeepSeekProcessor; +import org.dromara.ai.test.ChatRequest; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @Author xins + * @Date 2025/7/8 9:25 + * @Description: + */ +@Service +public class DatabaseMetaServiceImpl { + + @Autowired + private DatabaseMetaMapper databaseMetaMapper; + + @Autowired + private DeepSeekProcessor deepSeekService; + + /** + * 获取格式化的数据库结构描述 + */ + public String getFormattedSchema() { + List> allColumns = databaseMetaMapper.getAllTablesStructure(); + + Map>> tables = allColumns.stream() + .collect(Collectors.groupingBy( + col -> (String) col.get("tableName"), + LinkedHashMap::new, + Collectors.toList() + )); + + StringBuilder sb = new StringBuilder("SQL Server 数据库结构:\n\n"); + + for (Map.Entry>> entry : tables.entrySet()) { + String tableName = entry.getKey(); + List primaryKeys = databaseMetaMapper.getPrimaryKeys(tableName); + + sb.append("- 表名: ").append(tableName).append("\n"); + sb.append(" 主键: ").append(String.join(", ", primaryKeys)).append("\n"); + sb.append(" 字段:\n"); + + for (Map column : entry.getValue()) { + sb.append(" * ") + .append(column.get("columnName")) + .append(" (").append(column.get("dataType")); + + if (column.get("maxLength") != null && (short)column.get("maxLength") > 0) { + sb.append(", 长度: ").append(column.get("maxLength")); + } + if (column.get("precision") != null && (short)column.get("precision") > 0) { + sb.append(", 精度: ").append(column.get("precision")); + } + if (column.get("scale") != null && (short)column.get("scale") > 0) { + sb.append(", 小数位: ").append(column.get("scale")); + } + + sb.append(column.get("nullable").equals(1) ? ", 可空" : ", 非空"); + + if (!((String)column.get("defaultValue")).isEmpty()) { + sb.append(", 默认值: ").append(column.get("defaultValue")); + } + + if (!((String)column.get("description")).isEmpty()) { + sb.append(", 描述: ").append(column.get("description")); + } + + sb.append(")\n"); + } + sb.append("\n"); + } + + return sb.toString(); + } + + /** + * 获取表的结构信息 + */ + public Map getTableDetail(String tableName) { + Map result = new LinkedHashMap<>(); + result.put("tableName", tableName); + result.put("primaryKeys", databaseMetaMapper.getPrimaryKeys(tableName)); + result.put("columns", databaseMetaMapper.getTableStructure(tableName)); + return result; + } + + + + + + public String generateSQL(String naturalLanguageQuery) { + // 1. 获取数据库结构 + String schemaDescription = this.getFormattedSchema(); + + // 2. 构建 AI 提示 + String prompt = String.format( + "你是一个专业的 SQL Server 数据库专家。根据以下数据库结构:\n\n%s\n\n" + + "请将以下自然语言查询转换为优化的 SQL Server T-SQL 语句:\n" + + "---\n%s\n---\n\n" + + "要求:\n" + + "1. 只返回 SQL 语句,不要包含解释\n" + + "2. 使用 SQL Server 特有的语法(如 TOP 而不是 LIMIT)\n" + + "3. 考虑性能优化\n" + + "4. 使用合适的索引提示(如果需要)\n" + + "5. 包含必要的 WITH(NOLOCK) 提示(适用于高并发环境)\n" + + "6. 使用 ANSI 标准的 JOIN 语法", + schemaDescription, naturalLanguageQuery + ); + + ChatRequest chatRequest = new ChatRequest(); + chatRequest.setPrompt(prompt); + +// Mono dd = deepSeekService.getStandardResponse(chatRequest); +// dd.subscribe(System.out::println); +// dd.subscribe( +// response -> { +// System.out.println("\n=== 完整响应 ==="); +// if (response.getChoices() != null && !response.getChoices().isEmpty()) { +// System.out.println(response.getChoices().get(0).getText()); +// } +// System.out.println("=== 响应结束 ==="); +// }, +// error -> System.err.println("调用失败: " + error.getMessage()) +// ); + +// System.out.println(d); +// d.subscribe( +// data -> System.out.println("Received: " + data), +// error -> System.err.println("Error: " + error), +// () -> System.out.println("Stream completed") +// ); + return prompt; +// // 3. 调用 DeepSeek API +// HttpHeaders headers = new HttpHeaders(); +// headers.setContentType(MediaType.APPLICATION_JSON); +// headers.set("Authorization", "Bearer " + apiKey); +// +// Map request = new HashMap<>(); +// request.put("model", "deepseek-chat"); +// request.put("messages", List.of(Map.of("role", "user", "content", prompt))); +// request.put("temperature", 0.3); // 降低随机性以获得更确定的 SQL +// +// HttpEntity> entity = new HttpEntity<>(request, headers); +// +// try { +// ResponseEntity response = restTemplate.exchange( +// apiUrl, HttpMethod.POST, entity, Map.class); +// +// if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) { +// return extractSqlFromResponse(response.getBody()); +// } +// throw new RuntimeException("API 调用失败,状态码: " + response.getStatusCode()); +// } catch (Exception e) { +// throw new RuntimeException("生成 SQL 失败: " + e.getMessage(), e); +// } + } + + + + public String generateSQL1(String naturalLanguageQuery) { + // 1. 获取数据库结构 + String schemaDescription = this.getFormattedSchema(); + + // 2. 构建 AI 提示 + String prompt = String.format( + "你是一个专业的 SQL Server 数据库专家。根据以下数据库结构:\n\n%s\n\n" + + "请将以下自然语言查询转换为优化的 SQL Server T-SQL 语句:\n" + + "---\n%s\n---\n\n" + + "要求:\n" + + "1. 只返回 SQL 语句,不要包含解释\n" + + "2. 使用 SQL Server 特有的语法(如 TOP 而不是 LIMIT)\n" + + "3. 考虑性能优化\n" + + "4. 使用合适的索引提示(如果需要)\n" + + "5. 包含必要的 WITH(NOLOCK) 提示(适用于高并发环境)\n" + + "6. 使用 ANSI 标准的 JOIN 语法", + schemaDescription, naturalLanguageQuery + ); + + return prompt; + } + +} diff --git a/ruoyi-modules/hwmom-ai/src/main/resources/application.yml b/ruoyi-modules/hwmom-ai/src/main/resources/application.yml new file mode 100644 index 00000000..48f6b320 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/resources/application.yml @@ -0,0 +1,34 @@ +# Tomcat +server: + port: 6019 + +# Spring +spring: + application: + # 应用名称 + name: hwmom-ai + profiles: + # 环境配置 + active: @profiles.active@ + +--- # nacos 配置 +spring: + cloud: + nacos: + # nacos 服务地址 + server-addr: @nacos.server@ + username: @nacos.username@ + password: @nacos.password@ + discovery: + # 注册组 + group: @nacos.discovery.group@ + namespace: ${spring.profiles.active} + config: + # 配置组 + group: @nacos.config.group@ + namespace: ${spring.profiles.active} + config: + import: + - optional:nacos:application-common.yml + - optional:nacos:datasource.yml + - optional:nacos:${spring.application.name}.yml diff --git a/ruoyi-modules/hwmom-ai/src/main/resources/banner.txt b/ruoyi-modules/hwmom-ai/src/main/resources/banner.txt new file mode 100644 index 00000000..26a8803c --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/resources/banner.txt @@ -0,0 +1,7 @@ +Spring Boot Version: ${spring-boot.version} +Spring Application Name: ${spring.application.name} + __ + / /_ _ ______ ___ ____ ____ ___ ____ ___ ___ _____ + / __ \ | /| / / __ `__ \/ __ \/ __ `__ \______/ __ `__ \/ _ \/ ___/ + / / / / |/ |/ / / / / / / /_/ / / / / / /_____/ / / / / / __(__ ) +/_/ /_/|__/|__/_/ /_/ /_/\____/_/ /_/ /_/ /_/ /_/ /_/\___/____/ diff --git a/ruoyi-modules/hwmom-ai/src/main/resources/logback-plus.xml b/ruoyi-modules/hwmom-ai/src/main/resources/logback-plus.xml new file mode 100644 index 00000000..caaa3455 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/resources/logback-plus.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + ${console.log.pattern} + utf-8 + + + + + + + + + + + + + + + diff --git a/ruoyi-modules/hwmom-ai/src/main/resources/mapper/ai/AiBaseModelMapper.xml b/ruoyi-modules/hwmom-ai/src/main/resources/mapper/ai/AiBaseModelMapper.xml new file mode 100644 index 00000000..6d6af6c9 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/resources/mapper/ai/AiBaseModelMapper.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ruoyi-modules/hwmom-ai/src/main/resources/mapper/ai/AiChatMessageDetailMapper.xml b/ruoyi-modules/hwmom-ai/src/main/resources/mapper/ai/AiChatMessageDetailMapper.xml new file mode 100644 index 00000000..9b97b2a1 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/resources/mapper/ai/AiChatMessageDetailMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/ruoyi-modules/hwmom-ai/src/main/resources/mapper/ai/AiChatMessageMapper.xml b/ruoyi-modules/hwmom-ai/src/main/resources/mapper/ai/AiChatMessageMapper.xml new file mode 100644 index 00000000..dab6549b --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/resources/mapper/ai/AiChatMessageMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/ruoyi-modules/hwmom-ai/src/main/resources/mapper/ai/AiKnowledgeBaseTypeMapper.xml b/ruoyi-modules/hwmom-ai/src/main/resources/mapper/ai/AiKnowledgeBaseTypeMapper.xml new file mode 100644 index 00000000..bd6ffdd4 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/resources/mapper/ai/AiKnowledgeBaseTypeMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/ruoyi-modules/hwmom-ai/src/main/resources/mapper/ai/AiModelMapper.xml b/ruoyi-modules/hwmom-ai/src/main/resources/mapper/ai/AiModelMapper.xml new file mode 100644 index 00000000..abdcfba2 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/resources/mapper/ai/AiModelMapper.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ruoyi-modules/hwmom-ai/src/main/resources/mapper/ai/AiPlatformMapper.xml b/ruoyi-modules/hwmom-ai/src/main/resources/mapper/ai/AiPlatformMapper.xml new file mode 100644 index 00000000..feda1eb3 --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/resources/mapper/ai/AiPlatformMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/ruoyi-modules/hwmom-ai/src/main/resources/mapper/ai/DatabaseMetaMapper.xml b/ruoyi-modules/hwmom-ai/src/main/resources/mapper/ai/DatabaseMetaMapper.xml new file mode 100644 index 00000000..349a183a --- /dev/null +++ b/ruoyi-modules/hwmom-ai/src/main/resources/mapper/ai/DatabaseMetaMapper.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + diff --git a/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/controller/WmsOutstockDetailController.java b/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/controller/WmsOutstockDetailController.java index 680aeb3f..bfa421f2 100644 --- a/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/controller/WmsOutstockDetailController.java +++ b/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/controller/WmsOutstockDetailController.java @@ -1,26 +1,26 @@ package org.dromara.wms.controller; -import java.util.List; - -import lombok.RequiredArgsConstructor; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.constraints.*; import cn.dev33.satoken.annotation.SaCheckPermission; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; import org.dromara.wms.domain.bo.WmsOutstockDetailBo; import org.dromara.wms.domain.vo.WmsOutstockDetailVo; import org.dromara.wms.service.IWmsOutstockDetailService; -import org.springframework.web.bind.annotation.*; import org.springframework.validation.annotation.Validated; -import org.dromara.common.idempotent.annotation.RepeatSubmit; -import org.dromara.common.log.annotation.Log; -import org.dromara.common.web.core.BaseController; -import org.dromara.common.mybatis.core.page.PageQuery; -import org.dromara.common.core.domain.R; -import org.dromara.common.core.validate.EditGroup; -import org.dromara.common.log.enums.BusinessType; -import org.dromara.common.excel.utils.ExcelUtil; +import org.springframework.web.bind.annotation.*; -import org.dromara.common.mybatis.core.page.TableDataInfo; +import java.util.List; /** * 出库单-物料 @@ -115,4 +115,14 @@ public class WmsOutstockDetailController extends BaseController { public R removeOutstockDetailId(@PathVariable Long outstockDetailId) { return toAjax(wmsOutstockDetailService.deleteById(outstockDetailId)>0); } + + /** + * 查询出库单-物料列表 + */ + @GetMapping("/getOutstockDetailList") + public R> getOutstockDetailList(WmsOutstockDetailBo bo) { + return R.ok(wmsOutstockDetailService.queryList(bo)); + } + + } diff --git a/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/controller/WmsOutstockOrderController.java b/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/controller/WmsOutstockOrderController.java index 0c9ad0a4..e9c2b5df 100644 --- a/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/controller/WmsOutstockOrderController.java +++ b/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/controller/WmsOutstockOrderController.java @@ -1,27 +1,26 @@ package org.dromara.wms.controller; -import java.util.List; - -import lombok.RequiredArgsConstructor; -import jakarta.servlet.http.HttpServletResponse; -import jakarta.validation.constraints.*; import cn.dev33.satoken.annotation.SaCheckPermission; -import org.dromara.wms.domain.WmsOutstockOrder; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; import org.dromara.wms.domain.bo.WmsOutstockOrderBo; import org.dromara.wms.domain.vo.WmsOutstockOrderVo; import org.dromara.wms.service.IWmsOutstockOrderService; -import org.springframework.web.bind.annotation.*; import org.springframework.validation.annotation.Validated; -import org.dromara.common.idempotent.annotation.RepeatSubmit; -import org.dromara.common.log.annotation.Log; -import org.dromara.common.web.core.BaseController; -import org.dromara.common.mybatis.core.page.PageQuery; -import org.dromara.common.core.domain.R; -import org.dromara.common.core.validate.AddGroup; -import org.dromara.common.core.validate.EditGroup; -import org.dromara.common.log.enums.BusinessType; -import org.dromara.common.excel.utils.ExcelUtil; -import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.springframework.web.bind.annotation.*; + +import java.util.List; /** * 出库单 @@ -116,4 +115,14 @@ public class WmsOutstockOrderController extends BaseController { return toAjax(wmsOutstockOrderService.approve(bo)); } + + /** + * 查询出库单列表 + */ + @GetMapping("/getOutstockOrderList") + public R> getOutstockOrderList(WmsOutstockOrderBo bo) { + return R.ok(wmsOutstockOrderService.queryList(bo)); + } + + } diff --git a/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/domain/WmsOutstockDetail.java b/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/domain/WmsOutstockDetail.java index 7f49b9f8..c4394244 100644 --- a/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/domain/WmsOutstockDetail.java +++ b/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/domain/WmsOutstockDetail.java @@ -114,4 +114,10 @@ public class WmsOutstockDetail { @TableField(exist = false) private String materialCategoryName;//字段映射 + /** + * 是否高价值物料(0否,1是) + */ + @TableField(exist = false) + private String isHighValue;//字段映射 + } diff --git a/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/domain/vo/WmsOutstockDetailVo.java b/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/domain/vo/WmsOutstockDetailVo.java index 967393df..25459276 100644 --- a/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/domain/vo/WmsOutstockDetailVo.java +++ b/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/domain/vo/WmsOutstockDetailVo.java @@ -114,4 +114,8 @@ public class WmsOutstockDetailVo implements Serializable { */ private String materialCategoryName;//字段映射 + /** + * 是否高价值物料(0否,1是) + */ + private String isHighValue;//字段映射 } diff --git a/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/service/IWmsInventoryService.java b/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/service/IWmsInventoryService.java index 08a332ad..713fe963 100644 --- a/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/service/IWmsInventoryService.java +++ b/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/service/IWmsInventoryService.java @@ -83,10 +83,10 @@ public interface IWmsInventoryService { TableDataInfo listInventoryAlarm(WmsInventoryBo bo, PageQuery pageQuery); /** - * 根据批次码和库位编码查询库存 + * 根据批次码查询库存 * @param batchCode 批次码 * @param locationCode 库位编码 * @return 库存VO */ - WmsInventoryVo queryByBatchAndLocation(String batchCode, String locationCode); + WmsInventoryVo queryByBatchAndLocation(String batchCode); } diff --git a/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/service/impl/WmsInstockOrderServiceImpl.java b/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/service/impl/WmsInstockOrderServiceImpl.java index 2c8a4c0f..8a706b98 100644 --- a/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/service/impl/WmsInstockOrderServiceImpl.java +++ b/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/service/impl/WmsInstockOrderServiceImpl.java @@ -450,9 +450,9 @@ public class WmsInstockOrderServiceImpl implements IWmsInstockOrderService { @Override public Integer approveInstockOrder(WmsInstockOrderBo bo) { String username = LoginHelper.getUsername(); - if (!username.equals(bo.getAuditBy())){ - return 0; - } +// if (!username.equals(bo.getAuditBy())){ +// return 0; +// } WmsInstockOrder update = MapstructUtils.convert(bo, WmsInstockOrder.class); validEntityBeforeSave(update); update.setUpdateBy(username); @@ -480,13 +480,7 @@ public class WmsInstockOrderServiceImpl implements IWmsInstockOrderService { int count = wmsInstockDetailService.deleteByInstockId(ids); return delete>0; } -/** - * 创建外协入库单 - * 1. 生成入库单号 - * 2. 转换并保存入库单主表信息 - * 3. 保存明细信息 - * 4. 若明细中有批次号,则生成打印记录 - */ + /** * 创建外协入库单 diff --git a/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/service/impl/WmsOutstockDetailServiceImpl.java b/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/service/impl/WmsOutstockDetailServiceImpl.java index 9eb9a0ce..55761894 100644 --- a/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/service/impl/WmsOutstockDetailServiceImpl.java +++ b/ruoyi-modules/hwmom-wms/src/main/java/org/dromara/wms/service/impl/WmsOutstockDetailServiceImpl.java @@ -87,6 +87,9 @@ public class WmsOutstockDetailServiceImpl implements IWmsOutstockDetailService { MPJLambdaWrapper lqw = JoinWrappers.lambda(WmsOutstockDetail.class) .selectAll(WmsOutstockDetail.class) + .select(BaseMaterialInfo::getIsHighValue) + .leftJoin(BaseMaterialInfo.class, BaseMaterialInfo::getMaterialId, WmsOutstockDetail::getMaterialId) + // 关联表查询物料大类名称 .select(BaseMaterialCategory::getMaterialCategoryName) .leftJoin(BaseMaterialCategory.class, BaseMaterialCategory::getMaterialCategoryId, WmsOutstockDetail::getMaterialCategoryId) diff --git a/ruoyi-modules/pom.xml b/ruoyi-modules/pom.xml index b1ff8ae6..4e184f8a 100644 --- a/ruoyi-modules/pom.xml +++ b/ruoyi-modules/pom.xml @@ -23,6 +23,7 @@ hwmom-dp hwmom-tsdb hwmom-workflow + hwmom-ai