Merge remote-tracking branch 'origin/master'

hwmom-htk
wanghao 5 months ago
commit 5d883a1512

@ -0,0 +1,260 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-modules</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>hwmom-ai</artifactId>
<description>
hwmom-ai人工智能系统模块
</description>
<properties>
<grpc.version>1.59.0</grpc.version> <!-- 匹配 Milvus v2.5.14 -->
</properties>
<dependencies>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-nacos</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-sentinel</artifactId>
</dependency>
<!-- RuoYi Common Log -->
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-log</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-dict</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-doc</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-web</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-mybatis</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-dubbo</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-seata</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-idempotent</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-tenant</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-security</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-translation</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-sensitive</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-api-resource</artifactId>
</dependency>
<!-- <dependency>-->
<!-- <groupId>com.mysql</groupId>-->
<!-- <artifactId>mysql-connector-j</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>hwmom-common-mom</artifactId>
<version>2.2.2</version>
<scope>compile</scope>
</dependency>
<!-- DeepSeek API Client (假设使用HTTP客户端) -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5.13</version>
</dependency>
<!-- OpenAI SDK -->
<dependency>
<groupId>com.theokanning.openai-gpt3-java</groupId>
<artifactId>service</artifactId>
<version>0.18.0</version>
</dependency>
<dependency>
<groupId>com.theokanning.openai-gpt3-java</groupId>
<artifactId>client</artifactId>
<version>0.18.0</version>
</dependency>
<!-- 本地模型依赖以sentence-transformers为例 -->
<dependency>
<groupId>org.tensorflow</groupId>
<artifactId>tensorflow-core-platform</artifactId>
<version>0.5.0</version>
</dependency>
<dependency>
<groupId>org.deeplearning4j</groupId>
<artifactId>deeplearning4j-core</artifactId>
<version>1.0.0-M2.1</version>
</dependency>
<dependency>
<groupId>ai.djl</groupId>
<artifactId>api</artifactId>
<version>0.28.0</version>
</dependency>
<dependency>
<groupId>ai.djl</groupId>
<artifactId>model-zoo</artifactId>
<version>0.25.0</version>
</dependency>
<dependency>
<groupId>ai.djl.pytorch</groupId>
<artifactId>pytorch-engine</artifactId>
<version>0.25.0</version>
<scope>runtime</scope>
</dependency>
<!-- JSON处理 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- 文本处理工具 -->
<dependency>
<groupId>org.apache.tika</groupId>
<artifactId>tika-core</artifactId>
<version>2.9.1</version>
</dependency>
<!-- 向量数据库客户端 - 以ChromaDB为例 -->
<!-- <dependency>-->
<!-- <groupId>io.github.hwchase17</groupId>-->
<!-- <artifactId>langchain4j-chroma</artifactId>-->
<!-- <version>0.22.0</version>-->
<!-- </dependency>-->
<!-- 其他必要依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java-common</artifactId>
<version>LATEST</version>
</dependency>
<dependency>
<groupId>com.tencentcloudapi</groupId>
<artifactId>tencentcloud-sdk-java</artifactId>
<version>LATEST</version>
</dependency>
<dependency>
<groupId>com.github.jelmerk</groupId>
<artifactId>hnswlib-core</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
</dependency>
<!-- Milvus Java SDK -->
<dependency>
<groupId>io.milvus</groupId>
<artifactId>milvus-sdk-java</artifactId>
<version>2.4.1</version>
</dependency>
<dependency>
<groupId>org.dromara</groupId>
<artifactId>ruoyi-common-encrypt</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -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模块启动成功 ლ(´ڡ`ლ)゙ ");
}
}

@ -0,0 +1,158 @@
package org.dromara.ai.controller;
/**
* @Author xins
* @Date 2025/7/17 14:26
* @Description:AIController
*/
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<List<AiModelVo>> getAiModelJoinList(AiModelBo bo) {
List<AiModelVo> list = aiModelService.queryJoinList(bo);
return R.ok(list);
}
/**
* AI
*/
@GetMapping("/getAiChatMessageList")
public R<List<AiChatMessageVo>> getAiChatMessageList(AiChatMessageBo bo) {
List<AiChatMessageVo> list = aiChatMessageService.queryList(bo);
return R.ok(list);
}
/**
* AI
*/
@GetMapping("/getAiChatMessages/{sessionId}")
public R<List<AIMessage>> getAiChatMessages(@PathVariable("sessionId") String sessionId) {
List<AIMessage> list = aiChatMessageDetailService.getAIChatMessages(sessionId);
return R.ok(list);
}
/**
* message topic
*/
@Log(title = "会话标题", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PostMapping("/updateAiChatMessageTopic")
public R<Void> 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<Void> remove(@NotEmpty(message = "会话ID不能为空")
@PathVariable String sessionId) {
return toAjax(aiChatMessageService.deleteWithValidBySessionId(sessionId, true));
}
/**
*
*/
@PostMapping(value = "/chatStream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> 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<AIResponse> 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 "";
}
}

@ -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<AiChatMessageVo> 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<AiChatMessageVo> list = aiChatMessageService.queryList(bo);
ExcelUtil.exportExcel(list, "聊天消息", AiChatMessageVo.class, response);
}
/**
*
*
* @param chatMessageId
*/
@SaCheckPermission("ai:aiChatMessage:query")
@GetMapping("/{chatMessageId}")
public R<AiChatMessageVo> 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<Void> 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<Void> 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<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] chatMessageIds) {
return toAjax(aiChatMessageService.deleteWithValidByIds(List.of(chatMessageIds), true));
}
/**
*
*/
@GetMapping("/getAiChatMessageList")
public R<List<AiChatMessageVo>> getAiChatMessageList(AiChatMessageBo bo) {
List<AiChatMessageVo> list = aiChatMessageService.queryList(bo);
return R.ok(list);
}
}

@ -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<AiKnowledgeBaseTypeVo> 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<AiKnowledgeBaseTypeVo> list = aiKnowledgeBaseTypeService.queryList(bo);
ExcelUtil.exportExcel(list, "AI知识库类型", AiKnowledgeBaseTypeVo.class, response);
}
/**
* AI
*
* @param knowledgeBaseTypeId
*/
@SaCheckPermission("ai:aiKnowledgeBaseType:query")
@GetMapping("/{knowledgeBaseTypeId}")
public R<AiKnowledgeBaseTypeVo> 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<Void> 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<Void> 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<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] knowledgeBaseTypeIds) {
return toAjax(aiKnowledgeBaseTypeService.deleteWithValidByIds(List.of(knowledgeBaseTypeIds), true));
}
/**
* AI
*/
@GetMapping("/getAiKnowledgeBaseTypeList")
public R<List<AiKnowledgeBaseTypeVo>> getAiKnowledgeBaseTypeList(AiKnowledgeBaseTypeBo bo) {
List<AiKnowledgeBaseTypeVo> list = aiKnowledgeBaseTypeService.queryList(bo);
return R.ok(list);
}
}

@ -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<AiModelVo>> 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<AiModelVo> list = aiModelService.queryList(bo);
ExcelUtil.exportExcel(list, "AI模型", AiModelVo.class, response);
}
/**
* AI
*
* @param modelId
*/
@SaCheckPermission("ai:aiModel:query")
@GetMapping("/{modelId}")
public R<AiModelVo> 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<Void> 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<Void> 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<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] modelIds) {
return toAjax(aiModelService.deleteWithValidByIds(List.of(modelIds), true));
}
/**
* AI
*/
@GetMapping("/getAiModelList")
public R<List<AiModelVo>> getAiModelList(AiModelBo bo) {
List<AiModelVo> list = aiModelService.queryList(bo);
return R.ok(list);
}
/**
*
*/
@PostMapping("/testAIModel")
public R<Void> 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<Double> 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("模型类型错误");
}
}

@ -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<AiPlatformVo> list(AiPlatformBo bo, PageQuery pageQuery) {
// return aiPlatformService.queryPageList(bo, pageQuery);
// }
/**
* AI
*/
@SaCheckPermission("ai/base:aiPlatform:list")
@GetMapping("/list")
public R<List<AiPlatformVo>> list(AiPlatformBo bo) {
return R.ok(aiPlatformService.queryList(bo));
}
@GetMapping("/getAiBaseModelList")
public R<List<AiBaseModel>> getAiBaseModelList(AiBaseModelBo bo) {
List<AiBaseModel> 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<AiPlatformVo> list = aiPlatformService.queryList(bo);
ExcelUtil.exportExcel(list, "AI平台", AiPlatformVo.class, response);
}
/**
* AI
*
* @param platformId
*/
@SaCheckPermission("ai/base:aiPlatform:query")
@GetMapping("/{platformId}")
public R<AiPlatformVo> 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<Void> 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<Void> 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<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable Long[] platformIds) {
return toAjax(aiPlatformService.deleteWithValidByIds(List.of(platformIds), true));
}
/**
* AI
*/
@GetMapping("/getAiPlatformList")
public R<List<AiPlatformVo>> getAiPlatformList(AiPlatformBo bo) {
List<AiPlatformVo> list = aiPlatformService.queryList(bo);
return R.ok(list);
}
}

@ -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;
/**
* IDai_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;
/**
* (10)
*/
private String defaultFlag;
/**
*
*/
private Long rateLimit;
}

@ -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;
/**
* IDai_platform
*/
private Long platformId;
/**
* IDai_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;
/**
* (10)
*/
private String activeFlag;
/**
*
*/
@TableField(exist = false)
private List<AiModelCapability> aiModelCapabilityList;
/**
* api
*/
@TableField(exist = false)
private List<AiApiEndpoint> aiApiEndpointList;
/**
*
*/
@TableField(exist = false)
private String modelTypeName;
}

@ -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;
/**
* IDai_model
*/
private Long modelId;
/**
* IDai_knowledge_base
*/
private Long knowledgeBaseId;
/**
* (1AI23AISQL,4AI)
*/
private String messageType;
}

@ -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;
/**
* IDai_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;
/**
* IDai_model
*/
private Long modelId;
/**
* IDai_knowledge_base
*/
private Long knowledgeBaseId;
/**
* 10
*/
private String takeFlag;
/**
* (10)
*/
private String completeFlag;
}

@ -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;
}

@ -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;
/**
* AIIDai_platform
*/
private Long platformId;
/**
* IDai_model_type
*/
private Long modelTypeId;
/**
* IDai_base_model
*/
private Long baseModelId;
/**
* (1token2)
*/
private String chargeType;
/**
*
*/
private Long modelPrice;
/**
*
*/
private String apiEndpoint;
/**
*
*/
private String apiKey;
/**
*
*/
private String apiSecret;
/**
* (1milvus2weaviate,)
*/
private String vectorLibrary;
/**
*
*/
private String description;
/**
* (0-1);00.5-0.8
*/
private Long modelTemperature;
/**
* (0-1);AIAI
*/
private Long vocabularyAttribute;
/**
* (-2.0-2.0);AI
*/
private Long topicAttribute;
/**
* (-2.0-2.0);AI
*/
private Long duplicateAttribute;
/**
* (1-16000);AI500-800800-20002000-36004000
*/
private Long maxReply;
/**
* (10)
*/
private String defaultFlag;
/**
* (10)
*/
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;
}

@ -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;
/**
* IDai_base_model
*/
private Long baseModelId;
/**
* : , ,
*/
private String modelCapabilityName;
/**
*
*/
private String modelCapabilityDescription;
}

@ -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;
/**
* LLMEmbedding
*/
private String modelTypeCode;
/**
*
*/
private String modelTypeName;
/**
*
*/
private String modelTypeDescription;
}

@ -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;
}

@ -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;
/**
* IDai_platform
*/
@NotNull(message = "平台ID关联ai_platform不能为空", groups = { AddGroup.class, EditGroup.class })
private Long platformId;
/**
* IDai_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;
/**
* (10)
*/
@NotBlank(message = "激活标识(1是0否)不能为空", groups = { AddGroup.class, EditGroup.class })
private String activeFlag;
}

@ -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;
/**
* IDai_model
*/
@NotNull(message = "模型ID关联ai_model不能为空", groups = { AddGroup.class, EditGroup.class })
private Long modelId;
/**
* IDai_knowledge_base
*/
@NotNull(message = "知识库ID关联ai_knowledge_base不能为空", groups = { AddGroup.class, EditGroup.class })
private Long knowledgeBaseId;
/**
* (1AI23AISQL,4AI)
*/
@NotBlank(message = "聊天类型(1AI问答2知识库问答3AI生成SQL,4AI填报)不能为空", groups = { AddGroup.class, EditGroup.class })
private String messageType;
}

@ -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;
/**
* IDai_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;
/**
* IDai_model
*/
@NotNull(message = "模型ID关联ai_model不能为空", groups = { AddGroup.class, EditGroup.class })
private Long modelId;
/**
* IDai_knowledge_base
*/
@NotNull(message = "知识库ID关联ai_knowledge_base不能为空", groups = { AddGroup.class, EditGroup.class })
private Long knowledgeBaseId;
/**
* 10
*/
@NotBlank(message = "是否携带历史内容1是0否不能为空", groups = { AddGroup.class, EditGroup.class })
private String takeFlag;
/**
* (10)
*/
@NotBlank(message = "完整标识(1是0否),代表回复信息是否完整回复,中间可以暂停继续。不能为空", groups = { AddGroup.class, EditGroup.class })
private String completeFlag;
}

@ -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;
}

@ -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;
}

@ -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;
/**
* AIIDai_platform
*/
@NotNull(message = "AI平台ID关联ai_platform不能为空", groups = { AddGroup.class, EditGroup.class })
private Long platformId;
/**
* IDai_model_type
*/
@NotNull(message = "模型类型ID关联ai_model_type不能为空", groups = { AddGroup.class, EditGroup.class })
private Long modelTypeId;
/**
* IDai_base_model
*/
@NotNull(message = "基础模型ID关联ai_base_model不能为空", groups = { AddGroup.class, EditGroup.class })
private Long baseModelId;
/**
* (1token2)
*/
private String chargeType;
/**
*
*/
private Long modelPrice;
/**
*
*/
private String apiEndpoint;
/**
*
*/
private String apiKey;
/**
*
*/
private String apiSecret;
/**
* (1milvus2weaviate,)
*/
private String vectorLibrary;
/**
*
*/
private String description;
/**
* (0-1);00.5-0.8
*/
private Long modelTemperature;
/**
* (0-1);AIAI
*/
private Long vocabularyAttribute;
/**
* (-2.0-2.0);AI
*/
private Long topicAttribute;
/**
* (-2.0-2.0);AI
*/
private Long duplicateAttribute;
/**
* (1-16000);AI500-800800-20002000-36004000
*/
private Long maxReply;
/**
* (10)
*/
private String defaultFlag;
/**
* (10)
*/
// @NotBlank(message = "激活标识(1是0否)不能为空", groups = { AddGroup.class, EditGroup.class })
private String activeFlag;
}

@ -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;
/**
* LLMEmbedding
*/
@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;
}

@ -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;
}

@ -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;
/**
* IDai_platform
*/
@ExcelProperty(value = "平台ID关联ai_platform")
private Long platformId;
/**
* IDai_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;
/**
* (10)
*/
@ExcelProperty(value = "激活标识(1是0否)")
private String activeFlag;
/**
*
*/
@TableField(exist = false)
private List<AiModelCapability> aiModelCapabilityList;
/**
* api
*/
@TableField(exist = false)
private List<AiApiEndpoint> aiApiEndpointList;
}

@ -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;
/**
* IDai_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;
/**
* IDai_model
*/
@ExcelProperty(value = "模型ID关联ai_model")
private Long modelId;
/**
* IDai_knowledge_base
*/
@ExcelProperty(value = "知识库ID关联ai_knowledge_base")
private Long knowledgeBaseId;
/**
* 10
*/
@ExcelProperty(value = "是否携带历史内容", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "1=是0否")
private String takeFlag;
/**
* (10)
*/
@ExcelProperty(value = "完整标识(1是0否),代表回复信息是否完整回复,中间可以暂停继续。")
private String completeFlag;
}

@ -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;
/**
* IDai_model
*/
@ExcelProperty(value = "模型ID关联ai_model")
private Long modelId;
/**
* IDai_knowledge_base
*/
@ExcelProperty(value = "知识库ID关联ai_knowledge_base")
private Long knowledgeBaseId;
/**
* (1AI23AISQL,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();
}
}

@ -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;
}

@ -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;
/**
* LLMEmbedding
*/
@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;
}

@ -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;
/**
* AIIDai_platform
*/
@ExcelProperty(value = "AI平台ID关联ai_platform")
private Long platformId;
/**
* IDai_model_type
*/
@ExcelProperty(value = "模型类型ID关联ai_model_type")
private Long modelTypeId;
/**
* IDai_base_model
*/
@ExcelProperty(value = "基础模型ID关联ai_base_model")
private Long baseModelId;
/**
* (1token2)
*/
@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;
/**
* (1milvus2weaviate,)
*/
@ExcelProperty(value = "向量库(1milvus2weaviate,)")
private String vectorLibrary;
/**
*
*/
@ExcelProperty(value = "描述")
private String description;
/**
* (0-1);00.5-0.8
*/
@ExcelProperty(value = "高级配置—模型温度(0-1);值越大回复内容越赋有多样性创造性、随机性设为0根据事实回答希望得到精准答案应该降低该参数日常聊天建议0.5-0.8")
private Long modelTemperature;
/**
* (0-1);AIAI
*/
@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);AI500-800800-20002000-36004000
*/
@ExcelProperty(value = "高级配置—最大回复(1-16000);设置AI最大回复内容大小会影响返回结果内容的长度普通聊天建议500-800短文生成建议800-2000代码生成建议2000-3600长文生成建议4000左右", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "或=选择长回复模型")
private Long maxReply;
/**
* (10)
*/
@ExcelProperty(value = "默认标识(1是0否)")
private String defaultFlag;
/**
* (10)
*/
@ExcelProperty(value = "激活标识(1是0否)")
private String activeFlag;
}

@ -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;
}

@ -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<AiBaseModel, AiBaseModelVo> {
/**
* join apiendpointai_model_capability
*
* @param aiBaseModel
* @return
*/
public List<AiBaseModel> selectAiBaseModelJoinList(AiBaseModelBo aiBaseModel);
}

@ -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<AiChatMessageDetail, AiChatMessageDetailVo> {
}

@ -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<AiChatMessage, AiChatMessageVo> {
}

@ -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;
/**
* AIMapper
*
* @author xins
* @date 2025-08-06
*/
public interface AiKnowledgeBaseTypeMapper extends BaseMapperPlus<AiKnowledgeBaseType, AiKnowledgeBaseTypeVo> {
}

@ -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;
/**
* AIMapper
*
* @author xins
* @date 2025-08-07
*/
public interface AiModelMapper extends BaseMapperPlus<AiModel, AiModelVo> {
@InterceptorIgnore(tenantLine = "true") // leftJoin 的表不拼接租户
List<AiModelVo> selectJoinList(@Param(Constants.WRAPPER) MPJLambdaWrapper<AiModel> wrapper);
}

@ -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<AiModelType, AiModelTypeVo> {
}

@ -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;
/**
* AIMapper
*
* @author xins
* @date 2025-08-06
*/
@InterceptorIgnore(dataPermission = "true", tenantLine = "true")
public interface AiPlatformMapper extends BaseMapperPlus<AiPlatform, AiPlatformVo> {
}

@ -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<String> getAllTableNames();
/**
*
*/
List<Map<String, Object>> getTableStructure(@Param("tableName") String tableName);
/**
*
*/
List<Map<String, Object>> 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<String> getPrimaryKeys(@Param("tableName") String tableName);
}

@ -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;
}

@ -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-chattongyi-qianwentencent-lkewenxin-yiyan)
*/
private String model;
/**
* AIID
*/
private Long modelId;
/**
* ID
*/
private String sessionId;
/**
* LLMEmbedding
*/
private String modelType;
/**
* 10
*/
private String carryHistoryFlag;
/**
*
*/
private List<AIMessage> 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;
}

@ -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;
}
}
}

@ -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);
}
}

@ -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);
}
}

@ -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);
}
}

@ -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<AIProviderEnum, IUnifiedAIProviderProcessor> processorMap;
@Autowired
public AIProviderProcessorFactory(List<IUnifiedAIProviderProcessor> 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));
}
/**
* providerAPI
*/
public IUnifiedAIProviderProcessor getProcessor(String providerName) {
AIProviderEnum provider = AIProviderEnum.fromString(providerName);
return getProcessor(provider);
}
}

@ -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<AIResponse> chatTest(AIRequest request);
/**
*
* @param request
* @return Mono
*/
Mono<AIResponse> chat(AIRequest request);
/**
*
* @param request
* @return Flux
*/
Flux<String> chatStream(AIRequest request, LoginUser loginUser);
/**
*
* @return
*/
AIProviderEnum supportedProvider();
public List<Double> getEmbeddingTest(AIRequest aiRequest);
/**
*
* @param text
* @return Double
* @throws RuntimeException
*/
public List<Double> getEmbedding(AIRequest aiRequest);
/**
*
* @param texts
* @return Double
* @throws RuntimeException
*/
public List<List<Double>> getEmbeddings(AIRequest aiRequest);
}

@ -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<AIResponse> 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;
/**
* SSEJSON
*/
protected Flux<String> extractJsonChunks(String rawChunk) {
// 简单实现假设每个事件都是完整的JSON
// return Flux.just(data);
System.out.println("RawChunk: " + rawChunk);
// 从原始块中提取完整的JSON对象
Matcher matcher = JSON_PATTERN.matcher(rawChunk);
Flux<String> chunks = Flux.empty();
while (matcher.find()) {
chunks = chunks.concatWithValues(matcher.group());
}
return chunks;
}
/**
* http()
* @param url
* @param requestBody
* @param apiKey
* @return AIResponse
*/
public Mono<AIResponse> 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<String> 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<String> 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());
// }
}

@ -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<AIResponse> 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<AIResponse> 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<String> 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<Double> getEmbeddingTest(AIRequest aiRequest) {
return List.of();
}
@Override
public List<Double> getEmbedding(AIRequest aiRequest) {
return List.of();
}
@Override
public List<List<Double>> getEmbeddings(AIRequest aiRequest) {
return List.of();
}
private String extractContentRaw(String jsonStr) {
// 简单的正则提取,作为最后手段
Pattern pattern = Pattern.compile("\"content\":\"(.*?)(?<!\\\\)\"");
Matcher matcher = pattern.matcher(jsonStr);
if (matcher.find()) {
return matcher.group(1)
.replace("\\\"", "\"")
.replace("\\n", "\n");
}
return "";
}
private void setApiKey(AIRequest request) {
AiModelVo aiModelVo = aiModelMapper.selectVoById(request.getModelId());
if (aiModelVo != null) {
if (aiModelVo.getApiKey() == null) {
throw new RuntimeException("请设置AI模型的API Key");
}
request.setApiKey(EncryptUtils.decryptByBase64(aiModelVo.getApiKey()));
}
}
private void saveChatMessage(AIRequest request, String fullResponse, LoginUser loginUser) {
String sessionId = request.getSessionId();
AiChatMessage aiChatMessage = aiChatMessageMapper
.selectOne(new LambdaQueryWrapper<AiChatMessage>()
.eq(AiChatMessage::getSessionId, sessionId));
List<AIMessage> 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());
}
}

@ -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);
}

@ -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<AiBaseModelVo> queryPageList(AiBaseModelBo bo, PageQuery pageQuery);
/**
*
*
* @param bo
* @return
*/
List<AiBaseModelVo> 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<Long> ids, Boolean isValid);
/**
*
*
* @param queryBaseModel
* @return
*/
public List<AiBaseModel> queryJoinList(AiBaseModelBo queryBaseModel);
}

@ -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<AiChatMessageDetailVo> queryPageList(AiChatMessageDetailBo bo, PageQuery pageQuery);
/**
*
*
* @param bo
* @return
*/
List<AiChatMessageDetailVo> 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<Long> ids, Boolean isValid);
/**
* sessionId
* @param sessionId
* @return LIST<AIMessage></AIMessage>
*/
public List<AIMessage> getAIChatMessages(String sessionId);
}

@ -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<AiChatMessageVo> queryPageList(AiChatMessageBo bo, PageQuery pageQuery);
/**
*
*
* @param bo
* @return
*/
List<AiChatMessageVo> 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<Long> 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);
}

@ -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;
/**
* AIService
*
* @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<AiKnowledgeBaseTypeVo> queryPageList(AiKnowledgeBaseTypeBo bo, PageQuery pageQuery);
/**
* AI
*
* @param bo
* @return AI
*/
List<AiKnowledgeBaseTypeVo> 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<Long> ids, Boolean isValid);
}

@ -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;
/**
* AIService
*
* @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<AiModelVo> queryPageList(AiModelBo bo, PageQuery pageQuery);
/**
* AI
*
* @param bo
* @return AI
*/
List<AiModelVo> queryList(AiModelBo bo);
/**
* AI,Join basemodel,modeltype,platform
*
* @param bo
* @return AI
*/
public List<AiModelVo> 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<Long> ids, Boolean isValid);
}

@ -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;
/**
* AIService
*
* @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<AiPlatformVo> queryPageList(AiPlatformBo bo, PageQuery pageQuery);
/**
* AI
*
* @param bo
* @return AI
*/
List<AiPlatformVo> 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<Long> ids, Boolean isValid);
}

@ -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<String, IUnifiedAIProviderProcessor> aiProviderProcessorMap;
@Autowired
private Map<String, IEmbeddingProcessor> 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;
}
}

@ -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<AiBaseModelVo> queryPageList(AiBaseModelBo bo, PageQuery pageQuery) {
MPJLambdaWrapper<AiBaseModel> lqw = buildQueryWrapper(bo);
Page<AiBaseModelVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
*
*
* @param bo
* @return
*/
@Override
public List<AiBaseModelVo> queryList(AiBaseModelBo bo) {
MPJLambdaWrapper<AiBaseModel> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private MPJLambdaWrapper<AiBaseModel> buildQueryWrapper(AiBaseModelBo bo) {
MPJLambdaWrapper<AiBaseModel> 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<Long> ids, Boolean isValid) {
if(isValid){
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
/**
*
*
* @param queryBaseModel
* @return
*/
@Override
public List<AiBaseModel> queryJoinList(AiBaseModelBo queryBaseModel) {
List<AiBaseModel> aiBaseModelList = baseMapper.selectAiBaseModelJoinList(queryBaseModel);
return aiBaseModelList;
}
}

@ -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<AiChatMessageDetailVo> queryPageList(AiChatMessageDetailBo bo, PageQuery pageQuery) {
MPJLambdaWrapper<AiChatMessageDetail> lqw = buildQueryWrapper(bo);
Page<AiChatMessageDetailVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
*
*
* @param bo
* @return
*/
@Override
public List<AiChatMessageDetailVo> queryList(AiChatMessageDetailBo bo) {
MPJLambdaWrapper<AiChatMessageDetail> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private MPJLambdaWrapper<AiChatMessageDetail> buildQueryWrapper(AiChatMessageDetailBo bo) {
Map<String, Object> params = bo.getParams();
MPJLambdaWrapper<AiChatMessageDetail> 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<Long> ids, Boolean isValid) {
if (isValid) {
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
/**
* sessionId
* @param sessionId
* @return LIST<AIMessage></AIMessage>
*/
@Override
public List<AIMessage> getAIChatMessages(String sessionId) {
AiChatMessageDetailBo bo = new AiChatMessageDetailBo();
bo.setSessionId(sessionId);
MPJLambdaWrapper<AiChatMessageDetail> lqw = buildQueryWrapper(bo);
List<AiChatMessageDetail> aiChatMessageDetails = baseMapper.selectList(lqw);
List<AIMessage> 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;
}
}

@ -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<AiChatMessageVo> queryPageList(AiChatMessageBo bo, PageQuery pageQuery) {
MPJLambdaWrapper<AiChatMessage> lqw = buildQueryWrapper(bo);
Page<AiChatMessageVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
*
*
* @param bo
* @return
*/
@Override
public List<AiChatMessageVo> queryList(AiChatMessageBo bo) {
MPJLambdaWrapper<AiChatMessage> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private MPJLambdaWrapper<AiChatMessage> buildQueryWrapper(AiChatMessageBo bo) {
Map<String, Object> params = bo.getParams();
MPJLambdaWrapper<AiChatMessage> 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<Long> ids, Boolean isValid) {
if (isValid) {
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
/**
*
*
* @param bo
* @return
*/
@Override
public Boolean updateMessageTopic(AiChatMessageTopicBo bo) {
UpdateWrapper<AiChatMessage> 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.<AiChatMessageDetail>lambdaQuery()
.eq(AiChatMessageDetail::getSessionId, sessionId));
return baseMapper.delete(Wrappers.<AiChatMessage>lambdaQuery()
.eq(AiChatMessage::getSessionId, sessionId)) > 0;
}
}

@ -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;
/**
* AIService
*
* @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<AiKnowledgeBaseTypeVo> queryPageList(AiKnowledgeBaseTypeBo bo, PageQuery pageQuery) {
MPJLambdaWrapper<AiKnowledgeBaseType> lqw = buildQueryWrapper(bo);
Page<AiKnowledgeBaseTypeVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* AI
*
* @param bo
* @return AI
*/
@Override
public List<AiKnowledgeBaseTypeVo> queryList(AiKnowledgeBaseTypeBo bo) {
MPJLambdaWrapper<AiKnowledgeBaseType> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private MPJLambdaWrapper<AiKnowledgeBaseType> buildQueryWrapper(AiKnowledgeBaseTypeBo bo) {
Map<String, Object> params = bo.getParams();
MPJLambdaWrapper<AiKnowledgeBaseType> 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<Long> ids, Boolean isValid) {
if (isValid) {
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
}

@ -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;
/**
* AIService
*
* @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<AiModelVo> queryPageList(AiModelBo bo, PageQuery pageQuery) {
MPJLambdaWrapper<AiModel> lqw = buildQueryWrapper(bo);
Page<AiModelVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* AI
*
* @param bo
* @return AI
*/
@Override
public List<AiModelVo> queryList(AiModelBo bo) {
MPJLambdaWrapper<AiModel> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
/**
* AI,Join basemodel,modeltype,platform
*
* @param bo
* @return AI
*/
@Override
public List<AiModelVo> queryJoinList(AiModelBo bo) {
MPJLambdaWrapper<AiModel> lqw = buildJoinQueryWrapper(bo);
return baseMapper.selectJoinList(lqw);
}
private MPJLambdaWrapper<AiModel> buildQueryWrapper(AiModelBo bo) {
Map<String, Object> params = bo.getParams();
MPJLambdaWrapper<AiModel> 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<AiModel> buildJoinQueryWrapper(AiModelBo bo) {
Map<String, Object> params = bo.getParams();
MPJLambdaWrapper<AiModel> 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<Long> ids, Boolean isValid) {
if (isValid) {
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
}

@ -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;
/**
* AIService
*
* @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<AiPlatformVo> queryPageList(AiPlatformBo bo, PageQuery pageQuery) {
MPJLambdaWrapper<AiPlatform> lqw = buildQueryWrapper(bo);
Page<AiPlatformVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
* AI
*
* @param bo
* @return AI
*/
@Override
public List<AiPlatformVo> queryList(AiPlatformBo bo) {
MPJLambdaWrapper<AiPlatform> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private MPJLambdaWrapper<AiPlatform> buildQueryWrapper(AiPlatformBo bo) {
Map<String, Object> params = bo.getParams();
MPJLambdaWrapper<AiPlatform> 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<Long> ids, Boolean isValid) {
if(isValid){
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
}

@ -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<Map<String, Object>> allColumns = databaseMetaMapper.getAllTablesStructure();
Map<String, List<Map<String, Object>>> 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<String, List<Map<String, Object>>> entry : tables.entrySet()) {
String tableName = entry.getKey();
List<String> primaryKeys = databaseMetaMapper.getPrimaryKeys(tableName);
sb.append("- 表名: ").append(tableName).append("\n");
sb.append(" 主键: ").append(String.join(", ", primaryKeys)).append("\n");
sb.append(" 字段:\n");
for (Map<String, Object> 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<String, Object> getTableDetail(String tableName) {
Map<String, Object> 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<String> 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<String, Object> 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<Map<String, Object>> entity = new HttpEntity<>(request, headers);
//
// try {
// ResponseEntity<Map> 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;
}
}

@ -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

@ -0,0 +1,7 @@
Spring Boot Version: ${spring-boot.version}
Spring Application Name: ${spring.application.name}
__
/ /_ _ ______ ___ ____ ____ ___ ____ ___ ___ _____
/ __ \ | /| / / __ `__ \/ __ \/ __ `__ \______/ __ `__ \/ _ \/ ___/
/ / / / |/ |/ / / / / / / /_/ / / / / / /_____/ / / / / / __(__ )
/_/ /_/|__/|__/_/ /_/ /_/\____/_/ /_/ /_/ /_/ /_/ /_/\___/____/

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<!-- 日志存放路径 -->
<property name="log.path" value="logs/${project.artifactId}" />
<!-- 日志输出格式 -->
<property name="console.log.pattern"
value="%red(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/>
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${console.log.pattern}</pattern>
<charset>utf-8</charset>
</encoder>
</appender>
<include resource="logback-common.xml" />
<include resource="logback-logstash.xml" />
<!-- 开启 skywalking 日志收集 -->
<include resource="logback-skylog.xml" />
<!--系统操作日志-->
<root level="info">
<appender-ref ref="console" />
</root>
</configuration>

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.ai.mapper.AiBaseModelMapper">
<resultMap type="AiBaseModel" id="AiBaseModelResult">
<result property="baseModelId" column="base_model_id" />
<result property="baseModelName" column="base_model_name" />
<result property="modelTypeId" column="model_type_id" />
<result property="modelTypeCode" column="model_type_code" />
<result property="contextLength" column="context_length" />
<result property="activeFlag" column="active_flag" />
<result property="createTime" column="create_time" />
<result property="updateTime" column="update_time" />
<result property="baseModelDescription" column="base_model_description" />
<result property="modelTypeName" column="model_type_name" />
</resultMap>
<resultMap id="AiBaseModelApiEndpointCapabilityResult" type="AiBaseModel"
extends="AiBaseModelResult">
<collection property="aiApiEndpointList" notNullColumn="sub_point_base_model_id" javaType="java.util.List"
resultMap="AiApiEndpointResult"/>
<collection property="aiModelCapabilityList" notNullColumn="sub_capability_base_model_id" javaType="java.util.List"
resultMap="AiModelCapabilityResult"/>
</resultMap>
<resultMap type="AiApiEndpoint" id="AiApiEndpointResult">
<result property="apiEndpointId" column="sub_api_endpoint_id"/>
<result property="baseModelId" column="sub_point_base_model_id"/>
<result property="apiEndpointName" column="sub_api_endpoint_name"/>
<result property="apiVersion" column="sub_api_version"/>
<result property="httpMethod" column="sub_http_method"/>
<result property="apiEndpointPath" column="sub_api_endpoint_path"/>
<result property="defaultFlag" column="sub_default_flag"/>
<result property="rateLimit" column="sub_rate_limit"/>
</resultMap>
<resultMap type="AiModelCapability" id="AiModelCapabilityResult">
<result property="modelCapabilityId" column="sub_model_capability_id"/>
<result property="baseModelId" column="sub_capability_base_model_id"/>
<result property="modelCapabilityName" column="sub_model_capability_name"/>
<result property="modelCapabilityDescription" column="sub_model_capability_description"/>
</resultMap>
<select id="selectAiBaseModelJoinList" parameterType="AiBaseModel"
resultMap="AiBaseModelApiEndpointCapabilityResult">
select a.base_model_id,
a.base_model_name,
a.model_type_id,
a.context_length,
a.active_flag,
a.base_model_description,
amt.model_type_code,
amt.model_type_name,
b.base_model_id as sub_point_base_model_id,
b.api_endpoint_id as sub_api_endpoint_id,
b.api_endpoint_name as sub_api_endpoint_name,
b.api_version as sub_api_version,
b.http_method as sub_http_method,
b.api_endpoint_path as sub_api_endpoint_path,
b.default_flag as sub_default_flag,
b.rate_limit as sub_rate_limit,
c.model_capability_id as sub_model_capability_id,
c.base_model_id as sub_capability_base_model_id,
c.model_capability_name as sub_model_capability_name,
c.model_capability_description as sub_model_capability_description
from ai_base_model a
left join ai_api_endpoint b on b.base_model_id = a.base_model_id
left join ai_model_capability c on c.base_model_id = a.base_model_id
left join ai_model_type amt on a.model_type_id = amt.model_type_id
<where>
<if test="platformId != null and platformId != ''">and a.platform_id = #{platformId}</if>
</where>
</select>
</mapper>

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.ai.mapper.AiChatMessageDetailMapper">
</mapper>

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.ai.mapper.AiChatMessageMapper">
</mapper>

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.ai.mapper.AiKnowledgeBaseTypeMapper">
</mapper>

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.ai.mapper.AiModelMapper">
<!-- <resultMap type="AiModel" id="AiModelResult">-->
<!-- <result property="modelId" column="model_id" />-->
<!-- <result property="tenantId" column="tenant_id" />-->
<!-- <result property="modelName" column="model_name" />-->
<!-- <result property="platformId" column="platform_id" />-->
<!-- <result property="modelTypeId" column="model_type_id" />-->
<!-- <result property="baseModelId" column="base_model_id" />-->
<!-- <result property="chargeType" column="charge_type" />-->
<!-- <result property="modelPrice" column="model_price"/>-->
<!-- <result property="apiEndpoint" column="api_endpoint" />-->
<!-- <result property="apiKey" column="api_key" />-->
<!-- <result property="apiSecret" column="api_secret" />-->
<!-- <result property="vectorLibrary" column="vector_library" />-->
<!-- <result property="description" column="description" />-->
<!-- <result property="modelTemperature" column="model_temperature" />-->
<!-- <result property="vocabularyAttribute" column="vocabulary_attribute" />-->
<!-- <result property="topicAttribute" column="topic_attribute" />-->
<!-- <result property="duplicateAttribute" column="duplicate_attribute" />-->
<!-- <result property="modelReply" column="model_reply" />-->
<!-- <result property="activeFlag" column="active_flag" />-->
<!-- <result property="createDept" column="create_dept" />-->
<!-- <result property="createBy" column="create_by" />-->
<!-- <result property="createTime" column="create_time" />-->
<!-- <result property="updateBy" column="update_by" />-->
<!-- <result property="updateTime" column="update_time" />-->
<!-- <result property="delFlag" column="del_flag" />-->
<!-- -->
<!-- <result property="platformName" column="platform_name" />-->
<!-- <result property="platformIcon" column="platform_icon" />-->
<!-- <result property="modelTypeName" column="model_type_name" />-->
<!-- <result property="baseModelName" column="base_model_name" />-->
<!-- </resultMap>-->
<!-- <select id="queryJoinList" resultType="org.dromara.ai.domain.vo.AiModel">-->
<!-- select a.*, b.base_model_name, c.platform_name as platform_name, d.model_type_name-->
<!-- from ai_model a-->
<!-- left join ai_base_model b on a.base_model_id = b.id-->
<!-- left join ai_platform c on a.platform_id = c.id-->
<!-- left join ai_model_type d on a.type_id = d.id-->
<!-- </select>-->
</mapper>

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.ai.mapper.AiPlatformMapper">
</mapper>

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.ai.mapper.DatabaseMetaMapper">
<!-- 获取指定表的结构信息 -->
<select id="getTableStructure" resultType="map">
SELECT
c.name AS columnName,
t.name AS dataType,
c.max_length AS maxLength,
c.precision,
c.scale,
c.is_nullable AS nullable,
ISNULL(OBJECT_DEFINITION(c.default_object_id), '') AS defaultValue,
ep.value AS description
FROM sys.columns c
JOIN sys.types t ON c.user_type_id = t.user_type_id
LEFT JOIN sys.extended_properties ep ON
ep.major_id = c.object_id AND
ep.minor_id = c.column_id AND
ep.name = 'MS_Description'
WHERE OBJECT_NAME(c.object_id) = #{tableName}
ORDER BY c.column_id
</select>
<!-- 获取所有表的结构信息 -->
<select id="getAllTablesStructure" resultType="map">
SELECT
OBJECT_NAME(c.object_id) AS tableName,
c.name AS columnName,
t.name AS dataType,
c.max_length AS maxLength,
c.precision,
c.scale,
c.is_nullable AS nullable,
ISNULL(OBJECT_DEFINITION(c.default_object_id), '') AS defaultValue,
ISNULL(ep.value, '') AS description
FROM sys.columns c
JOIN sys.types t ON c.user_type_id = t.user_type_id
LEFT JOIN sys.extended_properties ep ON
ep.major_id = c.object_id AND
ep.minor_id = c.column_id AND
ep.name = 'MS_Description'
WHERE OBJECT_NAME(c.object_id) IN (SELECT name FROM hwmom.sys.tables where name='sys_user')
ORDER BY tableName, c.column_id
</select>
</mapper>

@ -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<Void> removeOutstockDetailId(@PathVariable Long outstockDetailId) {
return toAjax(wmsOutstockDetailService.deleteById(outstockDetailId)>0);
}
/**
* -
*/
@GetMapping("/getOutstockDetailList")
public R<List<WmsOutstockDetailVo>> getOutstockDetailList(WmsOutstockDetailBo bo) {
return R.ok(wmsOutstockDetailService.queryList(bo));
}
}

