iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/core/ArrayValuable.java
对比新文件 @@ -0,0 +1,15 @@ package com.iailab.framework.common.core; /** * 可生成 T 数组的接口 * * @author HUIHUI */ public interface ArrayValuable<T> { /** * @return 数组 */ T[] array(); } iailab-framework/iailab-common/src/main/java/com/iailab/framework/common/util/template/XMLParserUtils.java
对比新文件 @@ -0,0 +1,296 @@ package com.iailab.framework.common.util.template; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.io.ByteArrayInputStream; import java.util.HashMap; import java.util.Map; public class XMLParserUtils { private final Map<String, String> settings; private final double[][] inputData; public XMLParserUtils(Map<String, String> settings, double[][] inputData) { this.settings = settings; this.inputData = inputData; } public String parse(String xml) throws Exception { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = builder.parse(new ByteArrayInputStream(xml.getBytes())); doc.getDocumentElement().normalize(); StringBuilder output = new StringBuilder(); processNode(doc.getDocumentElement(), output, -1); return output.toString(); } private void processNode(Node node, StringBuilder output, int index) { if (node.getNodeType() == Node.TEXT_NODE) { output.append(node.getTextContent().trim()); } else if (node.getNodeType() == Node.ELEMENT_NODE) { Element element = (Element) node; switch (element.getTagName()) { case "simple-value": processSimpleValue(element, output, index); break; case "setting-value": processSettingValue(element, output); break; case "input-value": processInputValue(element, output, index); break; case "foreach-value": processForeachValue(element, output); break; default: processChildren(element, output, index); } } } private void processChildren(Element element, StringBuilder output, int index) { NodeList children = element.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { processNode(children.item(i), output, index); } } private void processSimpleValue(Element element, StringBuilder output, int index) { processChildren(element, output, index); } private void processSettingValue(Element element, StringBuilder output) { String key = element.getAttribute("key"); output.append(settings.getOrDefault(key, "")); } private void processInputValue(Element element, StringBuilder output, int index) { // 优先使用:port属性(动态计算) String portExpr = element.getAttribute(":port"); if (portExpr.isEmpty()) { // 回退到port属性(静态值) portExpr = element.getAttribute("port"); } String column = element.getAttribute("column"); int col = Integer.parseInt(column); // 动态计算端口值(考虑当前索引) int port = evaluateExpression(portExpr, index); if (port >= 0 && port < inputData.length && col >= 0 && col < inputData[port].length) { double value = inputData[port][col]; output.append((int)value); } else { System.err.printf("Invalid data access: port=%d (max=%d), col=%d (max=%d)%n", port, inputData.length - 1, col, (port >= 0 && port < inputData.length) ? inputData[port].length - 1 : -1); // 默认值 output.append("0"); } } private void processForeachValue(Element element, StringBuilder output) { String lengthKey = element.getAttribute("length"); int length = Integer.parseInt(settings.getOrDefault(lengthKey, "0")); String separator = element.getAttribute("separator"); StringBuilder loopResult = new StringBuilder(); for (int i = 0; i < length; i++) { StringBuilder itemOutput = new StringBuilder(); // 直接处理所有子节点 NodeList children = element.getChildNodes(); for (int j = 0; j < children.getLength(); j++) { Node child = children.item(j); if (child.getNodeType() == Node.TEXT_NODE) { String text = child.getTextContent(); // 动态确定转炉状态 boolean isBlowing = isFurnaceBlowing(i); // 替换占位符并调整状态描述 text = text.replace("{index}", String.valueOf(i + 1)) .replace("未吹炼", isBlowing ? "正在吹炼" : "未吹炼") .replace("距离上次吹炼结束时间", isBlowing ? "吹炼持续时间" : "距离上次吹炼结束时间"); itemOutput.append(text); } else if (child.getNodeType() == Node.ELEMENT_NODE) { // 传递当前循环索引 processNode(child, itemOutput, i); } } loopResult.append(itemOutput); if (i < length - 1 && !separator.isEmpty()) { loopResult.append(separator); } } output.append(loopResult.toString().replace(" ", "").replace("\n", "")); } private boolean isFurnaceBlowing(int furnaceIndex) { // 实际业务逻辑:根据输入数据判断转炉状态 // 这里简化处理:第三个转炉(index=2)正在吹炼 return furnaceIndex == 2; } // 自定义表达式计算器 private int evaluateExpression(String expr, int index) { if (expr == null || expr.isEmpty()) { System.err.println("Empty expression, returning 0"); return 0; } // 替换表达式中的{index}占位符 String processedExpr = expr.replace("{index}", String.valueOf(index)); // 移除所有空格确保表达式正确解析 processedExpr = processedExpr.replaceAll("\\s", ""); // 尝试直接解析为整数(优化常见情况) try { return Integer.parseInt(processedExpr); } catch (NumberFormatException e) { // 不是简单整数,继续尝试表达式计算 } // 自定义简单表达式解析器 try { // 处理加法表达式(如 "3+0") if (processedExpr.contains("+")) { String[] parts = processedExpr.split("\\+"); int result = 0; for (String part : parts) { result += Integer.parseInt(part); } return result; } // 处理减法表达式(如 "5-2") else if (processedExpr.contains("-")) { String[] parts = processedExpr.split("-"); int result = Integer.parseInt(parts[0]); for (int i = 1; i < parts.length; i++) { result -= Integer.parseInt(parts[i]); } return result; } // // 处理乘法表达式(如 "2 * 3") // else if (processedExpr.contains("*")) { // String[] parts = processedExpr.split("\\*"); // int result = 1; // for (String part : parts) { // result *= Integer.parseInt(part); // } // return result; // } // // 处理除法表达式(如 "6/2") // else if (processedExpr.contains("/")) { // String[] parts = processedExpr.split("/"); // int result = Integer.parseInt(parts[0]); // for (int i = 1; i < parts.length; i++) { // int divisor = Integer.parseInt(parts[i]); // if (divisor != 0) { // result /= divisor; // } // } // return result; // } } catch (NumberFormatException e) { System.err.println("Error parsing expression: " + processedExpr); } // 无法解析的表达式 System.err.println("Unsupported expression format: " + processedExpr); return 0; } // test public static void main(String[] args) throws Exception { String xmlContent = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<iquestion>\n" + " <simple-value>根据以下输入,</simple-value>\n" + " <simple-value>\n" + " 根据未来<setting-value key=\"predictLength\"/>min的转炉煤气系统运行情况,对煤气调整用户进行调度分配,确保能源产消平衡与系统稳定运行。\n" + " </simple-value>\n" + " <simple-value>\n" + " 该系统共包含<setting-value key=\"luShu\"/>座转炉。\n" + " </simple-value>\n" + " <foreach-value length=\"luShu\" index=\"index\" separator=\";\">\n" + " {index}#转炉当前状态未吹炼,\n" + " 距离上次吹炼结束时间<input-value :port=\"3 + {index}\" column=\"0\" type=\"double\"/>min,\n" + " 前一炉回收间隔<input-value :port=\"3 + {index}\" column=\"2\" type=\"double\"/>min\n" + " </foreach-value>\n" + " <simple-value>\n" + " 日计划炉数:<input-value port=\"2\" column=\"0\" type=\"double\"/>,当前已吹炼炉数:<input-value port=\"3\" column=\"0\" type=\"double\"/>;\n" + " </simple-value>\n" + " <simple-value>\n" + " 历史60min平均每炉煤气回收量:<input-value port=\"1\" column=\"0\" type=\"double\"/>Km3,\n" + " </simple-value>\n" + " <simple-value>\n" + " 当前煤气消耗量<input-value port=\"1\" column=\"1\" type=\"double\"/>km3;\n" + " </simple-value>\n" + " <simple-value>\n" + " 当前煤气柜容量<setting-value key=\"guiRongLiang\"/>km3;\n" + " </simple-value>\n" + " <simple-value>\n" + " 煤气柜柜位安全区间为<setting-value key=\"anQuanQuJian\"/>km3,期望煤气柜位值在<setting-value key=\"qiWangZhi\"/>km3,\n" + " </simple-value>\n" + " <simple-value>\n" + " 当前调度用户为电厂各机组:<setting-value key=\"jiZuMingCheng\"/>,\n" + " </simple-value>\n" + " <simple-value>\n" + " 各机组当前使用转炉煤气量 [<input-value port=\"0\" column=\"0\" type=\"double\"/>, <input-value port=\"0\" column=\"1\" type=\"double\"/>, <input-value port=\"0\" column=\"2\" type=\"double\"/>, <input-value port=\"0\" column=\"3\" type=\"double\"/>, <input-value port=\"0\" column=\"4\" type=\"double\"/>, <input-value port=\"0\" column=\"5\" type=\"double\"/>];\n" + " </simple-value>\n" + " <simple-value>\n" + " 各机组使用转炉煤气下限量 <setting-value key=\"jiZuXiaXian\"/>。\n" + " </simple-value>\n" + " <simple-value>\n" + " 各机组使用转炉煤气上限量 <setting-value key=\"jiZuShangXian\"/>。\n" + " </simple-value>\n" + " <simple-value>\n" + " 机组优先级顺序为:<setting-value key=\"jiZuYouXianJi\"/>。\n" + " </simple-value>\n" + " <simple-value>请根据优先级顺序确定调度方案。</simple-value>\n" + "</iquestion>"; // 1. 准备配置数据 Map<String, String> settings = new HashMap<>(); settings.put("predictLength", "60"); settings.put("luShu", "3"); settings.put("guiRongLiang", "80"); settings.put("anQuanQuJian", "[30, 110]"); settings.put("qiWangZhi", "65"); settings.put("jiZuMingCheng", "1#135, 2#135, 1#BTG, 2#BTG, 3#BTG, 4#BTG"); settings.put("jiZuXiaXian", "[20, 1, 16, 17, 6, 11]"); settings.put("jiZuShangXian", "[92, 96, 74, 101, 86, 93]"); settings.put("jiZuYouXianJi", "1#135, 1#BTG, 3#BTG, 2#135, 2#BTG, 4#BTG"); // 示例输入数据 - 与期望输出完全匹配 double[][] inputData = { // 机组当前用量 {39, 39, 32, 56, 38, 48}, // [0]:历史回收量, [1]:当前消耗量 {24, 115}, // [0]:日计划炉数, [1]:已吹炼炉数 {21, 10}, // 转炉1: [0]:结束时间, [2]:回收间隔 {15, 0, 18}, // 转炉2: [0]:结束时间, [2]:回收间隔 {9, 0, 24}, // 转炉3: [0]:吹炼持续时间, [2]:回收间隔 {7, 0, 25} }; // 3. 创建解析器并执行 XMLParserUtils parser = new XMLParserUtils(settings, inputData); String result = parser.parse(xmlContent); // 替换为你的XML内容 System.out.println(result); } } iailab-module-ai/.idea/compiler.xml
对比新文件 @@ -0,0 +1,35 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="CompilerConfiguration"> <annotationProcessing> <profile default="true" name="Default" enabled="true" /> <profile name="Maven default annotation processors profile" enabled="true"> <sourceOutputDir name="target/generated-sources/annotations" /> <sourceTestOutputDir name="target/generated-test-sources/test-annotations" /> <outputRelativeToContentRoot value="true" /> <module name="yudao-module-ai-api" /> </profile> <profile name="Annotation profile for iailab-module-ai" enabled="true"> <sourceOutputDir name="target/generated-sources/annotations" /> <sourceTestOutputDir name="target/generated-test-sources/test-annotations" /> <outputRelativeToContentRoot value="true" /> <processorPath useClasspath="false"> <entry name="$PROJECT_DIR$/../../../../.m2/repository/org/springframework/boot/spring-boot-configuration-processor/3.4.1/spring-boot-configuration-processor-3.4.1.jar" /> <entry name="$PROJECT_DIR$/../../../../.m2/repository/org/projectlombok/lombok/1.18.36/lombok-1.18.36.jar" /> <entry name="$PROJECT_DIR$/../../../../.m2/repository/org/mapstruct/mapstruct-processor/1.6.3/mapstruct-processor-1.6.3.jar" /> <entry name="$PROJECT_DIR$/../../../../.m2/repository/org/mapstruct/mapstruct/1.6.3/mapstruct-1.6.3.jar" /> </processorPath> <module name="iailab-module-ai-api" /> <module name="iailab-spring-boot-starter-ai" /> <module name="iailab-module-ai-biz" /> </profile> </annotationProcessing> </component> <component name="JavacSettings"> <option name="ADDITIONAL_OPTIONS_OVERRIDE"> <module name="iailab-module-ai-api" options="-parameters" /> <module name="iailab-module-ai-biz" options="-parameters" /> <module name="iailab-spring-boot-starter-ai" options="-parameters" /> </option> </component> </project> iailab-module-ai/.idea/jarRepositories.xml
对比新文件 @@ -0,0 +1,30 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="RemoteRepositoriesConfiguration"> <remote-repository> <option name="id" value="central" /> <option name="name" value="Central Repository" /> <option name="url" value="https://repo.maven.apache.org/maven2" /> </remote-repository> <remote-repository> <option name="id" value="iailab" /> <option name="name" value="iailab" /> <option name="url" value="http://172.16.8.100:8090/repository/iailab/" /> </remote-repository> <remote-repository> <option name="id" value="central" /> <option name="name" value="Maven Central repository" /> <option name="url" value="https://repo1.maven.org/maven2" /> </remote-repository> <remote-repository> <option name="id" value="jboss.community" /> <option name="name" value="JBoss Community repository" /> <option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" /> </remote-repository> <remote-repository> <option name="id" value="iailab" /> <option name="name" value="iailab" /> <option name="url" value="http://www.jira.dlindusit.com:30078/repository/iailab/" /> </remote-repository> </component> </project> iailab-module-ai/.idea/misc.xml
对比新文件 @@ -0,0 +1,14 @@ <?xml version="1.0" encoding="UTF-8"?> <project version="4"> <component name="ExternalStorageConfigurationManager" enabled="true" /> <component name="MavenProjectsManager"> <option name="originalFiles"> <list> <option value="$PROJECT_DIR$/pom.xml" /> <option value="$PROJECT_DIR$/iailab-module-ai-api/pom.xml" /> <option value="$PROJECT_DIR$/iailab-spring-boot-starter-ai/pom.xml" /> </list> </option> </component> <component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="graalvm-jdk-17" project-jdk-type="JavaSDK" /> </project> iailab-module-ai/iailab-module-ai-api/pom.xml
@@ -5,7 +5,7 @@ <parent> <groupId>com.iailab</groupId> <artifactId>iailab-module-ai</artifactId> <version>${ai.revision}</version> <version>${revision}</version> </parent> <build> <plugins> @@ -13,8 +13,8 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>17</source> <target>17</target> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> @@ -34,21 +34,26 @@ <artifactId>iailab-common</artifactId> </dependency> <!-- Web 相关 --> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-ui</artifactId> <version>1.8.0</version> <scope>provided</scope> <!-- 设置为 provided,主要是 PageParam 使用到 --> </dependency> <!-- 参数校验 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> <optional>true</optional> </dependency> <!-- RPC 远程调用相关 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-openfeign-core</artifactId> </dependency> <dependency> <groupId>io.swagger.core.v3</groupId> <artifactId>swagger-annotations</artifactId> <version>2.2.25</version> <scope>compile</scope> <artifactId>spring-cloud-starter-openfeign</artifactId> <optional>true</optional> </dependency> </dependencies> </project> iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/AiChatConversationApi.java
对比新文件 @@ -0,0 +1,20 @@ package com.iailab.module.ai.api.chat; import com.iailab.framework.common.pojo.CommonResult; import com.iailab.module.ai.enums.ApiConstants; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; @FeignClient(name = ApiConstants.NAME) @Tag(name = "RPC 大模型会话") public interface AiChatConversationApi { String PREFIX = ApiConstants.PREFIX + "/conversation"; @GetMapping(PREFIX + "/energy-conversation") @Operation(summary = "会话列表", description = "查询会话列表,只有一条,首次查询先创建再查询") Long chatEnergyConversation(@RequestParam("modelName") String modelName); } iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/AiChatMessageApi.java
对比新文件 @@ -0,0 +1,22 @@ package com.iailab.module.ai.api.chat; import com.iailab.module.ai.api.chat.dto.AiChatMessageSendRespDTO; import com.iailab.module.ai.api.chat.dto.AiChatMessageSendReqDTO; import com.iailab.module.ai.enums.ApiConstants; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @FeignClient(name = ApiConstants.NAME) @Tag(name = "RPC 大模型消息") public interface AiChatMessageApi { String PREFIX = ApiConstants.PREFIX + "/message"; @PostMapping(PREFIX + "/send-message") @Operation(summary = "发送消息(段式)", description = "一次性返回,响应较慢") AiChatMessageSendRespDTO sendMessage(@Valid @RequestBody AiChatMessageSendReqDTO sendReqDTO); } iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/dto/AiChatConversationRespDTO.java
对比新文件 @@ -0,0 +1,58 @@ package com.iailab.module.ai.api.chat.dto; import io.swagger.v3.oas.annotations.media.Schema; import java.time.LocalDateTime; public class AiChatConversationRespDTO { @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") private Long roleId; @Schema(description = "模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") 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.Iailab.cn/1.png") private String roleAvatar; @Schema(description = "角色名字", example = "小黄") private String roleName; // ========== 仅在【对话管理】时加载 ========== @Schema(description = "消息数量", example = "20") private Integer messageCount; } iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/dto/AiChatMessageSendReqDTO.java
对比新文件 @@ -0,0 +1,23 @@ package com.iailab.module.ai.api.chat.dto; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import lombok.Data; @Schema(description = "管理后台 - AI 聊天消息发送 Request VO") @Data public class AiChatMessageSendReqDTO { @Schema(description = "聊天对话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") @NotNull(message = "聊天对话编号不能为空") private Long conversationId; @Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "帮我写个 Java 算法") @NotEmpty(message = "聊天内容不能为空") private String content; @Schema(description = "是否携带上下文", example = "true") private Boolean useContext; } iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/dto/AiChatMessageSendRespDTO.java
对比新文件 @@ -0,0 +1,34 @@ package com.iailab.module.ai.api.chat.dto; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.time.LocalDateTime; @Data public class AiChatMessageSendRespDTO { @Schema(description = "发送消息", requiredMode = Schema.RequiredMode.REQUIRED) private Message send; @Schema(description = "接收消息", requiredMode = Schema.RequiredMode.REQUIRED) private Message receive; @Schema(description = "消息") @Data public static class Message { @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private Long id; @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "role") private String type; // 参见 MessageType 枚举类 @Schema(description = "聊天内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "你好,你好啊") private String content; @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) private LocalDateTime createTime; } } iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/chat/dto/AiModelDO.java
对比新文件 @@ -0,0 +1,79 @@ package com.iailab.module.ai.api.chat.dto; import com.iailab.framework.common.enums.CommonStatusEnum; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; /** * AI 模型 DO * * 默认模型:{@link #status} 为开启,并且 {@link #sort} 排序第一 * * @author houzhongjian * @since 2025/6/33 11:39 */ @Data @Builder @NoArgsConstructor @AllArgsConstructor public class AiModelDO { /** * 编号 */ private Long id; /** * API 秘钥编号 * */ private Long keyId; /** * 模型名称 */ private String name; /** * 模型标志 */ private String model; /** * 平台 * */ private String platform; /** * 类型 * */ private Integer type; /** * 排序值 */ private Integer sort; /** * 状态 * * 枚举 {@link CommonStatusEnum} */ private Integer status; // ========== 对话配置 ========== /** * 温度参数 * * 用于调整生成回复的随机性和多样性程度:较低的温度值会使输出更收敛于高频词汇,较高的则增加多样性 */ private Double temperature; /** * 单条回复的最大 Token 数量 */ private Integer maxTokens; /** * 上下文的最大 Message 数量 */ private Integer maxContexts; } iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/questionTemplate/QuestionTemplateApi.java
对比新文件 @@ -0,0 +1,34 @@ package com.iailab.module.ai.api.questionTemplate; import com.iailab.framework.common.enums.RpcConstants; import com.iailab.module.ai.api.questionTemplate.dto.AiModelDTO; import com.iailab.module.ai.api.questionTemplate.dto.QuestionTemplateDTO; import com.iailab.module.ai.enums.ApiConstants; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import java.util.List; /** * @author DongYukun * @Description * @createTime 2025年06月04日 */ @FeignClient(name = ApiConstants.NAME) @Tag(name = "问题模板") public interface QuestionTemplateApi { String PREFIX = RpcConstants.RPC_API_PREFIX + "/ai/questiontemplate"; @GetMapping(PREFIX + "/modelList") @Operation(summary = "Ai大模型级联问题模版") List<AiModelDTO> queryModelList(); @PostMapping(PREFIX + "/queryTemplates") @Operation(summary = "Ai大模型级联问题模版") List<QuestionTemplateDTO> queryTemplates(@RequestBody QuestionTemplateDTO questionTemplateDTO); } iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/questionTemplate/QuestiontemplateApi.java
文件已删除 iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/questionTemplate/dto/AiModelDTO.java
文件名从 iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/questionTemplate/dto/AiModelDto.java 修改 @@ -6,7 +6,7 @@ import java.util.List; @Data public class AiModelDto implements Serializable { public class AiModelDTO implements Serializable { @Schema(description = "编号", example = "2630") private Long id; @@ -41,6 +41,6 @@ @Schema(description = "上下文的最大 Message 数量", example = "8192") private Integer maxContexts; private List<QuestionTemplateDto> children; private List<QuestionTemplateDTO> children; } iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/questionTemplate/dto/QuestionTemplateDTO.java
文件名从 iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/api/questionTemplate/dto/QuestionTemplateDto.java 修改 @@ -11,7 +11,7 @@ * @author 超级管理员 */ @Data public class QuestionTemplateDto implements Serializable { public class QuestionTemplateDTO implements Serializable { /** * id iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/AiChatRoleEnum.java
@@ -12,29 +12,25 @@ @Getter public enum AiChatRoleEnum { AI_WRITE_ROLE("写作助手", """ 你是一位出色的写作助手,能够帮助用户生成创意和灵感,并在用户提供场景和提示词时生成对应的回复。你的任务包括: 1. 撰写建议:根据用户提供的主题或问题,提供详细的写作建议、情节发展方向、角色设定以及背景描写,确保内容结构清晰、有逻辑。 2. 回复生成:根据用户提供的场景和提示词,生成合适的对话或文字回复,确保语气和风格符合场景需求。 除此之外不需要除了正文内容外的其他回复,如标题、开头、任何解释性语句或道歉。 """), AI_WRITE_ROLE("写作助手", "你是一位出色的写作助手,能够帮助用户生成创意和灵感,并在用户提供场景和提示词时生成对应的回复。你的任务包括:\n" + " 1.\t撰写建议:根据用户提供的主题或问题,提供详细的写作建议、情节发展方向、角色设定以及背景描写,确保内容结构清晰、有逻辑。\n" + " 2.\t回复生成:根据用户提供的场景和提示词,生成合适的对话或文字回复,确保语气和风格符合场景需求。\n" + " 除此之外不需要除了正文内容外的其他回复,如标题、开头、任何解释性语句或道歉。"), AI_MIND_MAP_ROLE("导图助手", """ 你是一位非常优秀的思维导图助手,你会把用户的所有提问都总结成思维导图,然后以 Markdown 格式输出。markdown 只需要输出一级标题,二级标题,三级标题,四级标题,最多输出四级,除此之外不要输出任何其他 markdown 标记。下面是一个合格的例子: # Geek-AI 助手 ## 完整的开源系统 ### 前端开源 ### 后端开源 ## 支持各种大模型 ### OpenAI ### Azure ### 文心一言 ### 通义千问 ## 集成多种收费方式 ### 支付宝 ### 微信 除此之外不要任何解释性语句。 """), AI_MIND_MAP_ROLE("导图助手", "你是一位非常优秀的思维导图助手,你会把用户的所有提问都总结成思维导图,然后以 Markdown 格式输出。markdown 只需要输出一级标题,二级标题,三级标题,四级标题,最多输出四级,除此之外不要输出任何其他 markdown 标记。下面是一个合格的例子:\n" + " # Geek-AI 助手\n" + " ## 完整的开源系统\n" + " ### 前端开源\n" + " ### 后端开源\n" + " ## 支持各种大模型\n" + " ### OpenAI\n" + " ### Azure\n" + " ### 文心一言\n" + " ### 通义千问\n" + " ## 集成多种收费方式\n" + " ### 支付宝\n" + " ### 微信\n" + " 除此之外不要任何解释性语句。"), ; /** iailab-module-ai/iailab-module-ai-api/src/main/java/com/iailab/module/ai/enums/ApiConstants.java
对比新文件 @@ -0,0 +1,23 @@ package com.iailab.module.ai.enums; import com.iailab.framework.common.enums.RpcConstants; /** * API 相关的枚举 * * @author iailab */ public class ApiConstants { /** * 服务名 * * 注意,需要保证和 spring.application.name 保持一致 */ public static final String NAME = "ai-server"; public static final String PREFIX = RpcConstants.RPC_API_PREFIX + "/ai"; public static final String VERSION = "1.0.0"; } iailab-module-ai/iailab-module-ai-biz/pom.xml
@@ -5,7 +5,7 @@ <parent> <groupId>com.iailab</groupId> <artifactId>iailab-module-ai</artifactId> <version>${ai.revision}</version> <version>${revision}</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>iailab-module-ai-biz</artifactId> @@ -29,7 +29,7 @@ <dependency> <groupId>com.iailab</groupId> <artifactId>iailab-module-ai-api</artifactId> <version>${ai.revision}</version> <version>${revision}</version> </dependency> <!-- 业务组件 --> @@ -114,8 +114,8 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>17</source> <target>17</target> <source>${java.version}</source> <target>${java.version}</target> </configuration> </plugin> </plugins> iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/api/QuestionTemplateApiImpl.java
文件已删除 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/api/chat/AiChatConversionApiImpl.java
对比新文件 @@ -0,0 +1,34 @@ package com.iailab.module.ai.api.chat; import com.iailab.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateEnergyReqVO; import com.iailab.module.ai.dal.dataobject.chat.AiChatConversationDO; import com.iailab.module.ai.service.chat.AiChatConversationService; import jakarta.annotation.Resource; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RestController; import java.util.List; import static com.iailab.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; @RestController // 提供 RESTful API 接口,给 Feign 调用 @Validated public class AiChatConversionApiImpl implements AiChatConversationApi { @Resource private AiChatConversationService chatConversationService; @Override public Long chatEnergyConversation(String modelName) { Long conversationId; List<AiChatConversationDO> list = chatConversationService.getChatConversationList(getLoginUserId(), modelName); if(list.size() == 0) { AiChatConversationCreateEnergyReqVO createReqVO = new AiChatConversationCreateEnergyReqVO(); createReqVO.setModelName(modelName); conversationId = chatConversationService.createChatConversationEnergy(createReqVO); } else { conversationId = list.get(0).getId(); } return conversationId; } } iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/api/chat/AiChatMessageApiImpl.java
对比新文件 @@ -0,0 +1,27 @@ package com.iailab.module.ai.api.chat; import com.iailab.framework.common.util.object.BeanUtils; import com.iailab.module.ai.api.chat.dto.AiChatMessageSendRespDTO; import com.iailab.module.ai.api.chat.dto.AiChatMessageSendReqDTO; import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO; import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO; import com.iailab.module.ai.service.chat.AiChatMessageService; import jakarta.annotation.Resource; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RestController; import static com.iailab.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; @RestController // 提供 RESTful API 接口,给 Feign 调用 @Validated public class AiChatMessageApiImpl implements AiChatMessageApi { @Resource private AiChatMessageService chatMessageService; @Override public AiChatMessageSendRespDTO sendMessage(AiChatMessageSendReqDTO sendReqDTO) { AiChatMessageSendRespVO aiChatMessageSendRespVO = chatMessageService.sendMessage(BeanUtils.toBean(sendReqDTO, AiChatMessageSendReqVO.class), getLoginUserId()); return BeanUtils.toBean(aiChatMessageSendRespVO, AiChatMessageSendRespDTO.class); } } iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/api/package-info.java
对比新文件 @@ -0,0 +1 @@ package com.iailab.module.ai.api; iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/api/template/QuestionTemplateApiImpl.java
对比新文件 @@ -0,0 +1,46 @@ package com.iailab.module.ai.api.template; import com.iailab.framework.common.util.object.BeanUtils; import com.iailab.module.ai.api.questionTemplate.QuestionTemplateApi; import com.iailab.module.ai.api.questionTemplate.dto.AiModelDTO; import com.iailab.module.ai.api.questionTemplate.dto.QuestionTemplateDTO; import com.iailab.module.ai.controller.admin.questiontemplate.vo.QuestionTemplateReqVO; import com.iailab.module.ai.service.model.AiModelService; import com.iailab.module.ai.service.questiontemplate.QuestionTemplateService; import jakarta.annotation.Resource; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RestController; import java.util.List; /** * @description: * @author: dongyukun * @date: 2025/6/4 14:50 **/ @RestController // 提供 RESTful API 接口,给 Feign 调用 @Validated public class QuestionTemplateApiImpl implements QuestionTemplateApi { @Resource private QuestionTemplateService questionTemplateService; @Resource private AiModelService aiModelService; @Override public List<AiModelDTO> queryModelList() { List<AiModelDTO> list = BeanUtils.toBean(aiModelService.getModelListByStatusAndType(0, 1, null), AiModelDTO.class); list.forEach(item->{ item.setChildren( BeanUtils.toBean(questionTemplateService.getQuestionTemplateList(item.getId()), QuestionTemplateDTO.class)); }); return list; } @Override public List<QuestionTemplateDTO> queryTemplates(QuestionTemplateDTO reqDTO) { QuestionTemplateReqVO ReqVO = BeanUtils.toBean(reqDTO, QuestionTemplateReqVO.class); return BeanUtils.toBean(questionTemplateService.getQuestionTemplates(ReqVO), QuestionTemplateDTO.class); } } iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/AiChatMessageController.java
@@ -7,10 +7,7 @@ import com.iailab.framework.common.pojo.PageResult; import com.iailab.framework.common.util.collection.MapUtils; import com.iailab.framework.common.util.object.BeanUtils; import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO; import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO; import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO; import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO; import com.iailab.module.ai.controller.admin.chat.vo.message.*; import com.iailab.module.ai.dal.dataobject.chat.AiChatConversationDO; import com.iailab.module.ai.dal.dataobject.chat.AiChatMessageDO; import com.iailab.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; @@ -33,6 +30,7 @@ import org.springframework.web.bind.annotation.*; import reactor.core.publisher.Flux; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; @@ -70,6 +68,29 @@ return chatMessageService.sendChatMessageStream(sendReqVO, getLoginUserId()); } @Operation(summary = "获得指定对话的消息列表(分页)") @GetMapping("/page-list-by-conversation-id") public CommonResult<PageResult<AiChatMessageRespVO>> getChatMessagePageListByConversationId(AiChatMessageReqVO queryReqVO) { PageResult<AiChatMessageRespVO> pageResult = new PageResult<>(); AiChatConversationDO conversation = chatConversationService.getChatConversation(queryReqVO.getConversationId()); if (conversation == null || ObjUtil.notEqual(conversation.getUserId(), getLoginUserId())) { return success(pageResult); } // 1. 获取消息列表 PageResult<AiChatMessageDO> pageList = chatMessageService.getChatMessagePageByConversationId(queryReqVO); List<AiChatMessageDO> messageList = pageList.getList(); if (CollUtil.isEmpty(messageList)) { pageResult.setList(new ArrayList<>()); pageResult.setTotal(0L); return success(pageResult); } List<AiChatMessageRespVO> messageVOList = BeanUtils.toBean(messageList, AiChatMessageRespVO.class); dealKnowledge(messageList, messageVOList); pageResult.setList(messageVOList); pageResult.setTotal(pageList.getTotal()); return success(pageResult); } @Operation(summary = "获得指定对话的消息列表") @GetMapping("/list-by-conversation-id") @Parameter(name = "conversationId", required = true, description = "对话编号", example = "1024") @@ -84,32 +105,8 @@ 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()); })); } dealKnowledge(messageList, messageVOList); return success(messageVOList); } @@ -180,4 +177,30 @@ return success(true); } private void dealKnowledge(List<AiChatMessageDO> messageList, List<AiChatMessageRespVO> messageVOList) { // 拼接数据,主要是知识库段落信息 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)); 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()); })); } } } iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/conversation/AiChatConversationCreateEnergyReqVO.java
@@ -10,4 +10,7 @@ @Schema(description = "模型名称", example = "zhuanlu") private String modelName; @Schema(description = "会话名称", example = "2025-06-10 17:09:33") private String title; } iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/chat/vo/message/AiChatMessageReqVO.java
对比新文件 @@ -0,0 +1,29 @@ package com.iailab.module.ai.controller.admin.chat.vo.message; import com.iailab.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 com.iailab.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; @Schema(description = "管理后台 - AI 聊天消息查询 Request VO") @Data public class AiChatMessageReqVO 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; } iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questiontemplate/QuestionTemplateController.java
@@ -1,7 +1,7 @@ package com.iailab.module.ai.controller.admin.questiontemplate; import com.iailab.module.ai.api.questionTemplate.dto.AiModelDto; import com.iailab.module.ai.api.questionTemplate.dto.QuestionTemplateDto; import com.iailab.module.ai.api.questionTemplate.dto.AiModelDTO; import com.iailab.module.ai.api.questionTemplate.dto.QuestionTemplateDTO; import com.iailab.module.ai.service.model.AiModelService; import jakarta.annotation.Resource; import org.springframework.web.bind.annotation.*; @@ -87,11 +87,11 @@ @GetMapping( "/modelList") @Operation(summary = "Ai大模型级联问题模版") List<AiModelDto> queryModelList(){ List<AiModelDto> list = BeanUtils.toBean(aiModelService.getModelListByStatusAndType(0, 1, null), AiModelDto.class); List<AiModelDTO> queryModelList(){ List<AiModelDTO> list = BeanUtils.toBean(aiModelService.getModelListByStatusAndType(0, 1, null), AiModelDTO.class); list.forEach(item->{ item.setChildren( BeanUtils.toBean(questionTemplateService.getQuestionTemplateList(item.getId()), QuestionTemplateDto.class)); BeanUtils.toBean(questionTemplateService.getQuestionTemplateList(item.getId()), QuestionTemplateDTO.class)); }); return list; } iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/controller/admin/questiontemplate/vo/QuestionTemplateReqVO.java
对比新文件 @@ -0,0 +1,27 @@ package com.iailab.module.ai.controller.admin.questiontemplate.vo; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; /** * @author houzh */ @Schema(description = "管理后台 - 大模型问题模板查询 Request VO") @Data public class QuestionTemplateReqVO { @Schema(description = "id", requiredMode = Schema.RequiredMode.REQUIRED, example = "31480") private String id; @Schema(description = "模型id", requiredMode = Schema.RequiredMode.REQUIRED, example = "11609") private String modelId; @Schema(description = "问题编号", requiredMode = Schema.RequiredMode.REQUIRED) private String questionCode; @Schema(description = "问题名称", example = "赵六") private String questionName; @Schema(description = "模型名称") private String modelName; } iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/dal/mysql/chat/AiChatMessageMapper.java
@@ -7,6 +7,7 @@ import com.iailab.framework.mybatis.core.mapper.BaseMapperX; import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX; import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO; import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessageReqVO; import com.iailab.module.ai.dal.dataobject.chat.AiChatMessageDO; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import org.apache.ibatis.annotations.Mapper; @@ -24,6 +25,13 @@ @Mapper public interface AiChatMessageMapper extends BaseMapperX<AiChatMessageDO> { default PageResult<AiChatMessageDO> selectPageByConversationId(AiChatMessageReqVO reqVO) { return selectPage(reqVO, new LambdaQueryWrapperX<AiChatMessageDO>() .eq(AiChatMessageDO::getConversationId, reqVO.getConversationId()) .betweenIfPresent(AiChatMessageDO::getCreateTime, reqVO.getCreateTime()) .orderByDesc(AiChatMessageDO::getId)); } default List<AiChatMessageDO> selectListByConversationId(Long conversationId) { return selectList(new LambdaQueryWrapperX<AiChatMessageDO>() .eq(AiChatMessageDO::getConversationId, conversationId) iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/job/image/AiMidjourneySyncJob.java
@@ -1,4 +1,4 @@ //package com.iailab.module.ai.job.image; package com.iailab.module.ai.job.image;//package com.iailab.module.ai.job.image; // //import com.iailab.module.ai.service.image.AiImageService; //import com.xxl.job.core.handler.annotation.XxlJob; iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/job/music/AiSunoSyncJob.java
@@ -1,4 +1,4 @@ //package com.iailab.module.ai.job.music; package com.iailab.module.ai.job.music;//package com.iailab.module.ai.job.music; // //import com.iailab.module.ai.service.music.AiMusicService; //import com.xxl.job.core.handler.annotation.XxlJob; iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/chat/AiChatConversationServiceImpl.java
@@ -6,6 +6,7 @@ import cn.hutool.core.util.ObjectUtil; import com.iailab.framework.ai.core.enums.AiModelTypeEnum; import com.iailab.framework.common.pojo.PageResult; import com.iailab.framework.common.util.date.DateUtils; import com.iailab.framework.common.util.object.BeanUtils; import com.iailab.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateEnergyReqVO; import com.iailab.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateMyReqVO; @@ -20,15 +21,18 @@ import com.iailab.module.ai.service.model.AiChatRoleService; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import java.time.LocalDateTime; import java.util.Date; import java.util.List; import java.util.Objects; import static com.iailab.framework.common.exception.util.ServiceExceptionUtil.exception; import static com.iailab.framework.common.util.collection.CollectionUtils.convertList; import static com.iailab.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; import static com.iailab.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; import static com.iailab.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_MODEL_ERROR; import static com.iailab.module.ai.enums.ErrorCodeConstants.CHAT_CONVERSATION_NOT_EXISTS; @@ -92,7 +96,12 @@ AiChatConversationDO conversation = new AiChatConversationDO().setUserId(getLoginUserId()).setPinned(false) .setModelId(model.getId()).setModel(model.getModel()) .setTemperature(model.getTemperature()).setMaxTokens(model.getMaxTokens()).setMaxContexts(model.getMaxContexts()); conversation.setTitle("新对话"); if(StringUtils.isNotEmpty(createReqVO.getTitle())) { conversation.setTitle(createReqVO.getTitle()); } else { String format = DateUtils.format(new Date(), FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND); conversation.setTitle(format); } chatConversationMapper.insert(conversation); return conversation.getId(); } @@ -176,6 +185,7 @@ throw exception(CHAT_CONVERSATION_MODEL_ERROR); } @Override public AiChatConversationDO validateChatConversationExists(Long id) { AiChatConversationDO conversation = chatConversationMapper.selectById(id); if (conversation == null) { iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/chat/AiChatMessageService.java
@@ -3,6 +3,7 @@ import com.iailab.framework.common.pojo.CommonResult; import com.iailab.framework.common.pojo.PageResult; import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO; import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessageReqVO; import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO; import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO; import com.iailab.module.ai.dal.dataobject.chat.AiChatMessageDO; @@ -38,6 +39,14 @@ Flux<CommonResult<AiChatMessageSendRespVO>> sendChatMessageStream(AiChatMessageSendReqVO sendReqVO, Long userId); /** * 获得指定对话的消息列表(分页) * * @param queryReqVO 消息请求 * @return 消息列表 */ PageResult<AiChatMessageDO> getChatMessagePageByConversationId(AiChatMessageReqVO queryReqVO); /** * 获得指定对话的消息列表 * * @param conversationId 对话编号 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/chat/AiChatMessageServiceImpl.java
@@ -9,10 +9,7 @@ import com.iailab.framework.common.pojo.PageResult; import com.iailab.framework.common.util.object.BeanUtils; import com.iailab.framework.tenant.core.util.TenantUtils; import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessagePageReqVO; import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO; import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO; import com.iailab.module.ai.controller.admin.chat.vo.message.AiChatMessageSendRespVO; import com.iailab.module.ai.controller.admin.chat.vo.message.*; import com.iailab.module.ai.dal.dataobject.chat.AiChatConversationDO; import com.iailab.module.ai.dal.dataobject.chat.AiChatMessageDO; import com.iailab.module.ai.dal.dataobject.knowledge.AiKnowledgeDocumentDO; @@ -314,6 +311,11 @@ } @Override public PageResult<AiChatMessageDO> getChatMessagePageByConversationId(AiChatMessageReqVO reqVO) { return chatMessageMapper.selectPageByConversationId(reqVO); } @Override public List<AiChatMessageDO> getChatMessageListByConversationId(Long conversationId) { return chatMessageMapper.selectListByConversationId(conversationId); } iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/questiontemplate/QuestionTemplateService.java
@@ -46,6 +46,14 @@ QuestionTemplateDO getQuestionTemplate(String id); /** * 获得大模型问题模板 * * @param reqVO * @return 大模型问题模板 */ List<QuestionTemplateDO> getQuestionTemplates(QuestionTemplateReqVO reqVO); /** * 获得大模型问题模板分页 * * @param pageReqVO 分页查询 iailab-module-ai/iailab-module-ai-biz/src/main/java/com/iailab/module/ai/service/questiontemplate/QuestionTemplateServiceImpl.java
@@ -1,9 +1,15 @@ package com.iailab.module.ai.service.questiontemplate; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.iailab.framework.mybatis.core.query.LambdaQueryWrapperX; import com.iailab.module.ai.controller.admin.questionparamsetting.vo.QuestionParamSettingRespVO; import com.iailab.module.ai.dal.dataobject.model.AiModelDO; import com.iailab.module.ai.dal.dataobject.questionparamsetting.QuestionParamSettingDO; import com.iailab.module.ai.dal.mysql.questionparamsetting.QuestionParamSettingMapper; import com.iailab.module.ai.service.model.AiModelService; import jakarta.annotation.Resource; import org.apache.commons.lang3.ObjectUtils; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -34,6 +40,9 @@ @Resource private QuestionParamSettingMapper questionParamSettingMapper; @Resource private AiModelService aiModelService; @Override public String createQuestionTemplate(QuestionTemplateSaveReqVO createReqVO) { @@ -92,6 +101,27 @@ } @Override public List<QuestionTemplateDO> getQuestionTemplates(QuestionTemplateReqVO reqVO) { String modelName = reqVO.getModelName(); if(ObjectUtils.isNotEmpty(modelName)) { AiModelDO model = aiModelService.getModelByName(modelName); List<QuestionTemplateDO> questionTemplateDOS = questionTemplateMapper.selectList( new LambdaQueryWrapperX<QuestionTemplateDO>() .eqIfPresent(QuestionTemplateDO::getModelId, model.getId()) .eqIfPresent(QuestionTemplateDO::getQuestionCode, reqVO.getQuestionCode())); // 模板数量暂时很少,先循环遍历查询模板设置 if (questionTemplateDOS != null && questionTemplateDOS.size() > 0) { questionTemplateDOS.stream().forEach(questionTemplateDO -> { questionTemplateDO.setSettingList( BeanUtils.toBean(questionParamSettingMapper.selectList("template_id", questionTemplateDO.getId()), QuestionParamSettingRespVO.class)); }); } return questionTemplateDOS; } return null; } @Override public PageResult<QuestionTemplateDO> getQuestionTemplatePage(QuestionTemplatePageReqVO pageReqVO) { return questionTemplateMapper.selectPage(pageReqVO); } iailab-module-ai/iailab-module-ai-biz/src/main/resources/application.yaml
@@ -11,6 +11,7 @@ username: @nacos.username@ password: @nacos.password@ discovery: # 【配置中心】配置项 ip: @deploy.server@ namespace: @profiles.active@ group: DEFAULT_GROUP # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP metadata: @@ -59,7 +60,7 @@ logging: file: name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径 name: @log.path@/logs/${spring.application.name}.log # 日志文件名,全路径 --- #################### 接口文档配置 #################### @@ -111,7 +112,7 @@ job: executor: appname: ${spring.application.name} # 执行器 AppName logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径 logpath: @log.path@/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径 accessToken: default_token # 执行器通讯TOKEN --- #################### AI 相关配置 #################### iailab-module-ai/iailab-spring-boot-starter-ai/pom.xml
@@ -5,7 +5,7 @@ <parent> <groupId>com.iailab</groupId> <artifactId>iailab-module-ai</artifactId> <version>${ai.revision}</version> <version>${revision}</version> </parent> <build> <plugins> iailab-module-ai/pom.xml
@@ -10,7 +10,7 @@ <modelVersion>4.0.0</modelVersion> <groupId>com.iailab</groupId> <artifactId>iailab-module-ai</artifactId> <version>${ai.revision}</version> <version>${revision}</version> <packaging>pom</packaging> <modules> @@ -44,7 +44,7 @@ <spring.cloud.version>2024.0.0</spring.cloud.version> <spring.cloud.alibaba.version>2023.0.3.2</spring.cloud.alibaba.version> <!-- Web 相关 --> <springdoc.version>2.7.0</springdoc.version> <springdoc.version>2.8.3</springdoc.version> <knife4j.version>4.6.0</knife4j.version> <!-- 工具类相关 --> <bizlog-sdk.version>3.0.6</bizlog-sdk.version> @@ -191,7 +191,7 @@ <dependency> <groupId>com.iailab</groupId> <artifactId>iailab-module-infra-api</artifactId> <version>${revision}</version> <version>${ai.revision}</version> </dependency> <dependency> iailab-module-infra/iailab-module-infra-biz/src/main/resources/application-dev.yaml
@@ -44,7 +44,7 @@ primary: master datasource: master: url: jdbc:mysql://127.0.0.1:3306/iailab_plat_system?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 url: jdbc:mysql://172.16.8.100:3306/iailab_plat_system?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true # MySQL Connector/J 8.X 连接的示例 # url: jdbc:mysql://127.0.0.1:3306/iailab-plat?useSSL=true&allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai # MySQL Connector/J 5.X 连接的示例 # url: jdbc:postgresql://127.0.0.1:5432/ruoyi-vue-pro # PostgreSQL 连接的示例 # url: jdbc:oracle:thin:@127.0.0.1:1521:xe # Oracle 连接的示例 iailab-module-model/iailab-module-model-api/src/main/java/com/iailab/module/model/api/mcs/McsApi.java
@@ -156,10 +156,6 @@ @Operation(summary = "采纳建议") Boolean useSuggest(@RequestBody StAlarmAndSuggestReqVO reqVO); @PostMapping(PREFIX + "/schedule-suggest/cancel-use-suggest") @Operation(summary = "取消采纳建议") Boolean cancelUseSuggest(@RequestBody StAlarmAndSuggestReqVO reqVO); @PostMapping(PREFIX + "/schedule-suggest/ignore-suggest") @Operation(summary = "忽略建议") Boolean ignoreSuggest(StAlarmAndSuggestReqVO reqVO); @@ -179,13 +175,5 @@ @PostMapping(PREFIX + "/schedule-suggest/operation_record/save") @Operation(summary = "保存建议操作记录") Boolean saveSuggestOperationRecord(@RequestBody SuggestOperationRecordReqVO reqVO); @GetMapping(PREFIX + "/suggest/snapshot/getAllChartData") @Operation(summary = "根据建议id获取建议快照全部图表数据") List<StSuggestSnapshotRecordRespVO> getSuggestSnapshotAllChartData(@RequestParam("suggestId") String suggestId); @PostMapping(PREFIX + "/suggest/snapshot/conf-det/update-by-ext1") @Operation(summary = "根据ext1修改快照配置上下限") Boolean updateSuggestSnapshotConfDetByExt1(@RequestBody StSuggestSnapshotConfDetSaveReqVO updateReqVO); }