初始化代码
|
Before Width: | Height: | Size: 112 KiB |
BIN
.image/MySQL.jpg
|
Before Width: | Height: | Size: 142 KiB |
|
Before Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 65 KiB |
BIN
.image/Redis.jpg
|
Before Width: | Height: | Size: 95 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 348 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 46 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 201 KiB |
|
Before Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 204 KiB |
|
Before Width: | Height: | Size: 139 KiB |
|
Before Width: | Height: | Size: 69 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 13 KiB |
BIN
.image/个人中心.jpg
|
Before Width: | Height: | Size: 37 KiB |
BIN
.image/代码生成.jpg
|
Before Width: | Height: | Size: 130 KiB |
BIN
.image/令牌管理.jpg
|
Before Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 89 KiB |
|
Before Width: | Height: | Size: 160 KiB |
|
Before Width: | Height: | Size: 150 KiB |
BIN
.image/任务日志.jpg
|
Before Width: | Height: | Size: 124 KiB |
BIN
.image/商户信息.jpg
|
Before Width: | Height: | Size: 83 KiB |
BIN
.image/在线用户.jpg
|
Before Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 109 KiB |
|
Before Width: | Height: | Size: 69 KiB |
BIN
.image/字典数据.jpg
|
Before Width: | Height: | Size: 78 KiB |
BIN
.image/字典类型.jpg
|
Before Width: | Height: | Size: 126 KiB |
BIN
.image/定时任务.jpg
|
Before Width: | Height: | Size: 74 KiB |
BIN
.image/岗位管理.jpg
|
Before Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 177 KiB |
|
Before Width: | Height: | Size: 126 KiB |
|
Before Width: | Height: | Size: 93 KiB |
|
Before Width: | Height: | Size: 93 KiB |
BIN
.image/应用管理.jpg
|
Before Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 176 KiB |
|
Before Width: | Height: | Size: 91 KiB |
|
Before Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 41 KiB |
BIN
.image/操作日志.jpg
|
Before Width: | Height: | Size: 98 KiB |
BIN
.image/支付订单.jpg
|
Before Width: | Height: | Size: 208 KiB |
BIN
.image/敏感词.jpg
|
Before Width: | Height: | Size: 47 KiB |
BIN
.image/数据库文档.jpg
|
Before Width: | Height: | Size: 97 KiB |
BIN
.image/文件管理.jpg
|
Before Width: | Height: | Size: 74 KiB |
BIN
.image/文件管理2.jpg
|
Before Width: | Height: | Size: 114 KiB |
BIN
.image/文件配置.jpg
|
Before Width: | Height: | Size: 116 KiB |
BIN
.image/日志中心.jpg
|
Before Width: | Height: | Size: 84 KiB |
|
Before Width: | Height: | Size: 173 KiB |
|
Before Width: | Height: | Size: 125 KiB |
|
Before Width: | Height: | Size: 125 KiB |
BIN
.image/流程表单.jpg
|
Before Width: | Height: | Size: 148 KiB |
BIN
.image/生成效果.jpg
|
Before Width: | Height: | Size: 128 KiB |
BIN
.image/用户分组.jpg
|
Before Width: | Height: | Size: 82 KiB |
BIN
.image/用户管理.jpg
|
Before Width: | Height: | Size: 112 KiB |
BIN
.image/登录.jpg
|
Before Width: | Height: | Size: 93 KiB |
BIN
.image/登录日志.jpg
|
Before Width: | Height: | Size: 77 KiB |
BIN
.image/短信日志.jpg
|
Before Width: | Height: | Size: 41 KiB |
BIN
.image/短信模板.jpg
|
Before Width: | Height: | Size: 248 KiB |
BIN
.image/短信渠道.jpg
|
Before Width: | Height: | Size: 80 KiB |
BIN
.image/租户套餐.png
|
Before Width: | Height: | Size: 96 KiB |
BIN
.image/租户管理.jpg
|
Before Width: | Height: | Size: 115 KiB |
BIN
.image/系统接口.jpg
|
Before Width: | Height: | Size: 91 KiB |
BIN
.image/菜单管理.jpg
|
Before Width: | Height: | Size: 129 KiB |
BIN
.image/表单构建.jpg
|
Before Width: | Height: | Size: 102 KiB |
BIN
.image/角色管理.jpg
|
Before Width: | Height: | Size: 85 KiB |
BIN
.image/访问日志.jpg
|
Before Width: | Height: | Size: 73 KiB |
BIN
.image/退款订单.jpg
|
Before Width: | Height: | Size: 92 KiB |
BIN
.image/通知公告.jpg
|
Before Width: | Height: | Size: 77 KiB |
BIN
.image/部门管理.jpg
|
Before Width: | Height: | Size: 109 KiB |
BIN
.image/配置管理.jpg
|
Before Width: | Height: | Size: 89 KiB |
BIN
.image/链路追踪.jpg
|
Before Width: | Height: | Size: 80 KiB |
BIN
.image/错误日志.jpg
|
Before Width: | Height: | Size: 110 KiB |
BIN
.image/错误码管理.jpg
|
Before Width: | Height: | Size: 131 KiB |
BIN
.image/首页.jpg
|
Before Width: | Height: | Size: 62 KiB |
@@ -1,263 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>lyzsys</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<packaging>jar</packaging>
|
||||
<artifactId>lyzsys-module-ai</artifactId>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>
|
||||
ai 模块下,接入 LLM 大模型,支持聊天、绘图、音乐、写作、思维导图等功能。
|
||||
目前已接入各种模型,不限于:
|
||||
国内:通义千问、文心一言、讯飞星火、智谱 GLM、DeepSeek
|
||||
国外:OpenAI、Ollama、Midjourney、StableDiffusion、Suno
|
||||
</description>
|
||||
<properties>
|
||||
<spring-ai.version>1.1.2</spring-ai.version>
|
||||
<!-- https://mvnrepository.com/artifact/com.alibaba.cloud.ai/spring-ai-alibaba -->
|
||||
<alibaba-ai.version>1.1.0.0-RC2</alibaba-ai.version>
|
||||
<tinyflow.version>1.2.6</tinyflow.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>lyzsys-module-system</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>lyzsys-module-infra</artifactId>
|
||||
<version>${revision}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 业务组件 -->
|
||||
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>lyzsys-spring-boot-starter-biz-tenant</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Web 相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>lyzsys-spring-boot-starter-security</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- DB 相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>lyzsys-spring-boot-starter-mybatis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Job 相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>lyzsys-spring-boot-starter-job</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Test 测试相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>lyzsys-spring-boot-starter-test</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 工具类相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>lyzsys-spring-boot-starter-excel</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring AI Model 模型接入 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-starter-model-openai</artifactId>
|
||||
<version>${spring-ai.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-annotations-jakarta</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-starter-model-azure-openai</artifactId>
|
||||
<version>${spring-ai.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-starter-model-anthropic</artifactId>
|
||||
<version>${spring-ai.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-starter-model-deepseek</artifactId>
|
||||
<version>${spring-ai.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-starter-model-ollama</artifactId>
|
||||
<version>${spring-ai.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-starter-model-stability-ai</artifactId>
|
||||
<version>${spring-ai.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<!-- 智谱 GLM -->
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-starter-model-zhipuai</artifactId>
|
||||
<version>${spring-ai.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-starter-model-minimax</artifactId>
|
||||
<version>${spring-ai.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<!-- 通义千问 -->
|
||||
<groupId>com.alibaba.cloud.ai</groupId>
|
||||
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
|
||||
<version>${alibaba-ai.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<!-- 文心一言 -->
|
||||
<groupId>org.springaicommunity</groupId>
|
||||
<artifactId>qianfan-spring-boot-starter</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<!-- 月之暗面 -->
|
||||
<groupId>org.springaicommunity</groupId>
|
||||
<artifactId>moonshot-spring-boot-starter</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 向量存储:https://db-engines.com/en/ranking/vector+dbms -->
|
||||
<dependency>
|
||||
<!-- Qdrant:https://qdrant.tech/ -->
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-starter-vector-store-qdrant</artifactId>
|
||||
<version>${spring-ai.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<!-- Redis:https://redis.io/docs/latest/develop/get-started/vector-database/ -->
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-starter-vector-store-redis</artifactId>
|
||||
<version>${spring-ai.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.boot</groupId>
|
||||
<artifactId>lyzsys-spring-boot-starter-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<!-- Milvus:https://milvus.io/ -->
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-starter-vector-store-milvus</artifactId>
|
||||
<version>${spring-ai.version}</version>
|
||||
<exclusions>
|
||||
<!-- 解决和 logback 的日志冲突 -->
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-reload4j</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<!-- Tika:负责内容的解析 -->
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-tika-document-reader</artifactId>
|
||||
<version>${spring-ai.version}</version>
|
||||
<!-- TODO 芋艿:boot 项目里,不引入 cloud 依赖!!!另外,这样也是为了解决启动报错的问题! -->
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>spring-cloud-function-context</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>spring-cloud-function-core</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- MCP 相关 -->
|
||||
<!--
|
||||
特殊说明:不能使用 spring-ai-starter-mcp-server-webflux 或 spring-ai-starter-mcp-client-webflux !!!
|
||||
原因:项目使用了 SpringMVC,而不是 WebFlux。引入上述 2 个,会导致 SSE Server 失效。
|
||||
-->
|
||||
<dependency>
|
||||
<!-- 服务端 -->
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-starter-mcp-server-webmvc</artifactId>
|
||||
<version>${spring-ai.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>io.swagger.core.v3</groupId>
|
||||
<artifactId>swagger-annotations-jakarta</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<!-- 客户端 -->
|
||||
<groupId>org.springframework.ai</groupId>
|
||||
<artifactId>spring-ai-starter-mcp-client</artifactId>
|
||||
<version>${spring-ai.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- TinyFlow:AI 工作流 -->
|
||||
<dependency>
|
||||
<groupId>dev.tinyflow</groupId>
|
||||
<artifactId>tinyflow-java-core</artifactId>
|
||||
<version>${tinyflow.version}</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.jfinal</groupId>
|
||||
<artifactId>enjoy</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<!-- 解决 https://gitee.com/zhijiantianya/ruoyi-vue-pro/pulls/1318/ 问题 -->
|
||||
<groupId>com.agentsflex</groupId>
|
||||
<artifactId>agents-flex-store-elasticsearch</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<!-- 解决 https://t.zsxq.com/pCBZC 问题 -->
|
||||
<groupId>com.agentsflex</groupId>
|
||||
<artifactId>agents-flex-search-engine-es</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<!-- TODO @芋艿:暂时移除 groovy,和 iot 冲突 -->
|
||||
<groupId>org.codehaus.groovy</groupId>
|
||||
<artifactId>groovy-all</artifactId>
|
||||
</exclusion>
|
||||
<!-- 解决和 logback 的日志冲突 -->
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-simple</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-slf4j-impl</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-reload4j</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
@@ -1,118 +0,0 @@
|
||||
package cn.iocoder.lyzsys.module.ai.controller.admin.chat;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.lyzsys.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.lyzsys.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.lyzsys.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.lyzsys.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO;
|
||||
import cn.iocoder.lyzsys.module.ai.controller.admin.chat.vo.conversation.AiChatConversationPageReqVO;
|
||||
import cn.iocoder.lyzsys.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO;
|
||||
import cn.iocoder.lyzsys.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateMyReqVO;
|
||||
import cn.iocoder.lyzsys.module.ai.dal.dataobject.chat.AiChatConversationDO;
|
||||
import cn.iocoder.lyzsys.module.ai.service.chat.AiChatConversationService;
|
||||
import cn.iocoder.lyzsys.module.ai.service.chat.AiChatMessageService;
|
||||
import com.fhs.core.trans.anno.TransMethodResult;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.lyzsys.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.lyzsys.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.lyzsys.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - AI 聊天对话")
|
||||
@RestController
|
||||
@RequestMapping("/ai/chat/conversation")
|
||||
@Validated
|
||||
public class AiChatConversationController {
|
||||
|
||||
@Resource
|
||||
private AiChatConversationService chatConversationService;
|
||||
@Resource
|
||||
private AiChatMessageService chatMessageService;
|
||||
|
||||
@PostMapping("/create-my")
|
||||
@Operation(summary = "创建【我的】聊天对话")
|
||||
public CommonResult<Long> createChatConversationMy(@RequestBody @Valid AiChatConversationCreateMyReqVO createReqVO) {
|
||||
return success(chatConversationService.createChatConversationMy(createReqVO, getLoginUserId()));
|
||||
}
|
||||
|
||||
@PutMapping("/update-my")
|
||||
@Operation(summary = "更新【我的】聊天对话")
|
||||
public CommonResult<Boolean> updateChatConversationMy(@RequestBody @Valid AiChatConversationUpdateMyReqVO updateReqVO) {
|
||||
chatConversationService.updateChatConversationMy(updateReqVO, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/my-list")
|
||||
@Operation(summary = "获得【我的】聊天对话列表")
|
||||
@TransMethodResult
|
||||
public CommonResult<List<AiChatConversationRespVO>> getChatConversationMyList() {
|
||||
List<AiChatConversationDO> list = chatConversationService.getChatConversationListByUserId(getLoginUserId());
|
||||
return success(BeanUtils.toBean(list, AiChatConversationRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/get-my")
|
||||
@Operation(summary = "获得【我的】聊天对话")
|
||||
@Parameter(name = "id", required = true, description = "对话编号", example = "1024")
|
||||
@TransMethodResult
|
||||
public CommonResult<AiChatConversationRespVO> getChatConversationMy(@RequestParam("id") Long id) {
|
||||
AiChatConversationDO conversation = chatConversationService.getChatConversation(id);
|
||||
if (conversation != null && ObjUtil.notEqual(conversation.getUserId(), getLoginUserId())) {
|
||||
conversation = null;
|
||||
}
|
||||
return success(BeanUtils.toBean(conversation, AiChatConversationRespVO.class));
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-my")
|
||||
@Operation(summary = "删除聊天对话")
|
||||
@Parameter(name = "id", required = true, description = "对话编号", example = "1024")
|
||||
public CommonResult<Boolean> deleteChatConversationMy(@RequestParam("id") Long id) {
|
||||
chatConversationService.deleteChatConversationMy(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-by-unpinned")
|
||||
@Operation(summary = "删除未置顶的聊天对话")
|
||||
public CommonResult<Boolean> deleteChatConversationMyByUnpinned() {
|
||||
chatConversationService.deleteChatConversationMyByUnpinned(getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
// ========== 对话管理 ==========
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得对话分页", description = "用于【对话管理】菜单")
|
||||
@PreAuthorize("@ss.hasPermission('ai:chat-conversation:query')")
|
||||
@TransMethodResult
|
||||
public CommonResult<PageResult<AiChatConversationRespVO>> getChatConversationPage(AiChatConversationPageReqVO pageReqVO) {
|
||||
PageResult<AiChatConversationDO> pageResult = chatConversationService.getChatConversationPage(pageReqVO);
|
||||
if (CollUtil.isEmpty(pageResult.getList())) {
|
||||
return success(PageResult.empty());
|
||||
}
|
||||
// 拼接关联数据
|
||||
Map<Long, Integer> messageCountMap = chatMessageService.getChatMessageCountMap(
|
||||
convertList(pageResult.getList(), AiChatConversationDO::getId));
|
||||
return success(BeanUtils.toBean(pageResult, AiChatConversationRespVO.class,
|
||||
conversation -> conversation.setMessageCount(messageCountMap.getOrDefault(conversation.getId(), 0))));
|
||||
}
|
||||
|
||||
@Operation(summary = "管理员删除对话")
|
||||
@DeleteMapping("/delete-by-admin")
|
||||
@Parameter(name = "id", required = true, description = "对话编号", example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('ai:chat-conversation:delete')")
|
||||
public CommonResult<Boolean> deleteChatConversationByAdmin(@RequestParam("id") Long id) {
|
||||
chatConversationService.deleteChatConversationByAdmin(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
### 发送消息(段式)
|
||||
POST {{baseUrl}}/ai/chat/message/send
|
||||
Content-Type: application/json
|
||||
Authorization: {{token}}
|
||||
tenant-id: {{adminTenantId}}
|
||||
|
||||
{
|
||||
"conversationId": "1781604279872581724",
|
||||
"content": "你是 OpenAI 么?"
|
||||
}
|
||||
|
||||
### 发送消息(流式)
|
||||
POST {{baseUrl}}/ai/chat/message/send-stream
|
||||
Content-Type: application/json
|
||||
Authorization: {{token}}
|
||||
tenant-id: {{adminTenantId}}
|
||||
|
||||
{
|
||||
"conversationId": "1781604279872581724",
|
||||
"content": "1+1=?"
|
||||
}
|
||||
|
||||
### 发送消息(流式)【带文件】
|
||||
POST {{baseUrl}}/ai/chat/message/send-stream
|
||||
Content-Type: application/json
|
||||
Authorization: {{token}}
|
||||
tenant-id: {{adminTenantId}}
|
||||
|
||||
{
|
||||
"conversationId": "1781604279872581797",
|
||||
"content": "图片里有什么?",
|
||||
"attachmentUrls": ["http://test.lyzsys.iocoder.cn/1755531278.jpeg"]
|
||||
}
|
||||
|
||||
### 发送消息(流式)【追问带文件】
|
||||
POST {{baseUrl}}/ai/chat/message/send-stream
|
||||
Content-Type: application/json
|
||||
Authorization: {{token}}
|
||||
tenant-id: {{adminTenantId}}
|
||||
|
||||
{
|
||||
"conversationId": "1781604279872581799",
|
||||
"content": "说下图片里,有哪些字?",
|
||||
"useContext": true
|
||||
}
|
||||
|
||||
### 发送消息(流式)【联网搜索】
|
||||
POST {{baseUrl}}/ai/chat/message/send-stream
|
||||
Content-Type: application/json
|
||||
Authorization: {{token}}
|
||||
tenant-id: {{adminTenantId}}
|
||||
|
||||
{
|
||||
"conversationId": "1781604279872581799",
|
||||
"content": "今天是周几?",
|
||||
"useSearch": true
|
||||
}
|
||||
|
||||
### 获得指定对话的消息列表
|
||||
GET {{baseUrl}}/ai/chat/message/list-by-conversation-id?conversationId=1781604279872581799
|
||||
Authorization: {{token}}
|
||||
tenant-id: {{adminTenantId}}
|
||||
|
||||
### 删除消息
|
||||
DELETE {{baseUrl}}/ai/chat/message/delete?id=50
|
||||
Authorization: {{token}}
|
||||
@@ -1,157 +0,0 @@
|
||||
package cn.iocoder.lyzsys.module.ai.controller.admin.chat;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.lyzsys.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.lyzsys.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.lyzsys.framework.common.util.collection.MapUtils;
|
||||
import cn.iocoder.lyzsys.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.lyzsys.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO;
|
||||
import cn.iocoder.lyzsys.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO;
|
||||
import cn.iocoder.lyzsys.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO;
|
||||
import cn.iocoder.lyzsys.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO;
|
||||
import cn.iocoder.lyzsys.module.ai.dal.dataobject.chat.AiChatConversationDO;
|
||||
import cn.iocoder.lyzsys.module.ai.dal.dataobject.chat.AiChatMessageDO;
|
||||
import cn.iocoder.lyzsys.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO;
|
||||
import cn.iocoder.lyzsys.module.ai.dal.dataobject.knowledge.AiKnowledgeSegmentDO;
|
||||
import cn.iocoder.lyzsys.module.ai.dal.dataobject.model.AiChatRoleDO;
|
||||
import cn.iocoder.lyzsys.module.ai.service.chat.AiChatConversationService;
|
||||
import cn.iocoder.lyzsys.module.ai.service.chat.AiChatMessageService;
|
||||
import cn.iocoder.lyzsys.module.ai.service.knowledge.AiKnowledgeDocumentService;
|
||||
import cn.iocoder.lyzsys.module.ai.service.knowledge.AiKnowledgeSegmentService;
|
||||
import cn.iocoder.lyzsys.module.ai.service.model.AiChatRoleService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.validation.Valid;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.lyzsys.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.lyzsys.framework.common.util.collection.CollectionUtils.*;
|
||||
import static cn.iocoder.lyzsys.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - 聊天消息")
|
||||
@RestController
|
||||
@RequestMapping("/ai/chat/message")
|
||||
@Slf4j
|
||||
public class AiChatMessageController {
|
||||
|
||||
@Resource
|
||||
private AiChatMessageService chatMessageService;
|
||||
@Resource
|
||||
private AiChatConversationService chatConversationService;
|
||||
@Resource
|
||||
private AiChatRoleService chatRoleService;
|
||||
@Resource
|
||||
private AiKnowledgeSegmentService knowledgeSegmentService;
|
||||
@Resource
|
||||
private AiKnowledgeDocumentService knowledgeDocumentService;
|
||||
|
||||
@Operation(summary = "发送消息(段式)", description = "一次性返回,响应较慢")
|
||||
@PostMapping("/send")
|
||||
public CommonResult<AiChatMessageSendRespVO> sendMessage(@Valid @RequestBody AiChatMessageSendReqVO sendReqVO) {
|
||||
return success(chatMessageService.sendMessage(sendReqVO, getLoginUserId()));
|
||||
}
|
||||
|
||||
@Operation(summary = "发送消息(流式)", description = "流式返回,响应较快")
|
||||
@PostMapping(value = "/send-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
|
||||
public Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(@Valid @RequestBody AiChatMessageSendReqVO sendReqVO) {
|
||||
return chatMessageService.sendChatMessageStream(sendReqVO, getLoginUserId());
|
||||
}
|
||||
|
||||
@Operation(summary = "获得指定对话的消息列表")
|
||||
@GetMapping("/list-by-conversation-id")
|
||||
@Parameter(name = "conversationId", required = true, description = "对话编号", example = "1024")
|
||||
public CommonResult<List<AiChatMessageRespVO>> getChatMessageListByConversationId(
|
||||
@RequestParam("conversationId") Long conversationId) {
|
||||
AiChatConversationDO conversation = chatConversationService.getChatConversation(conversationId);
|
||||
if (conversation == null || ObjUtil.notEqual(conversation.getUserId(), getLoginUserId())) {
|
||||
return success(Collections.emptyList());
|
||||
}
|
||||
// 1. 获取消息列表
|
||||
List<AiChatMessageDO> messageList = chatMessageService.getChatMessageListByConversationId(conversationId);
|
||||
if (CollUtil.isEmpty(messageList)) {
|
||||
return success(Collections.emptyList());
|
||||
}
|
||||
|
||||
// 2. 拼接数据,主要是知识库段落信息
|
||||
Map<Long, AiKnowledgeSegmentDO> segmentMap = knowledgeSegmentService.getKnowledgeSegmentMap(convertListByFlatMap(messageList,
|
||||
message -> CollUtil.isEmpty(message.getSegmentIds()) ? null : message.getSegmentIds().stream()));
|
||||
Map<Long, AiKnowledgeDocumentDO> documentMap = knowledgeDocumentService.getKnowledgeDocumentMap(
|
||||
convertList(segmentMap.values(), AiKnowledgeSegmentDO::getDocumentId));
|
||||
List<AiChatMessageRespVO> messageVOList = BeanUtils.toBean(messageList, AiChatMessageRespVO.class);
|
||||
for (int i = 0; i < messageList.size(); i++) {
|
||||
AiChatMessageDO message = messageList.get(i);
|
||||
if (CollUtil.isEmpty(message.getSegmentIds())) {
|
||||
continue;
|
||||
}
|
||||
// 设置知识库段落信息
|
||||
messageVOList.get(i).setSegments(convertList(message.getSegmentIds(), segmentId -> {
|
||||
AiKnowledgeSegmentDO segment = segmentMap.get(segmentId);
|
||||
if (segment == null) {
|
||||
return null;
|
||||
}
|
||||
AiKnowledgeDocumentDO document = documentMap.get(segment.getDocumentId());
|
||||
if (document == null) {
|
||||
return null;
|
||||
}
|
||||
return new AiChatMessageRespVO.KnowledgeSegment().setId(segment.getId()).setContent(segment.getContent())
|
||||
.setDocumentId(segment.getDocumentId()).setDocumentName(document.getName());
|
||||
}));
|
||||
}
|
||||
return success(messageVOList);
|
||||
}
|
||||
|
||||
@Operation(summary = "删除消息")
|
||||
@DeleteMapping("/delete")
|
||||
@Parameter(name = "id", required = true, description = "消息编号", example = "1024")
|
||||
public CommonResult<Boolean> deleteChatMessage(@RequestParam("id") Long id) {
|
||||
chatMessageService.deleteChatMessage(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@Operation(summary = "删除指定对话的消息")
|
||||
@DeleteMapping("/delete-by-conversation-id")
|
||||
@Parameter(name = "conversationId", required = true, description = "对话编号", example = "1024")
|
||||
public CommonResult<Boolean> deleteChatMessageByConversationId(@RequestParam("conversationId") Long conversationId) {
|
||||
chatMessageService.deleteChatMessageByConversationId(conversationId, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
// ========== 对话管理 ==========
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得消息分页", description = "用于【对话管理】菜单")
|
||||
@PreAuthorize("@ss.hasPermission('ai:chat-conversation:query')")
|
||||
public CommonResult<PageResult<AiChatMessageRespVO>> getChatMessagePage(AiChatMessagePageReqVO pageReqVO) {
|
||||
PageResult<AiChatMessageDO> pageResult = chatMessageService.getChatMessagePage(pageReqVO);
|
||||
if (CollUtil.isEmpty(pageResult.getList())) {
|
||||
return success(PageResult.empty());
|
||||
}
|
||||
// 拼接数据
|
||||
Map<Long, AiChatRoleDO> roleMap = chatRoleService.getChatRoleMap(
|
||||
convertSet(pageResult.getList(), AiChatMessageDO::getRoleId));
|
||||
return success(BeanUtils.toBean(pageResult, AiChatMessageRespVO.class,
|
||||
respVO -> MapUtils.findAndThen(roleMap, respVO.getRoleId(),
|
||||
role -> respVO.setRoleName(role.getName()))));
|
||||
}
|
||||
|
||||
@Operation(summary = "管理员删除消息")
|
||||
@DeleteMapping("/delete-by-admin")
|
||||
@Parameter(name = "id", required = true, description = "消息编号", example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('ai:chat-message:delete')")
|
||||
public CommonResult<Boolean> deleteChatMessageByAdmin(@RequestParam("id") Long id) {
|
||||
chatMessageService.deleteChatMessageByAdmin(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package cn.iocoder.lyzsys.module.ai.controller.admin.chat.vo.conversation;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - AI 聊天对话创建【我的】 Request VO")
|
||||
@Data
|
||||
public class AiChatConversationCreateMyReqVO {
|
||||
|
||||
@Schema(description = "聊天角色编号", example = "666")
|
||||
private Long roleId;
|
||||
|
||||
@Schema(description = "知识库编号", example = "1204")
|
||||
private Long knowledgeId;
|
||||
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
package cn.iocoder.lyzsys.module.ai.controller.admin.chat.vo.conversation;
|
||||
|
||||
import cn.iocoder.lyzsys.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.lyzsys.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - AI 聊天对话的分页 Request VO")
|
||||
@Data
|
||||
public class AiChatConversationPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "用户编号", example = "1024")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "对话标题", example = "你好")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
package cn.iocoder.lyzsys.module.ai.controller.admin.chat.vo.conversation;
|
||||
|
||||
import cn.iocoder.lyzsys.module.ai.dal.dataobject.model.AiModelDO;
|
||||
import cn.iocoder.lyzsys.module.ai.dal.dataobject.model.AiChatRoleDO;
|
||||
import com.fhs.core.trans.anno.Trans;
|
||||
import com.fhs.core.trans.constant.TransType;
|
||||
import com.fhs.core.trans.vo.VO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - AI 聊天对话 Response VO")
|
||||
@Data
|
||||
public class AiChatConversationRespVO implements VO {
|
||||
|
||||
@Schema(description = "对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "对话标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "我是一个标题")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "是否置顶", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
private Boolean pinned;
|
||||
|
||||
@Schema(description = "角色编号", example = "1")
|
||||
@Trans(type = TransType.SIMPLE, target = AiChatRoleDO.class, fields = {"name", "avatar"}, refs = {"roleName", "roleAvatar"})
|
||||
private Long roleId;
|
||||
|
||||
@Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@Trans(type = TransType.SIMPLE, target = AiModelDO.class, fields = "name", ref = "modelName")
|
||||
private Long modelId;
|
||||
|
||||
@Schema(description = "模型标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "ERNIE-Bot-turbo-0922")
|
||||
private String model;
|
||||
|
||||
@Schema(description = "模型名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
|
||||
private String modelName;
|
||||
|
||||
@Schema(description = "角色设定", example = "一个快乐的程序员")
|
||||
private String systemMessage;
|
||||
|
||||
@Schema(description = "温度参数", requiredMode = Schema.RequiredMode.REQUIRED, example = "0.8")
|
||||
private Double temperature;
|
||||
|
||||
@Schema(description = "单条回复的最大 Token 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096")
|
||||
private Integer maxTokens;
|
||||
|
||||
@Schema(description = "上下文的最大 Message 数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
|
||||
private Integer maxContexts;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
// ========== 关联 role 信息 ==========
|
||||
|
||||
@Schema(description = "角色头像", example = "https://www.iocoder.cn/1.png")
|
||||
private String roleAvatar;
|
||||
|
||||
@Schema(description = "角色名字", example = "小黄")
|
||||
private String roleName;
|
||||
|
||||
// ========== 仅在【对话管理】时加载 ==========
|
||||
|
||||
@Schema(description = "消息数量", example = "20")
|
||||
private Integer messageCount;
|
||||
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package cn.iocoder.lyzsys.module.ai.controller.admin.chat.vo.conversation;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - AI 聊天对话更新【我的】 Request VO")
|
||||
@Data
|
||||
public class AiChatConversationUpdateMyReqVO {
|
||||
|
||||
@Schema(description = "对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "对话编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "对话标题", example = "我是一个标题")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "是否置顶", example = "true")
|
||||
private Boolean pinned;
|
||||
|
||||
@Schema(description = "模型编号", example = "1")
|
||||
private Long modelId;
|
||||
|
||||
@Schema(description = "知识库编号", example = "1")
|
||||
private Long knowledgeId;
|
||||
|
||||
@Schema(description = "角色设定", example = "一个快乐的程序员")
|
||||
private String systemMessage;
|
||||
|
||||
@Schema(description = "温度参数", example = "0.8")
|
||||
private Double temperature;
|
||||
|
||||
@Schema(description = "单条回复的最大 Token 数量", example = "4096")
|
||||
private Integer maxTokens;
|
||||
|
||||
@Schema(description = "上下文的最大 Message 数量", example = "10")
|
||||
private Integer maxContexts;
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package cn.iocoder.lyzsys.module.ai.controller.admin.chat.vo.message;
|
||||
|
||||
import cn.iocoder.lyzsys.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static cn.iocoder.lyzsys.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - AI 聊天消息的分页 Request VO")
|
||||
@Data
|
||||
public class AiChatMessagePageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "对话编号", example = "2048")
|
||||
private Long conversationId;
|
||||
|
||||
@Schema(description = "用户编号", example = "1024")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "消息内容", example = "你好")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
||||