ai 对话记忆功能,识别
This commit is contained in:
1
xinnengyuan/.gitignore
vendored
1
xinnengyuan/.gitignore
vendored
@ -53,3 +53,4 @@ logs/
|
|||||||
docs
|
docs
|
||||||
/file
|
/file
|
||||||
.idea/
|
.idea/
|
||||||
|
chat-memory/
|
||||||
|
|||||||
@ -262,8 +262,6 @@ springdoc:
|
|||||||
packages-to-scan: org.dromara.ctr
|
packages-to-scan: org.dromara.ctr
|
||||||
- group: 15.无人机模块
|
- group: 15.无人机模块
|
||||||
packages-to-scan: org.dromara.drone
|
packages-to-scan: org.dromara.drone
|
||||||
- group: 20.代码生成模块
|
|
||||||
packages-to-scan: org.dromara.generator
|
|
||||||
- group: 16.app模块
|
- group: 16.app模块
|
||||||
packages-to-scan: org.dromara.app
|
packages-to-scan: org.dromara.app
|
||||||
- group: 17.材料设备模块
|
- group: 17.材料设备模块
|
||||||
@ -272,18 +270,16 @@ springdoc:
|
|||||||
packages-to-scan: org.dromara.out
|
packages-to-scan: org.dromara.out
|
||||||
- group: 19.消息模块
|
- group: 19.消息模块
|
||||||
packages-to-scan: org.dromara.message
|
packages-to-scan: org.dromara.message
|
||||||
|
- group: 20.代码生成模块
|
||||||
|
packages-to-scan: org.dromara.generator
|
||||||
- group: 21.分标策划模块
|
- group: 21.分标策划模块
|
||||||
packages-to-scan: org.dromara.tender
|
packages-to-scan: org.dromara.tender
|
||||||
- group: 22.大屏模块
|
- group: 22.大屏模块
|
||||||
packages-to-scan: org.dromara.bigscreen
|
packages-to-scan: org.dromara.bigscreen
|
||||||
- group: 22.投标管理模块
|
|
||||||
packages-to-scan: org.dromara.bidding
|
|
||||||
- group: 23.GPS定位模块
|
- group: 23.GPS定位模块
|
||||||
packages-to-scan: org.dromara.gps
|
packages-to-scan: org.dromara.gps
|
||||||
- group: 24.招标模块
|
- group: 24.招标模块
|
||||||
packages-to-scan: org.dromara.tender
|
packages-to-scan: org.dromara.tender
|
||||||
- group: 29.app版本模块
|
|
||||||
packages-to-scan: org.dromara.app
|
|
||||||
- group: 25.数据迁移模块
|
- group: 25.数据迁移模块
|
||||||
packages-to-scan: org.dromara.transferData
|
packages-to-scan: org.dromara.transferData
|
||||||
- group: 26.netty消息模块
|
- group: 26.netty消息模块
|
||||||
@ -292,6 +288,12 @@ springdoc:
|
|||||||
packages-to-scan: org.dromara.xzd
|
packages-to-scan: org.dromara.xzd
|
||||||
- group: 28.车辆模块
|
- group: 28.车辆模块
|
||||||
packages-to-scan: org.dromara.vehicle
|
packages-to-scan: org.dromara.vehicle
|
||||||
|
- group: 29.app版本模块
|
||||||
|
packages-to-scan: org.dromara.app
|
||||||
|
- group: 30.AI 模块
|
||||||
|
packages-to-scan: org.dromara.ai
|
||||||
|
- group: 31.投标管理模块
|
||||||
|
packages-to-scan: org.dromara.bidding
|
||||||
# knife4j的增强配置,不需要增强可以不配
|
# knife4j的增强配置,不需要增强可以不配
|
||||||
knife4j:
|
knife4j:
|
||||||
enable: true
|
enable: true
|
||||||
|
|||||||
@ -33,6 +33,11 @@
|
|||||||
<groupId>com.alibaba.cloud.ai</groupId>
|
<groupId>com.alibaba.cloud.ai</groupId>
|
||||||
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
|
<artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.esotericsoftware</groupId>
|
||||||
|
<artifactId>kryo</artifactId>
|
||||||
|
<version>5.6.2</version>
|
||||||
|
</dependency>
|
||||||
<!-- Java WebSocket 标准API -->
|
<!-- Java WebSocket 标准API -->
|
||||||
<!-- <dependency>-->
|
<!-- <dependency>-->
|
||||||
<!-- <groupId>javax.websocket</groupId>-->
|
<!-- <groupId>javax.websocket</groupId>-->
|
||||||
|
|||||||
@ -0,0 +1,61 @@
|
|||||||
|
package org.dromara.ai.advisor;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.ai.chat.client.ChatClientMessageAggregator;
|
||||||
|
import org.springframework.ai.chat.client.ChatClientRequest;
|
||||||
|
import org.springframework.ai.chat.client.ChatClientResponse;
|
||||||
|
import org.springframework.ai.chat.client.advisor.api.CallAdvisor;
|
||||||
|
import org.springframework.ai.chat.client.advisor.api.CallAdvisorChain;
|
||||||
|
import org.springframework.ai.chat.client.advisor.api.StreamAdvisor;
|
||||||
|
import org.springframework.ai.chat.client.advisor.api.StreamAdvisorChain;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义日志拦截器
|
||||||
|
*
|
||||||
|
* @author lilemy
|
||||||
|
* @date 2025-11-04 10:15
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class CustomLoggerAdvisor implements CallAdvisor, StreamAdvisor {
|
||||||
|
|
||||||
|
private void before(ChatClientRequest request) {
|
||||||
|
log.info("AI 请求参数:{}", request.prompt());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void observeAfter(ChatClientResponse response) {
|
||||||
|
if (response.chatResponse() != null) {
|
||||||
|
log.info("AI 响应结果:{}", response.chatResponse().getResult().getOutput().getText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ChatClientResponse adviseCall(ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain) {
|
||||||
|
before(chatClientRequest);
|
||||||
|
ChatClientResponse chatClientResponse = callAdvisorChain.nextCall(chatClientRequest);
|
||||||
|
observeAfter(chatClientResponse);
|
||||||
|
return chatClientResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Flux<ChatClientResponse> adviseStream(ChatClientRequest chatClientRequest, StreamAdvisorChain streamAdvisorChain) {
|
||||||
|
before(chatClientRequest);
|
||||||
|
Flux<ChatClientResponse> chatClientResponseFlux = streamAdvisorChain.nextStream(chatClientRequest);
|
||||||
|
return (new ChatClientMessageAggregator()).aggregateChatClientResponse(chatClientResponseFlux, this::observeAfter);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the advisor.
|
||||||
|
*
|
||||||
|
* @return the advisor name.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return this.getClass().getSimpleName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getOrder() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,117 @@
|
|||||||
|
package org.dromara.ai.chat;
|
||||||
|
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.ai.advisor.CustomLoggerAdvisor;
|
||||||
|
import org.dromara.ai.chatmemory.FileBasedChatMemory;
|
||||||
|
import org.dromara.ai.domain.AIChatMemory;
|
||||||
|
import org.dromara.ai.service.IAIChatMemoryService;
|
||||||
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
|
import org.dromara.common.satoken.utils.LoginHelper;
|
||||||
|
import org.springframework.ai.chat.client.ChatClient;
|
||||||
|
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
|
||||||
|
import org.springframework.ai.chat.memory.ChatMemory;
|
||||||
|
import org.springframework.ai.chat.model.ChatModel;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lilemy
|
||||||
|
* @date 2025-11-04 09:34
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class DashScopeChat {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private SimpleChat simpleChat;
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private IAIChatMemoryService chatMemoryService;
|
||||||
|
|
||||||
|
private final ChatClient chatClient;
|
||||||
|
|
||||||
|
private static final String DEFAULT_PROMPT = "你是一个博学的智能聊天助手,请根据用户提问回答!";
|
||||||
|
|
||||||
|
private static final String DEFAULT_FILE_DIR = System.getProperty("user.dir") + "/chat-memory";
|
||||||
|
|
||||||
|
public DashScopeChat(ChatModel dashScopeChatModel) {
|
||||||
|
// 初始化基于文件的对话记忆
|
||||||
|
ChatMemory chatMemory = new FileBasedChatMemory(DEFAULT_FILE_DIR);
|
||||||
|
chatClient = ChatClient.builder(dashScopeChatModel)
|
||||||
|
.defaultSystem(DEFAULT_PROMPT)
|
||||||
|
.defaultAdvisors(
|
||||||
|
MessageChatMemoryAdvisor.builder(chatMemory).build(),
|
||||||
|
// 自定义日志输出
|
||||||
|
new CustomLoggerAdvisor()
|
||||||
|
).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI 对话,流式输出
|
||||||
|
*
|
||||||
|
* @param message 用户输入
|
||||||
|
* @param chatId 会话id
|
||||||
|
* @return 流式输出结果
|
||||||
|
*/
|
||||||
|
public Flux<String> doChatStream(String message, String chatId) {
|
||||||
|
Long userId = LoginHelper.getUserId();
|
||||||
|
boolean isFirst = StringUtils.isBlank(chatId);
|
||||||
|
if (StringUtils.isBlank(chatId)) {
|
||||||
|
// 构建新的会话id
|
||||||
|
chatId = UUID.randomUUID().toString();
|
||||||
|
}
|
||||||
|
String finalChatId = chatId;
|
||||||
|
return chatClient
|
||||||
|
.prompt()
|
||||||
|
.user(message)
|
||||||
|
.advisors(spec -> spec.param(ChatMemory.CONVERSATION_ID, finalChatId))
|
||||||
|
.stream()
|
||||||
|
.content()// 收集所有 token,生成完整回复
|
||||||
|
.collectList()
|
||||||
|
.flatMapMany(tokens -> {
|
||||||
|
String aiResponse = String.join("", tokens);
|
||||||
|
if (isFirst) {
|
||||||
|
// 异步生成标题
|
||||||
|
generateChatTitleAsync(finalChatId, message, aiResponse, userId);
|
||||||
|
}
|
||||||
|
// 返回完整的流结果
|
||||||
|
return Flux.fromIterable(tokens);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 异步生成标题
|
||||||
|
*
|
||||||
|
* @param chatId 会话id
|
||||||
|
* @param userMessage 用户输入
|
||||||
|
* @param aiResponse AI回复
|
||||||
|
* @param userId 用户id
|
||||||
|
*/
|
||||||
|
private void generateChatTitleAsync(String chatId, String userMessage, String aiResponse, Long userId) {
|
||||||
|
CompletableFuture.runAsync(() -> {
|
||||||
|
try {
|
||||||
|
// 构建生成标题的提示词
|
||||||
|
String prompt = String.format("""
|
||||||
|
请为下面这段用户与AI的对话生成一个简短的标题(不超过10个字):
|
||||||
|
用户:%s
|
||||||
|
AI:%s
|
||||||
|
""", userMessage, aiResponse);
|
||||||
|
String title = simpleChat.doChat(prompt);
|
||||||
|
log.info("用户:{} 生成标题成功:{} -> {}", userId, chatId, title);
|
||||||
|
// 保存对话数据
|
||||||
|
AIChatMemory memory = new AIChatMemory();
|
||||||
|
memory.setUserId(userId);
|
||||||
|
memory.setFileName(chatId);
|
||||||
|
memory.setFirstQuestion(title);
|
||||||
|
chatMemoryService.save(memory);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("生成标题失败", e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
package org.dromara.ai.chat;
|
||||||
|
|
||||||
|
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
|
||||||
|
import org.springframework.ai.chat.client.ChatClient;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lilemy
|
||||||
|
* @date 2025-11-04 15:26
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class SimpleChat {
|
||||||
|
|
||||||
|
private final ChatClient dashScopeChatClient;
|
||||||
|
|
||||||
|
public SimpleChat(ChatClient.Builder chatClientBuilder) {
|
||||||
|
this.dashScopeChatClient = chatClientBuilder
|
||||||
|
// 设置 ChatClient 中 ChatModel 的 Options 参数
|
||||||
|
.defaultOptions(
|
||||||
|
DashScopeChatOptions.builder()
|
||||||
|
.withTopP(0.7)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI 对话
|
||||||
|
*
|
||||||
|
* @param message 用户输入
|
||||||
|
* @return 响应结果
|
||||||
|
*/
|
||||||
|
public String doChat(String message) {
|
||||||
|
return dashScopeChatClient.prompt(message).call().content();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
package org.dromara.ai.chatmemory;
|
||||||
|
|
||||||
|
import com.esotericsoftware.kryo.Kryo;
|
||||||
|
import com.esotericsoftware.kryo.io.Input;
|
||||||
|
import com.esotericsoftware.kryo.io.Output;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.objenesis.strategy.StdInstantiatorStrategy;
|
||||||
|
import org.springframework.ai.chat.memory.ChatMemory;
|
||||||
|
import org.springframework.ai.chat.messages.Message;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 基于文件持久化的对话记忆
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class FileBasedChatMemory implements ChatMemory {
|
||||||
|
|
||||||
|
private final String BASE_DIR;
|
||||||
|
private static final Kryo kryo = new Kryo();
|
||||||
|
|
||||||
|
static {
|
||||||
|
kryo.setRegistrationRequired(false);
|
||||||
|
// 设置实例化策略
|
||||||
|
kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构造对象时,指定文件保存目录
|
||||||
|
public FileBasedChatMemory(String dir) {
|
||||||
|
this.BASE_DIR = dir;
|
||||||
|
File baseDir = new File(dir);
|
||||||
|
if (!baseDir.exists()) {
|
||||||
|
baseDir.mkdirs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(String conversationId, List<Message> messages) {
|
||||||
|
List<Message> conversationMessages = getOrCreateConversation(conversationId);
|
||||||
|
conversationMessages.addAll(messages);
|
||||||
|
saveConversation(conversationId, conversationMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Message> get(String conversationId) {
|
||||||
|
return getOrCreateConversation(conversationId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear(String conversationId) {
|
||||||
|
File file = getConversationFile(conversationId);
|
||||||
|
if (file.exists()) {
|
||||||
|
file.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Message> getOrCreateConversation(String conversationId) {
|
||||||
|
File file = getConversationFile(conversationId);
|
||||||
|
List<Message> messages = new ArrayList<>();
|
||||||
|
if (file.exists()) {
|
||||||
|
try (Input input = new Input(new FileInputStream(file))) {
|
||||||
|
messages = kryo.readObject(input, ArrayList.class);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("读取对话失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return messages;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveConversation(String conversationId, List<Message> messages) {
|
||||||
|
File file = getConversationFile(conversationId);
|
||||||
|
try (Output output = new Output(new FileOutputStream(file))) {
|
||||||
|
kryo.writeObject(output, messages);
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.error("保存对话失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private File getConversationFile(String conversationId) {
|
||||||
|
return new File(BASE_DIR, conversationId + ".kryo");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
package org.dromara.ai.controller;
|
||||||
|
|
||||||
|
import jakarta.annotation.Resource;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.dromara.ai.chat.DashScopeChat;
|
||||||
|
import org.dromara.ai.chatmemory.FileBasedChatMemory;
|
||||||
|
import org.dromara.common.core.domain.R;
|
||||||
|
import org.dromara.common.satoken.utils.LoginHelper;
|
||||||
|
import org.springframework.ai.chat.messages.Message;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lilemy
|
||||||
|
* @date 2025-10-23 11:32
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Validated
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/ai/chat")
|
||||||
|
public class AIChatController {
|
||||||
|
|
||||||
|
@Resource
|
||||||
|
private DashScopeChat dashScopeChat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ChatClient 流式调用
|
||||||
|
*/
|
||||||
|
@GetMapping("/stream")
|
||||||
|
public Flux<String> streamChat(String query, String chatId, HttpServletResponse response) {
|
||||||
|
response.setCharacterEncoding("UTF-8");
|
||||||
|
return dashScopeChat.doChatStream(query, chatId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对话记录
|
||||||
|
*/
|
||||||
|
@GetMapping("/history")
|
||||||
|
public R<List<Message>> getChatHistory(String chatId) {
|
||||||
|
FileBasedChatMemory memory = new FileBasedChatMemory(System.getProperty("user.dir") + "/chat-memory");
|
||||||
|
return R.ok(memory.get(chatId));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
package org.dromara.ai.controller;
|
||||||
|
|
||||||
|
import cn.dev33.satoken.annotation.SaCheckPermission;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
import jakarta.validation.constraints.NotEmpty;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.dromara.ai.domain.dto.AIChatMemoryQueryReq;
|
||||||
|
import org.dromara.ai.domain.vo.AIChatMemoryVo;
|
||||||
|
import org.dromara.ai.service.IAIChatMemoryService;
|
||||||
|
import org.dromara.common.core.domain.R;
|
||||||
|
import org.dromara.common.excel.utils.ExcelUtil;
|
||||||
|
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.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI 对话记录信息
|
||||||
|
*
|
||||||
|
* @author lilemy
|
||||||
|
* @date 2025-11-04
|
||||||
|
*/
|
||||||
|
@Validated
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/ai/chatMemory")
|
||||||
|
public class AIChatMemoryController extends BaseController {
|
||||||
|
|
||||||
|
private final IAIChatMemoryService aiChatMemoryService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询AI 对话记录信息列表
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("ai:chatMemory:list")
|
||||||
|
@GetMapping("/list")
|
||||||
|
public TableDataInfo<AIChatMemoryVo> list(AIChatMemoryQueryReq req, PageQuery pageQuery) {
|
||||||
|
return aiChatMemoryService.queryPageList(req, pageQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出AI 对话记录信息列表
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("ai:chatMemory:export")
|
||||||
|
@Log(title = "AI 对话记录信息", businessType = BusinessType.EXPORT)
|
||||||
|
@PostMapping("/export")
|
||||||
|
public void export(AIChatMemoryQueryReq req, HttpServletResponse response) {
|
||||||
|
List<AIChatMemoryVo> list = aiChatMemoryService.queryList(req);
|
||||||
|
ExcelUtil.exportExcel(list, "AI 对话记录信息", AIChatMemoryVo.class, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取AI 对话记录信息详细信息
|
||||||
|
*
|
||||||
|
* @param id 主键
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("ai:chatMemory:query")
|
||||||
|
@GetMapping("/{id}")
|
||||||
|
public R<AIChatMemoryVo> getInfo(@NotNull(message = "主键不能为空")
|
||||||
|
@PathVariable Long id) {
|
||||||
|
return R.ok(aiChatMemoryService.queryById(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除AI 对话记录信息
|
||||||
|
*
|
||||||
|
* @param ids 主键串
|
||||||
|
*/
|
||||||
|
@SaCheckPermission("ai:chatMemory:remove")
|
||||||
|
@Log(title = "AI 对话记录信息", businessType = BusinessType.DELETE)
|
||||||
|
@DeleteMapping("/{ids}")
|
||||||
|
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
||||||
|
@PathVariable Long[] ids) {
|
||||||
|
return toAjax(aiChatMemoryService.deleteWithValidByIds(List.of(ids), true));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,51 +0,0 @@
|
|||||||
package org.dromara.ai.controller;
|
|
||||||
|
|
||||||
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import org.springframework.ai.chat.client.ChatClient;
|
|
||||||
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
|
|
||||||
import org.springframework.validation.annotation.Validated;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
import reactor.core.publisher.Flux;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author lilemy
|
|
||||||
* @date 2025-10-23 11:32
|
|
||||||
*/
|
|
||||||
@Validated
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/ai")
|
|
||||||
public class AIController {
|
|
||||||
|
|
||||||
private static final String DEFAULT_PROMPT = "你是一个博学的智能聊天助手,请根据用户提问回答!";
|
|
||||||
|
|
||||||
private final ChatClient dashScopeChatClient;
|
|
||||||
|
|
||||||
public AIController(ChatClient.Builder chatClientBuilder) {
|
|
||||||
this.dashScopeChatClient = chatClientBuilder
|
|
||||||
.defaultSystem(DEFAULT_PROMPT)
|
|
||||||
// 实现 Logger 的 Advisor
|
|
||||||
.defaultAdvisors(
|
|
||||||
new SimpleLoggerAdvisor()
|
|
||||||
)
|
|
||||||
// 设置 ChatClient 中 ChatModel 的 Options 参数
|
|
||||||
.defaultOptions(
|
|
||||||
DashScopeChatOptions.builder()
|
|
||||||
.withTopP(0.7)
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ChatClient 流式调用
|
|
||||||
*/
|
|
||||||
@GetMapping("/stream/chat")
|
|
||||||
public Flux<String> streamChat(@RequestParam(value = "query", defaultValue = "你好,很高兴认识你,能简单介绍一下自己吗?") String query, HttpServletResponse response) {
|
|
||||||
response.setCharacterEncoding("UTF-8");
|
|
||||||
return dashScopeChatClient.prompt(query).stream().content();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
package org.dromara.ai.domain;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.dromara.common.mybatis.core.domain.BaseEntity;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI 对话记录信息对象 ai_chat_memory
|
||||||
|
*
|
||||||
|
* @author lilemy
|
||||||
|
* @date 2025-11-04
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@TableName("ai_chat_memory")
|
||||||
|
public class AIChatMemory extends BaseEntity {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键
|
||||||
|
*/
|
||||||
|
@TableId(value = "id")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户id
|
||||||
|
*/
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件名
|
||||||
|
*/
|
||||||
|
private String fileName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 第一条问题
|
||||||
|
*/
|
||||||
|
private String firstQuestion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备注
|
||||||
|
*/
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
package org.dromara.ai.domain.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author lilemy
|
||||||
|
* @date 2025-11-04 15:19
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class AIChatMemoryQueryReq implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = -4090176451164739134L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户id
|
||||||
|
*/
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件名
|
||||||
|
*/
|
||||||
|
private String fileName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 第一条问题
|
||||||
|
*/
|
||||||
|
private String firstQuestion;
|
||||||
|
}
|
||||||
@ -0,0 +1,58 @@
|
|||||||
|
package org.dromara.ai.domain.vo;
|
||||||
|
|
||||||
|
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
|
||||||
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
|
import io.github.linpeilie.annotations.AutoMapper;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.dromara.ai.domain.AIChatMemory;
|
||||||
|
|
||||||
|
import java.io.Serial;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI 对话记录信息视图对象 ai_chat_memory
|
||||||
|
*
|
||||||
|
* @author lilemy
|
||||||
|
* @date 2025-11-04
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ExcelIgnoreUnannotated
|
||||||
|
@AutoMapper(target = AIChatMemory.class)
|
||||||
|
public class AIChatMemoryVo implements Serializable {
|
||||||
|
|
||||||
|
@Serial
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "主键")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户id
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "用户id")
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 文件名
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "文件名")
|
||||||
|
private String fileName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 第一条问题
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "第一条问题")
|
||||||
|
private String firstQuestion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备注
|
||||||
|
*/
|
||||||
|
@ExcelProperty(value = "备注")
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
package org.dromara.ai.mapper;
|
||||||
|
|
||||||
|
import org.dromara.ai.domain.AIChatMemory;
|
||||||
|
import org.dromara.ai.domain.vo.AIChatMemoryVo;
|
||||||
|
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI 对话记录信息Mapper接口
|
||||||
|
*
|
||||||
|
* @author lilemy
|
||||||
|
* @date 2025-11-04
|
||||||
|
*/
|
||||||
|
public interface AIChatMemoryMapper extends BaseMapperPlus<AIChatMemory, AIChatMemoryVo> {
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
package org.dromara.ai.service;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import org.dromara.ai.domain.AIChatMemory;
|
||||||
|
import org.dromara.ai.domain.dto.AIChatMemoryQueryReq;
|
||||||
|
import org.dromara.ai.domain.vo.AIChatMemoryVo;
|
||||||
|
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||||
|
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI 对话记录信息Service接口
|
||||||
|
*
|
||||||
|
* @author lilemy
|
||||||
|
* @date 2025-11-04
|
||||||
|
*/
|
||||||
|
public interface IAIChatMemoryService extends IService<AIChatMemory>{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询AI 对话记录信息
|
||||||
|
*
|
||||||
|
* @param id 主键
|
||||||
|
* @return AI 对话记录信息
|
||||||
|
*/
|
||||||
|
AIChatMemoryVo queryById(Long id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询AI 对话记录信息列表
|
||||||
|
*
|
||||||
|
* @param bo 查询条件
|
||||||
|
* @param pageQuery 分页参数
|
||||||
|
* @return AI 对话记录信息分页列表
|
||||||
|
*/
|
||||||
|
TableDataInfo<AIChatMemoryVo> queryPageList(AIChatMemoryQueryReq bo, PageQuery pageQuery);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询符合条件的AI 对话记录信息列表
|
||||||
|
*
|
||||||
|
* @param bo 查询条件
|
||||||
|
* @return AI 对话记录信息列表
|
||||||
|
*/
|
||||||
|
List<AIChatMemoryVo> queryList(AIChatMemoryQueryReq bo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验并批量删除AI 对话记录信息信息
|
||||||
|
*
|
||||||
|
* @param ids 待删除的主键集合
|
||||||
|
* @param isValid 是否进行有效性校验
|
||||||
|
* @return 是否删除成功
|
||||||
|
*/
|
||||||
|
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
|
||||||
|
}
|
||||||
@ -0,0 +1,90 @@
|
|||||||
|
package org.dromara.ai.service.impl;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import org.dromara.ai.domain.AIChatMemory;
|
||||||
|
import org.dromara.ai.domain.dto.AIChatMemoryQueryReq;
|
||||||
|
import org.dromara.ai.domain.vo.AIChatMemoryVo;
|
||||||
|
import org.dromara.ai.mapper.AIChatMemoryMapper;
|
||||||
|
import org.dromara.ai.service.IAIChatMemoryService;
|
||||||
|
import org.dromara.common.core.utils.StringUtils;
|
||||||
|
import org.dromara.common.mybatis.core.page.PageQuery;
|
||||||
|
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* AI 对话记录信息Service业务层处理
|
||||||
|
*
|
||||||
|
* @author lilemy
|
||||||
|
* @date 2025-11-04
|
||||||
|
*/
|
||||||
|
@Service
|
||||||
|
public class AIChatMemoryServiceImpl extends ServiceImpl<AIChatMemoryMapper, AIChatMemory>
|
||||||
|
implements IAIChatMemoryService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询AI 对话记录信息
|
||||||
|
*
|
||||||
|
* @param id 主键
|
||||||
|
* @return AI 对话记录信息
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public AIChatMemoryVo queryById(Long id) {
|
||||||
|
return baseMapper.selectVoById(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询AI 对话记录信息列表
|
||||||
|
*
|
||||||
|
* @param req 查询条件
|
||||||
|
* @param pageQuery 分页参数
|
||||||
|
* @return AI 对话记录信息分页列表
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public TableDataInfo<AIChatMemoryVo> queryPageList(AIChatMemoryQueryReq req, PageQuery pageQuery) {
|
||||||
|
LambdaQueryWrapper<AIChatMemory> lqw = buildQueryWrapper(req);
|
||||||
|
Page<AIChatMemoryVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
||||||
|
return TableDataInfo.build(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询符合条件的AI 对话记录信息列表
|
||||||
|
*
|
||||||
|
* @param req 查询条件
|
||||||
|
* @return AI 对话记录信息列表
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public List<AIChatMemoryVo> queryList(AIChatMemoryQueryReq req) {
|
||||||
|
LambdaQueryWrapper<AIChatMemory> lqw = buildQueryWrapper(req);
|
||||||
|
return baseMapper.selectVoList(lqw);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LambdaQueryWrapper<AIChatMemory> buildQueryWrapper(AIChatMemoryQueryReq req) {
|
||||||
|
LambdaQueryWrapper<AIChatMemory> lqw = Wrappers.lambdaQuery();
|
||||||
|
lqw.orderByDesc(AIChatMemory::getId);
|
||||||
|
lqw.eq(req.getUserId() != null, AIChatMemory::getUserId, req.getUserId());
|
||||||
|
lqw.like(StringUtils.isNotBlank(req.getFileName()), AIChatMemory::getFileName, req.getFileName());
|
||||||
|
lqw.eq(StringUtils.isNotBlank(req.getFirstQuestion()), AIChatMemory::getFirstQuestion, req.getFirstQuestion());
|
||||||
|
return lqw;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验并批量删除AI 对话记录信息信息
|
||||||
|
*
|
||||||
|
* @param ids 待删除的主键集合
|
||||||
|
* @param isValid 是否进行有效性校验
|
||||||
|
* @return 是否删除成功
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
||||||
|
if (isValid) {
|
||||||
|
//TODO 做一些业务上的校验,判断是否需要校验
|
||||||
|
}
|
||||||
|
return baseMapper.deleteByIds(ids) > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -13,17 +13,17 @@ import java.util.stream.Collectors;
|
|||||||
@Getter
|
@Getter
|
||||||
public enum RecognizerTypeEnum {
|
public enum RecognizerTypeEnum {
|
||||||
|
|
||||||
WEARING_ALL("穿戴安全帽反光衣", "wearingall", ""),
|
WEARING_ALL("穿戴安全帽反光衣", "wearingall", "1"),
|
||||||
NO_EQUIPMENT("没穿安全帽反光衣", "noequipment", "1"),
|
NO_EQUIPMENT("没穿安全帽反光衣", "noequipment", "2"),
|
||||||
NO_HELMET("有反光衣没安全帽", "nohelmet", ""),
|
NO_HELMET("有反光衣没安全帽", "nohelmet", "3"),
|
||||||
NO_VEST("有安全帽没反光衣", "novest", ""),
|
NO_VEST("有安全帽没反光衣", "novest", "4"),
|
||||||
SMOKE("吸烟", "smoke", "3"),
|
SMOKE("吸烟", "smoke", "5"),
|
||||||
FIRE("火焰", "fire", "16"),
|
FIRE("火焰", "fire", "6"),
|
||||||
SMOGGY("烟雾", "smoggy", ""),
|
SMOGGY("烟雾", "smoggy", "7"),
|
||||||
PANEL("光伏板", "solar", ""),
|
PANEL("光伏板", "solar", "8"),
|
||||||
BRACKET("光伏板支架", "bracket", ""),
|
BRACKET("光伏板支架", "bracket", "9"),
|
||||||
COLUMN("光伏板桩", "column", ""),
|
COLUMN("光伏板桩", "column", "10"),
|
||||||
HOLE("光伏板孔", "hole", "");
|
HOLE("光伏板孔", "hole", "11");
|
||||||
|
|
||||||
private final String text;
|
private final String text;
|
||||||
|
|
||||||
|
|||||||
@ -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.AIChatMemoryMapper">
|
||||||
|
|
||||||
|
</mapper>
|
||||||
@ -1904,21 +1904,52 @@ create table mat_warehouse
|
|||||||
|
|
||||||
|
|
||||||
-- 菜单 SQL
|
-- 菜单 SQL
|
||||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible,
|
||||||
values(1983374316882939905, '物资仓库', '1953994827229114369', '1', 'warehouse', 'materials/warehouse/index', 1, 0, 'C', '0', '0', 'materials:warehouse:list', '#', 103, 1, sysdate(), null, null, '物资仓库菜单');
|
status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||||
|
values (1983374316882939905, '物资仓库', '1953994827229114369', '1', 'warehouse', 'materials/warehouse/index', 1, 0,
|
||||||
|
'C', '0', '0', 'materials:warehouse:list', '#', 103, 1, sysdate(), null, null, '物资仓库菜单');
|
||||||
|
|
||||||
-- 按钮 SQL
|
-- 按钮 SQL
|
||||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible,
|
||||||
values(1983374316882939906, '物资仓库查询', 1983374316882939905, '1', '#', '', 1, 0, 'F', '0', '0', 'materials:warehouse:query', '#', 103, 1, sysdate(), null, null, '');
|
status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||||
|
values (1983374316882939906, '物资仓库查询', 1983374316882939905, '1', '#', '', 1, 0, 'F', '0', '0',
|
||||||
|
'materials:warehouse:query', '#', 103, 1, sysdate(), null, null, '');
|
||||||
|
|
||||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible,
|
||||||
values(1983374316882939907, '物资仓库新增', 1983374316882939905, '2', '#', '', 1, 0, 'F', '0', '0', 'materials:warehouse:add', '#', 103, 1, sysdate(), null, null, '');
|
status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||||
|
values (1983374316882939907, '物资仓库新增', 1983374316882939905, '2', '#', '', 1, 0, 'F', '0', '0',
|
||||||
|
'materials:warehouse:add', '#', 103, 1, sysdate(), null, null, '');
|
||||||
|
|
||||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible,
|
||||||
values(1983374316882939908, '物资仓库修改', 1983374316882939905, '3', '#', '', 1, 0, 'F', '0', '0', 'materials:warehouse:edit', '#', 103, 1, sysdate(), null, null, '');
|
status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||||
|
values (1983374316882939908, '物资仓库修改', 1983374316882939905, '3', '#', '', 1, 0, 'F', '0', '0',
|
||||||
|
'materials:warehouse:edit', '#', 103, 1, sysdate(), null, null, '');
|
||||||
|
|
||||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible,
|
||||||
values(1983374316882939909, '物资仓库删除', 1983374316882939905, '4', '#', '', 1, 0, 'F', '0', '0', 'materials:warehouse:remove', '#', 103, 1, sysdate(), null, null, '');
|
status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||||
|
values (1983374316882939909, '物资仓库删除', 1983374316882939905, '4', '#', '', 1, 0, 'F', '0', '0',
|
||||||
|
'materials:warehouse:remove', '#', 103, 1, sysdate(), null, null, '');
|
||||||
|
|
||||||
|
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible,
|
||||||
|
status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
||||||
|
values (1983374316882939910, '物资仓库导出', 1983374316882939905, '5', '#', '', 1, 0, 'F', '0', '0',
|
||||||
|
'materials:warehouse:export', '#', 103, 1, sysdate(), null, null, '');
|
||||||
|
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS ai_chat_memory;
|
||||||
|
CREATE TABLE `ai_chat_memory`
|
||||||
|
(
|
||||||
|
`id` bigint not null auto_increment comment '主键',
|
||||||
|
`user_id` bigint not null comment '用户id',
|
||||||
|
`file_name` varchar(64) not null comment '文件名',
|
||||||
|
`first_question` varchar(128) not null comment '第一条问题',
|
||||||
|
`remark` varchar(255) null comment '备注',
|
||||||
|
`create_by` bigint null comment '创建者',
|
||||||
|
`update_by` bigint null comment '更新者',
|
||||||
|
`create_dept` bigint null comment '创建部门',
|
||||||
|
`create_time` datetime default CURRENT_TIMESTAMP null comment '创建时间',
|
||||||
|
`update_time` datetime default CURRENT_TIMESTAMP null on update CURRENT_TIMESTAMP comment '更新时间',
|
||||||
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
|
INDEX `idx_user_id` (`user_id` ASC) USING BTREE comment '用户id'
|
||||||
|
) comment = 'AI 对话记录信息';
|
||||||
|
|
||||||
insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark)
|
|
||||||
values(1983374316882939910, '物资仓库导出', 1983374316882939905, '5', '#', '', 1, 0, 'F', '0', '0', 'materials:warehouse:export', '#', 103, 1, sysdate(), null, null, '');
|
|
||||||
|
|||||||
Reference in New Issue
Block a user