@ -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<List<WmsOutstockOrderVo>> getOutstockOrderList(WmsOutstockOrderBo bo) {
return R.ok(wmsOutstockOrderService.queryList(bo));
}
}

@ -114,4 +114,10 @@ public class WmsOutstockDetail {
@TableField(exist = false)
private String materialCategoryName;//字段映射
/**
* (0,1)
*/
@TableField(exist = false)
private String isHighValue;//字段映射
}

@ -114,4 +114,8 @@ public class WmsOutstockDetailVo implements Serializable {
*/
private String materialCategoryName;//字段映射
/**
* (0,1)
*/
private String isHighValue;//字段映射
}

@ -83,10 +83,10 @@ public interface IWmsInventoryService {
TableDataInfo<WmsInventoryVo> listInventoryAlarm(WmsInventoryBo bo, PageQuery pageQuery);
/**
*
*
* @param batchCode
* @param locationCode
* @return VO
*/
WmsInventoryVo queryByBatchAndLocation(String batchCode, String locationCode);
WmsInventoryVo queryByBatchAndLocation(String batchCode);
}

@ -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.
*/
/**
*

@ -87,6 +87,9 @@ public class WmsOutstockDetailServiceImpl implements IWmsOutstockDetailService {
MPJLambdaWrapper<WmsOutstockDetail> 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)

@ -23,6 +23,7 @@
<module>hwmom-dp</module>
<module>hwmom-tsdb</module>
<module>hwmom-workflow</module>
<module>hwmom-ai</module>
<!-- <module>hwmom-test</module>-->
</modules>

Loading…
Cancel
Save