mirror of
https://gitcode.com/ageerle/ruoyi-ai.git
synced 2026-04-18 22:33:38 +00:00
Compare commits
2 Commits
731352fd04
...
a4314dbbde
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a4314dbbde | ||
|
|
188dc1e55e |
@@ -322,6 +322,9 @@ wechat:
|
|||||||
|
|
||||||
spring:
|
spring:
|
||||||
ai:
|
ai:
|
||||||
|
openai:
|
||||||
|
api-key: sk-xX
|
||||||
|
base-url: https://api.pandarobot.chat/
|
||||||
ollama:
|
ollama:
|
||||||
base-url: http://localhost:11434
|
base-url: http://localhost:11434
|
||||||
mcp:
|
mcp:
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ public class ChatRequest {
|
|||||||
/**
|
/**
|
||||||
* 用户id
|
* 用户id
|
||||||
*/
|
*/
|
||||||
private String userId;
|
private Long userId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 应用ID
|
* 应用ID
|
||||||
|
|||||||
83
ruoyi-extend/call-mcp-server/pom.xml
Normal file
83
ruoyi-extend/call-mcp-server/pom.xml
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
<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/maven-v4_0_0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.ruoyi</groupId>
|
||||||
|
<artifactId>ruoyi-ai</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
<relativePath>../../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
<artifactId>call-mcp-server</artifactId>
|
||||||
|
<name>Archetype - call-mcp-server</name>
|
||||||
|
<url>http://maven.apache.org</url>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<java.version>17</java.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.ai</groupId>
|
||||||
|
<artifactId>spring-ai-bom</artifactId>
|
||||||
|
<version>1.0.0-M6</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-thymeleaf</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.ai</groupId>
|
||||||
|
<artifactId>spring-ai-mcp-client-spring-boot-starter</artifactId>
|
||||||
|
<version>1.0.0-M6</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.ai</groupId>
|
||||||
|
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.ai</groupId>
|
||||||
|
<artifactId>spring-ai-mcp</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
package org.ruoyi.rocket.callmcpserver;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
|
||||||
|
@SpringBootApplication
|
||||||
|
public class CallMcpServerApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(CallMcpServerApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package org.ruoyi.rocket.callmcpserver.cofing;
|
||||||
|
|
||||||
|
import io.modelcontextprotocol.client.McpClient;
|
||||||
|
import org.springframework.ai.mcp.customizer.McpSyncClientCustomizer;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author ageer
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
public class McpClientCfg implements McpSyncClientCustomizer {
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void customize(String name, McpClient.SyncSpec spec) {
|
||||||
|
// do nothing
|
||||||
|
spec.requestTimeout(Duration.ofSeconds(30));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
package org.ruoyi.rocket.callmcpserver.view;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
|
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.memory.InMemoryChatMemory;
|
||||||
|
import org.springframework.ai.chat.model.ChatResponse;
|
||||||
|
import org.springframework.ai.openai.OpenAiChatOptions;
|
||||||
|
import org.springframework.ai.tool.ToolCallbackProvider;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author jianzhang
|
||||||
|
* 2025/03/18/下午8:00
|
||||||
|
*/
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/dashscope/chat-client")
|
||||||
|
public class ChatController {
|
||||||
|
|
||||||
|
private final ChatClient chatClient;
|
||||||
|
|
||||||
|
private final ChatMemory chatMemory = new InMemoryChatMemory();
|
||||||
|
|
||||||
|
public ChatController(ChatClient.Builder chatClientBuilder,ToolCallbackProvider tools) {
|
||||||
|
this.chatClient = chatClientBuilder
|
||||||
|
.defaultTools(tools)
|
||||||
|
.defaultOptions(
|
||||||
|
OpenAiChatOptions.builder().model("gpt-4o-mini").build())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value = "/generate_stream", method = RequestMethod.GET)
|
||||||
|
public Flux<ChatResponse> generateStream(HttpServletResponse response, @RequestParam("id") String id, @RequestParam("prompt") String prompt) {
|
||||||
|
response.setCharacterEncoding("UTF-8");
|
||||||
|
var messageChatMemoryAdvisor = new MessageChatMemoryAdvisor(chatMemory, id, 10);
|
||||||
|
|
||||||
|
|
||||||
|
Flux<ChatResponse> chatResponseFlux = this.chatClient.prompt(prompt)
|
||||||
|
.advisors(messageChatMemoryAdvisor)
|
||||||
|
.stream()
|
||||||
|
.chatResponse();
|
||||||
|
|
||||||
|
Flux<String> content = this.chatClient.prompt(prompt)
|
||||||
|
.advisors(messageChatMemoryAdvisor)
|
||||||
|
.stream()
|
||||||
|
.content();
|
||||||
|
|
||||||
|
content.subscribe(
|
||||||
|
content1 -> System.out.println("chatResponse"+content1)
|
||||||
|
);
|
||||||
|
return chatResponseFlux;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@GetMapping("/advisor/chat/{id}/{prompt}")
|
||||||
|
public Flux<String> advisorChat(
|
||||||
|
HttpServletResponse response,
|
||||||
|
@PathVariable String id,
|
||||||
|
@PathVariable String prompt) {
|
||||||
|
|
||||||
|
response.setCharacterEncoding("UTF-8");
|
||||||
|
var messageChatMemoryAdvisor = new MessageChatMemoryAdvisor(chatMemory, id, 10);
|
||||||
|
return this.chatClient.prompt(prompt)
|
||||||
|
.advisors(messageChatMemoryAdvisor).stream().content();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package org.ruoyi.rocket.callmcpserver.view;
|
||||||
|
|
||||||
|
import org.springframework.stereotype.Controller;
|
||||||
|
import org.springframework.ui.Model;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author jianzhang
|
||||||
|
* 2025/03/18/下午8:00
|
||||||
|
*/
|
||||||
|
@Controller
|
||||||
|
public class IndexController {
|
||||||
|
|
||||||
|
@GetMapping("/")
|
||||||
|
public String chat(Model model) {
|
||||||
|
//model.addAttribute("name", "User");
|
||||||
|
// 返回视图名称,对应 templates/index.html
|
||||||
|
return "index";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
server:
|
||||||
|
port: 9999
|
||||||
|
spring:
|
||||||
|
ai:
|
||||||
|
openai:
|
||||||
|
api-key: sk-xXe1WMPjhlVb1aiI1b4c6c8934D8463f9e4b67Ed8718B772
|
||||||
|
base-url: https://api.pandarobot.chat/
|
||||||
|
mcp:
|
||||||
|
client:
|
||||||
|
enabled: true
|
||||||
|
name: call-mcp-server
|
||||||
|
sse:
|
||||||
|
connections:
|
||||||
|
server1:
|
||||||
|
url: http://127.0.0.1:6040
|
||||||
|
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"fileSystem": {
|
||||||
|
"command": "D:\\software\\nodeJs\\npx.cmd",
|
||||||
|
"args": [
|
||||||
|
"-y",
|
||||||
|
"@modelcontextprotocol/server-filesystem",
|
||||||
|
"D:\\software\\sqlite"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sqlLite": {
|
||||||
|
"command": "D:\\Program Files\\python3.12.3\\Scripts\\uvx.exe",
|
||||||
|
"args": [
|
||||||
|
"mcp-server-sqlite",
|
||||||
|
"--db-path",
|
||||||
|
"D:\\work-space-study\\spring-ai-mcp-demo\\mcp-client\\src\\main\\resources\\test.db"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"fetch": {
|
||||||
|
"command": "D:\\Program Files\\python3.12.3\\Scripts\\uvx.exe",
|
||||||
|
"args": [
|
||||||
|
"mcp-server-fetch"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"baidu-map": {
|
||||||
|
"command": "D:\\Program Files\\python3.12.3\\Scripts\\uvx.exe",
|
||||||
|
"args": [
|
||||||
|
"run",
|
||||||
|
"--with",
|
||||||
|
"mcp[cli]",
|
||||||
|
"mcp",
|
||||||
|
"run",
|
||||||
|
"D:\\work-space-python\\python-baidu-map\\baidu_map_mcp_server\\map.py"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"BAIDU_MAPS_API_KEY": "{百度地图API-KEY}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"mcpServers": {
|
||||||
|
"fileSystem": {
|
||||||
|
"command": "D:\\software\\nodeJs\\npx.cmd",
|
||||||
|
"args": [
|
||||||
|
"-y",
|
||||||
|
"@modelcontextprotocol/server-filesystem",
|
||||||
|
"D:\\software\\sqlite"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sqlLite": {
|
||||||
|
"command": "D:\\Program Files\\python3.12.3\\Scripts\\uvx.exe",
|
||||||
|
"args": [
|
||||||
|
"mcp-server-sqlite",
|
||||||
|
"--db-path",
|
||||||
|
"D:\\work-space-study\\spring-ai-mcp-demo\\mcp-client\\src\\main\\resources\\test.db"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>AI 对话助手</title>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
</head>
|
||||||
|
<body class="bg-gray-100 min-h-screen">
|
||||||
|
<div class="container mx-auto p-4 max-w-3xl">
|
||||||
|
<!-- 标题 -->
|
||||||
|
<div class="text-center mb-8">
|
||||||
|
<h1 class="text-3xl font-bold text-gray-800">AI 对话助手</h1>
|
||||||
|
<p class="text-gray-600 mt-2">基于 Spring AI 的流式对话系统 By AhuCodingBeast</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 聊天容器 -->
|
||||||
|
<div id="chat-container" class="bg-white rounded-xl shadow-lg p-4 mb-4 h-[500px] overflow-y-auto space-y-4">
|
||||||
|
<!-- 初始欢迎消息 -->
|
||||||
|
<div class="ai-message flex items-start gap-3">
|
||||||
|
<div class="bg-green-100 p-3 rounded-lg max-w-[85%]">
|
||||||
|
<span class="text-gray-800">您好!我是AI助手,有什么可以帮您?</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 输入区域 -->
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<input type="text" id="message-input"
|
||||||
|
class="flex-1 border border-gray-300 rounded-xl px-4 py-3 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
placeholder="输入您的问题...">
|
||||||
|
<button id="send-button"
|
||||||
|
class="bg-blue-500 text-white px-6 py-3 rounded-xl hover:bg-blue-600 transition-colors flex items-center">
|
||||||
|
<span>发送</span>
|
||||||
|
<svg id="loading-spinner" class="hidden w-4 h-4 ml-2 animate-spin" fill="none" viewBox="0 0 24 24">
|
||||||
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||||
|
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const chatContainer = document.getElementById('chat-container');
|
||||||
|
const messageInput = document.getElementById('message-input');
|
||||||
|
const sendButton = document.getElementById('send-button');
|
||||||
|
const loadingSpinner = document.getElementById('loading-spinner');
|
||||||
|
|
||||||
|
// 发送消息处理
|
||||||
|
function handleSend() {
|
||||||
|
const message = messageInput.value.trim();
|
||||||
|
if (!message) return;
|
||||||
|
|
||||||
|
// 添加用户消息
|
||||||
|
addMessage(message, 'user');
|
||||||
|
messageInput.value = '';
|
||||||
|
|
||||||
|
// 构建API URL
|
||||||
|
const apiUrl = new URL('http://localhost:9999/dashscope/chat-client/generate_stream');
|
||||||
|
apiUrl.searchParams.append('id', '01');
|
||||||
|
apiUrl.searchParams.append('prompt', message);
|
||||||
|
|
||||||
|
// 显示加载状态
|
||||||
|
sendButton.disabled = true;
|
||||||
|
loadingSpinner.classList.remove('hidden');
|
||||||
|
|
||||||
|
// 创建EventSource连接
|
||||||
|
const eventSource = new EventSource(apiUrl);
|
||||||
|
let aiMessageElement = null;
|
||||||
|
|
||||||
|
eventSource.onmessage = (event) => {
|
||||||
|
try {
|
||||||
|
const data = JSON.parse(event.data);
|
||||||
|
console.log(data);
|
||||||
|
const content = data.result?.output?.text || '';
|
||||||
|
const finishReason = data.result?.metadata?.finishReason;
|
||||||
|
|
||||||
|
// 创建消息容器(如果不存在)
|
||||||
|
if (!aiMessageElement) {
|
||||||
|
aiMessageElement = addMessage('', 'ai');
|
||||||
|
}
|
||||||
|
|
||||||
|
// 追加内容
|
||||||
|
if (content) {
|
||||||
|
aiMessageElement.querySelector('.message-content').textContent += content;
|
||||||
|
autoScroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理结束
|
||||||
|
if (finishReason === 'STOP') {
|
||||||
|
eventSource.close();
|
||||||
|
sendButton.disabled = false;
|
||||||
|
loadingSpinner.classList.add('hidden');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('解析错误:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
eventSource.onerror = (error) => {
|
||||||
|
console.error('连接错误:', error);
|
||||||
|
eventSource.close();
|
||||||
|
sendButton.disabled = false;
|
||||||
|
loadingSpinner.classList.add('hidden');
|
||||||
|
addMessage('对话连接异常,请重试', 'ai', true);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加消息到容器
|
||||||
|
function addMessage(content, type, isError = false) {
|
||||||
|
const messageDiv = document.createElement('div');
|
||||||
|
messageDiv.className = `${type}-message flex items-start gap-3`;
|
||||||
|
|
||||||
|
const bubble = document.createElement('div');
|
||||||
|
bubble.className = `p-3 rounded-lg max-w-[85%] ${
|
||||||
|
type === 'user'
|
||||||
|
? 'bg-blue-500 text-white ml-auto'
|
||||||
|
: `bg-green-100 ${isError ? 'text-red-500' : 'text-gray-800'}`
|
||||||
|
}`;
|
||||||
|
|
||||||
|
const contentSpan = document.createElement('span');
|
||||||
|
contentSpan.className = 'message-content';
|
||||||
|
contentSpan.textContent = content;
|
||||||
|
|
||||||
|
bubble.appendChild(contentSpan);
|
||||||
|
messageDiv.appendChild(bubble);
|
||||||
|
chatContainer.appendChild(messageDiv);
|
||||||
|
|
||||||
|
autoScroll();
|
||||||
|
return bubble;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自动滚动到底部
|
||||||
|
function autoScroll() {
|
||||||
|
chatContainer.scrollTop = chatContainer.scrollHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 事件监听
|
||||||
|
sendButton.addEventListener('click', handleSend);
|
||||||
|
messageInput.addEventListener('keypress', (e) => {
|
||||||
|
if (e.key === 'Enter' && !e.shiftKey) {
|
||||||
|
e.preventDefault();
|
||||||
|
handleSend();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -49,16 +49,16 @@
|
|||||||
<artifactId>spring-ai-mcp</artifactId>
|
<artifactId>spring-ai-mcp</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<!-- <dependency>-->
|
||||||
<groupId>org.ruoyi</groupId>
|
<!-- <groupId>org.ruoyi</groupId>-->
|
||||||
<artifactId>ruoyi-system-api</artifactId>
|
<!-- <artifactId>ruoyi-system-api</artifactId>-->
|
||||||
<exclusions>
|
<!-- <exclusions>-->
|
||||||
<exclusion>
|
<!-- <exclusion>-->
|
||||||
<groupId>org.ruoyi</groupId>
|
<!-- <groupId>org.ruoyi</groupId>-->
|
||||||
<artifactId>ruoyi-common-translation</artifactId>
|
<!-- <artifactId>ruoyi-common-translation</artifactId>-->
|
||||||
</exclusion>
|
<!-- </exclusion>-->
|
||||||
</exclusions>
|
<!-- </exclusions>-->
|
||||||
</dependency>
|
<!-- </dependency>-->
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
package org.ruoyi.mcp.service;
|
package org.ruoyi.mcp.service;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.ruoyi.system.domain.vo.SysUserVo;
|
|
||||||
import org.ruoyi.system.mapper.SysUserMapper;
|
|
||||||
import org.springframework.ai.tool.annotation.Tool;
|
import org.springframework.ai.tool.annotation.Tool;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@@ -10,18 +7,14 @@ import org.springframework.stereotype.Service;
|
|||||||
* @author ageer
|
* @author ageer
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
@RequiredArgsConstructor
|
|
||||||
public class McpCustomService {
|
public class McpCustomService {
|
||||||
|
|
||||||
private final SysUserMapper userMapper;
|
public record User(String userName, String userBalance) {
|
||||||
|
|
||||||
public record User(String userName, Double userBalance) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Tool(description = "根据用户名称查询用户信息")
|
@Tool(description = "根据用户名称查询用户信息")
|
||||||
public User getUserBalance(String username) {
|
public User getUserBalance(String username) {
|
||||||
SysUserVo sysUserVo = userMapper.selectUserByUserName(username);
|
return new User("admin","99.99");
|
||||||
return new User(sysUserVo.getUserName(),sysUserVo.getUserBalance());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,97 +0,0 @@
|
|||||||
--- # 监控中心配置
|
|
||||||
spring.boot.admin.client:
|
|
||||||
# 增加客户端开关
|
|
||||||
enabled: false
|
|
||||||
url: http://localhost:9090/admin
|
|
||||||
instance:
|
|
||||||
service-host-type: IP
|
|
||||||
username: ruoyi
|
|
||||||
password: 123456
|
|
||||||
|
|
||||||
--- # 数据源配置
|
|
||||||
spring:
|
|
||||||
datasource:
|
|
||||||
type: com.zaxxer.hikari.HikariDataSource
|
|
||||||
# 动态数据源文档 https://www.kancloud.cn/tracy5546/dynamic-datasource/content
|
|
||||||
dynamic:
|
|
||||||
# 性能分析插件(有性能损耗 不建议生产环境使用)
|
|
||||||
p6spy: true
|
|
||||||
# 设置默认的数据源或者数据源组,默认值即为 master
|
|
||||||
primary: master
|
|
||||||
# 严格模式 匹配不到数据源则报错
|
|
||||||
strict: true
|
|
||||||
datasource:
|
|
||||||
# 主库数据源
|
|
||||||
master:
|
|
||||||
type: ${spring.datasource.type}
|
|
||||||
driverClassName: com.mysql.cj.jdbc.Driver
|
|
||||||
url: jdbc:mysql://43.139.70.230:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&autoReconnect=true&rewriteBatchedStatements=true
|
|
||||||
username: ry-vue
|
|
||||||
password: xx
|
|
||||||
|
|
||||||
hikari:
|
|
||||||
# 最大连接池数量
|
|
||||||
maxPoolSize: 20
|
|
||||||
# 最小空闲线程数量
|
|
||||||
minIdle: 10
|
|
||||||
# 配置获取连接等待超时的时间
|
|
||||||
connectionTimeout: 30000
|
|
||||||
# 校验超时时间
|
|
||||||
validationTimeout: 5000
|
|
||||||
# 空闲连接存活最大时间,默认10分钟
|
|
||||||
idleTimeout: 600000
|
|
||||||
# 此属性控制池中连接的最长生命周期,值0表示无限生命周期,默认30分钟
|
|
||||||
maxLifetime: 1800000
|
|
||||||
# 连接测试query(配置检测连接是否有效)
|
|
||||||
connectionTestQuery: SELECT 1
|
|
||||||
# 多久检查一次连接的活性
|
|
||||||
keepaliveTime: 30000
|
|
||||||
|
|
||||||
--- # redis 单机配置(单机与集群只能开启一个另一个需要注释掉)
|
|
||||||
spring.data:
|
|
||||||
redis:
|
|
||||||
# 地址
|
|
||||||
host: 127.0.0.1
|
|
||||||
# 端口,默认为6379
|
|
||||||
port: 6379
|
|
||||||
# 数据库索引
|
|
||||||
database: 0
|
|
||||||
# 密码(如没有密码请注释掉)
|
|
||||||
# password: 123456
|
|
||||||
# 连接超时时间
|
|
||||||
timeout: 10S
|
|
||||||
|
|
||||||
redisson:
|
|
||||||
# redis key前缀
|
|
||||||
keyPrefix:
|
|
||||||
# 线程池数量
|
|
||||||
threads: 4
|
|
||||||
# Netty线程池数量
|
|
||||||
nettyThreads: 8
|
|
||||||
# 单节点配置
|
|
||||||
singleServerConfig:
|
|
||||||
# 客户端名称
|
|
||||||
clientName: ${ruoyi.name}
|
|
||||||
# 最小空闲连接数
|
|
||||||
connectionMinimumIdleSize: 8
|
|
||||||
# 连接池大小
|
|
||||||
connectionPoolSize: 32
|
|
||||||
# 连接空闲超时,单位:毫秒
|
|
||||||
idleConnectionTimeout: 10000
|
|
||||||
# 命令等待超时,单位:毫秒
|
|
||||||
timeout: 3000
|
|
||||||
# 发布和订阅连接池大小
|
|
||||||
subscriptionConnectionPoolSize: 50
|
|
||||||
|
|
||||||
--- # sms 短信
|
|
||||||
sms:
|
|
||||||
enabled: false
|
|
||||||
# 阿里云 dysmsapi.aliyuncs.com
|
|
||||||
# 腾讯云 sms.tencentcloudapi.com
|
|
||||||
endpoint: "dysmsapi.aliyuncs.com"
|
|
||||||
accessKeyId: xxxxxxx
|
|
||||||
accessKeySecret: xxxxxx
|
|
||||||
signName: 测试
|
|
||||||
# 腾讯专用
|
|
||||||
sdkAppId:
|
|
||||||
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
spring:
|
|
||||||
application:
|
|
||||||
name: mcp-server
|
|
||||||
ai:
|
|
||||||
mcp:
|
|
||||||
server:
|
|
||||||
name: webmvc-mcp-server
|
|
||||||
version: 1.0.0
|
|
||||||
type: SYNC
|
|
||||||
sse-message-endpoint: /mcp/messages
|
|
||||||
@@ -1,332 +1,12 @@
|
|||||||
|
|
||||||
# 项目相关配置
|
|
||||||
ruoyi:
|
|
||||||
# 名称
|
|
||||||
name: "ruoyi"
|
|
||||||
# 版本
|
|
||||||
version: ${revision}
|
|
||||||
# 版权年份
|
|
||||||
copyrightYear: 2025
|
|
||||||
# 实例演示开关
|
|
||||||
demoEnabled: true
|
|
||||||
# 获取ip地址开关
|
|
||||||
addressEnabled: false
|
|
||||||
|
|
||||||
captcha:
|
|
||||||
enable: false
|
|
||||||
# 页面 <参数设置> 可开启关闭 验证码校验
|
|
||||||
# 验证码类型 math 数组计算 char 字符验证
|
|
||||||
type: MATH
|
|
||||||
# line 线段干扰 circle 圆圈干扰 shear 扭曲干扰
|
|
||||||
category: CIRCLE
|
|
||||||
# 数字验证码位数
|
|
||||||
numberLength: 1
|
|
||||||
# 字符验证码长度
|
|
||||||
charLength: 4
|
|
||||||
|
|
||||||
# 开发环境配置
|
|
||||||
server:
|
server:
|
||||||
# 服务器的HTTP端口,默认为8080
|
|
||||||
port: 6040
|
port: 6040
|
||||||
servlet:
|
|
||||||
# 应用的访问路径
|
|
||||||
context-path: /
|
|
||||||
# undertow 配置
|
|
||||||
undertow:
|
|
||||||
# HTTP post内容的最大大小。当值为-1时,默认值为大小是无限的
|
|
||||||
max-http-post-size: -1
|
|
||||||
# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
|
|
||||||
# 每块buffer的空间大小,越小的空间被利用越充分
|
|
||||||
buffer-size: 512
|
|
||||||
# 是否分配的直接内存
|
|
||||||
direct-buffers: true
|
|
||||||
threads:
|
|
||||||
# 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
|
|
||||||
io: 8
|
|
||||||
# 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
|
|
||||||
worker: 256
|
|
||||||
# 用户配置
|
|
||||||
user:
|
|
||||||
password:
|
|
||||||
# 密码最大错误次数
|
|
||||||
maxRetryCount: 5
|
|
||||||
# 密码锁定时间(默认10分钟)
|
|
||||||
lockTime: 10
|
|
||||||
|
|
||||||
# Spring配置
|
|
||||||
spring:
|
spring:
|
||||||
application:
|
application:
|
||||||
name: ${ruoyi.name}
|
name: mcp-server
|
||||||
# 资源信息
|
|
||||||
messages:
|
|
||||||
# 国际化资源文件路径
|
|
||||||
basename: i18n/messages
|
|
||||||
profiles:
|
|
||||||
active: @profiles.active@
|
|
||||||
# 文件上传
|
|
||||||
servlet:
|
|
||||||
multipart:
|
|
||||||
# 单个文件大小
|
|
||||||
max-file-size: 50MB
|
|
||||||
# 设置总上传的文件大小
|
|
||||||
max-request-size: 200MB
|
|
||||||
mvc:
|
|
||||||
format:
|
|
||||||
date-time: yyyy-MM-dd HH:mm:ss
|
|
||||||
jackson:
|
|
||||||
# 日期格式化
|
|
||||||
date-format: yyyy-MM-dd HH:mm:ss
|
|
||||||
serialization:
|
|
||||||
# 格式化输出
|
|
||||||
indent_output: false
|
|
||||||
# 忽略无法转换的对象
|
|
||||||
fail_on_empty_beans: false
|
|
||||||
deserialization:
|
|
||||||
# 允许对象忽略json中不存在的属性
|
|
||||||
fail_on_unknown_properties: false
|
|
||||||
|
|
||||||
# Sa-Token配置
|
|
||||||
sa-token:
|
|
||||||
# token名称 (同时也是cookie名称)
|
|
||||||
token-name: Authorization
|
|
||||||
# token有效期 设为7天 (必定过期) 单位: 秒
|
|
||||||
timeout: 604800
|
|
||||||
# token临时有效期 (指定时间无操作就过期) 单位: 秒
|
|
||||||
activity-timeout: 604800
|
|
||||||
# 是否允许同一账号并发登录 (为true时允许一起登录, 为false时新登录挤掉旧登录)
|
|
||||||
is-concurrent: true
|
|
||||||
# 在多人登录同一账号时,是否共用一个token (为true时所有登录共用一个token, 为false时每次登录新建一个token)
|
|
||||||
is-share: false
|
|
||||||
# 是否尝试从header里读取token
|
|
||||||
is-read-header: true
|
|
||||||
# 是否尝试从cookie里读取token
|
|
||||||
is-read-cookie: false
|
|
||||||
# token前缀
|
|
||||||
token-prefix: "Bearer"
|
|
||||||
# jwt秘钥
|
|
||||||
jwt-secret-key: abcdefghijklmnopqrstuvwxyz
|
|
||||||
|
|
||||||
# security配置
|
|
||||||
security:
|
|
||||||
# 排除路径
|
|
||||||
excludes:
|
|
||||||
# 支付回调
|
|
||||||
- /pay/returnUrl
|
|
||||||
- /pay/notifyUrl
|
|
||||||
# 上传文件
|
|
||||||
- /resource/oss/upload
|
|
||||||
# 重置密码
|
|
||||||
- /auth/reset/password
|
|
||||||
# 聊天接口
|
|
||||||
- /chat
|
|
||||||
# 静态资源
|
|
||||||
- /*.html
|
|
||||||
- /**/*.html
|
|
||||||
- /**/*.css
|
|
||||||
- /**/*.js
|
|
||||||
# 公共路径
|
|
||||||
- /favicon.ico
|
|
||||||
- /error
|
|
||||||
# swagger 文档配置
|
|
||||||
- /*/api-docs
|
|
||||||
- /*/api-docs/**
|
|
||||||
# actuator 监控配置
|
|
||||||
- /actuator
|
|
||||||
- /actuator/**
|
|
||||||
# 多租户配置
|
|
||||||
tenant:
|
|
||||||
# 是否开启
|
|
||||||
enable: false
|
|
||||||
# 排除表
|
|
||||||
excludes:
|
|
||||||
- sys_menu
|
|
||||||
- sys_tenant
|
|
||||||
- sys_tenant_package
|
|
||||||
- sys_role_dept
|
|
||||||
- sys_role_menu
|
|
||||||
- sys_user_post
|
|
||||||
- sys_user_role
|
|
||||||
|
|
||||||
# MyBatisPlus配置
|
|
||||||
# https://baomidou.com/config/
|
|
||||||
mybatis-plus:
|
|
||||||
# 不支持多包, 如有需要可在注解配置 或 提升扫包等级
|
|
||||||
# 例如 com.**.**.mapper
|
|
||||||
mapperPackage: org.ruoyi.**.mapper
|
|
||||||
# 对应的 XML 文件位置
|
|
||||||
mapperLocations: classpath*:mapper/**/*Mapper.xml
|
|
||||||
# 实体扫描,多个package用逗号或者分号分隔
|
|
||||||
typeAliasesPackage: org.ruoyi.**.domain
|
|
||||||
# 启动时是否检查 MyBatis XML 文件的存在,默认不检查
|
|
||||||
checkConfigLocation: false
|
|
||||||
configuration:
|
|
||||||
# 自动驼峰命名规则(camel case)映射
|
|
||||||
mapUnderscoreToCamelCase: true
|
|
||||||
# MyBatis 自动映射策略
|
|
||||||
# NONE:不启用 PARTIAL:只对非嵌套 resultMap 自动映射 FULL:对所有 resultMap 自动映射
|
|
||||||
autoMappingBehavior: FULL
|
|
||||||
# MyBatis 自动映射时未知列或未知属性处理策
|
|
||||||
# NONE:不做处理 WARNING:打印相关警告 FAILING:抛出异常和详细信息
|
|
||||||
autoMappingUnknownColumnBehavior: NONE
|
|
||||||
# 更详细的日志输出 会有性能损耗 org.apache.ibatis.logging.stdout.StdOutImpl
|
|
||||||
# 关闭日志记录 (可单纯使用 p6spy 分析) org.apache.ibatis.logging.nologging.NoLoggingImpl
|
|
||||||
# 默认日志输出 org.apache.ibatis.logging.slf4j.Slf4jImpl
|
|
||||||
logImpl: org.apache.ibatis.logging.nologging.NoLoggingImpl
|
|
||||||
global-config:
|
|
||||||
# 是否打印 Logo banner
|
|
||||||
banner: true
|
|
||||||
dbConfig:
|
|
||||||
# 主键类型
|
|
||||||
# AUTO 自增 NONE 空 INPUT 用户输入 ASSIGN_ID 雪花 ASSIGN_UUID 唯一 UUID
|
|
||||||
idType: ASSIGN_ID
|
|
||||||
# 逻辑已删除值
|
|
||||||
logicDeleteValue: 2
|
|
||||||
# 逻辑未删除值
|
|
||||||
logicNotDeleteValue: 0
|
|
||||||
# 字段验证策略之 insert,在 insert 的时候的字段验证策略
|
|
||||||
# IGNORED 忽略 NOT_NULL 非NULL NOT_EMPTY 非空 DEFAULT 默认 NEVER 不加入 SQL
|
|
||||||
insertStrategy: NOT_NULL
|
|
||||||
# 字段验证策略之 update,在 update 的时候的字段验证策略
|
|
||||||
updateStrategy: NOT_NULL
|
|
||||||
# 字段验证策略之 select,在 select 的时候的字段验证策略既 wrapper 根据内部 entity 生成的 where 条件
|
|
||||||
where-strategy: NOT_NULL
|
|
||||||
|
|
||||||
# 数据加密
|
|
||||||
mybatis-encryptor:
|
|
||||||
# 是否开启加密
|
|
||||||
enable: false
|
|
||||||
# 默认加密算法
|
|
||||||
algorithm: BASE64
|
|
||||||
# 编码方式 BASE64/HEX。默认BASE64
|
|
||||||
encode: BASE64
|
|
||||||
# 安全秘钥 对称算法的秘钥 如:AES,SM4
|
|
||||||
password:
|
|
||||||
# 公私钥 非对称算法的公私钥 如:SM2,RSA
|
|
||||||
publicKey:
|
|
||||||
privateKey:
|
|
||||||
|
|
||||||
# Swagger配置
|
|
||||||
swagger:
|
|
||||||
info:
|
|
||||||
# 标题
|
|
||||||
title: '标题:${ruoyi.name}多租户管理系统_接口文档'
|
|
||||||
# 描述
|
|
||||||
description: '描述:用于管理集团旗下公司的人员信息,具体包括XXX,XXX模块...'
|
|
||||||
# 版本
|
|
||||||
version: '版本号: ${ruoyi.version}'
|
|
||||||
# 作者信息
|
|
||||||
contact:
|
|
||||||
name: ageerle
|
|
||||||
email: ageerle@163.com
|
|
||||||
url: https://gitee.com/ageerle/ruoyi-ai
|
|
||||||
components:
|
|
||||||
# 鉴权方式配置
|
|
||||||
security-schemes:
|
|
||||||
apiKey:
|
|
||||||
type: APIKEY
|
|
||||||
in: HEADER
|
|
||||||
name: ${sa-token.token-name}
|
|
||||||
|
|
||||||
springdoc:
|
|
||||||
api-docs:
|
|
||||||
# 是否开启接口文档
|
|
||||||
enabled: true
|
|
||||||
swagger-ui:
|
|
||||||
# 持久化认证数据
|
|
||||||
persistAuthorization: true
|
|
||||||
#这里定义了两个分组,可定义多个,也可以不定义
|
|
||||||
group-configs:
|
|
||||||
- group: 1.演示模块
|
|
||||||
packages-to-scan: org.ruoyi.demo
|
|
||||||
- group: 2.通用模块
|
|
||||||
packages-to-scan: org.ruoyi.web
|
|
||||||
- group: 3.系统模块
|
|
||||||
packages-to-scan: org.ruoyi.system
|
|
||||||
- group: 4.代码生成模块
|
|
||||||
packages-to-scan: org.ruoyi.generator
|
|
||||||
|
|
||||||
# 防止XSS攻击
|
|
||||||
xss:
|
|
||||||
# 过滤开关
|
|
||||||
enabled: true
|
|
||||||
# 排除链接(多个用逗号分隔)
|
|
||||||
excludes: /system/notice
|
|
||||||
# 匹配链接
|
|
||||||
urlPatterns: /system/*,/monitor/*,/tool/*
|
|
||||||
|
|
||||||
# 全局线程池相关配置
|
|
||||||
thread-pool:
|
|
||||||
# 是否开启线程池
|
|
||||||
enabled: false
|
|
||||||
# 队列最大长度
|
|
||||||
queueCapacity: 128
|
|
||||||
# 线程池维护线程所允许的空闲时间
|
|
||||||
keepAliveSeconds: 300
|
|
||||||
|
|
||||||
--- # 分布式锁 lock4j 全局配置
|
|
||||||
lock4j:
|
|
||||||
# 获取分布式锁超时时间,默认为 3000 毫秒
|
|
||||||
acquire-timeout: 3000
|
|
||||||
# 分布式锁的超时时间,默认为 30 秒
|
|
||||||
expire: 30000
|
|
||||||
|
|
||||||
--- # Actuator 监控端点的配置项
|
|
||||||
management:
|
|
||||||
endpoints:
|
|
||||||
web:
|
|
||||||
exposure:
|
|
||||||
include: '*'
|
|
||||||
endpoint:
|
|
||||||
health:
|
|
||||||
show-details: ALWAYS
|
|
||||||
logfile:
|
|
||||||
external-file: ./logs/sys-console.log
|
|
||||||
|
|
||||||
# websocket
|
|
||||||
websocket:
|
|
||||||
enabled: true
|
|
||||||
# 路径
|
|
||||||
path: '/resource/websocket'
|
|
||||||
# 设置访问源地址
|
|
||||||
allowedOrigins: '*'
|
|
||||||
|
|
||||||
# 微信小程序配置信息
|
|
||||||
wx:
|
|
||||||
miniapp:
|
|
||||||
configs:
|
|
||||||
- appid: # 你的appid
|
|
||||||
secret: # 你的secret
|
|
||||||
token: #微信小程序消息服务器配置的token
|
|
||||||
aesKey: #微信小程序消息服务器配置的EncodingAESKey
|
|
||||||
msgDataFormat: JSON
|
|
||||||
|
|
||||||
# 企业微信应用
|
|
||||||
wechat:
|
|
||||||
cp:
|
|
||||||
corpId:
|
|
||||||
appConfigs:
|
|
||||||
- agentId:
|
|
||||||
secret: ''
|
|
||||||
token: ''
|
|
||||||
aesKey: ''
|
|
||||||
|
|
||||||
spring:
|
|
||||||
ai:
|
ai:
|
||||||
openai:
|
|
||||||
api-key: sk-xX
|
|
||||||
base-url: https://api.pandarobot.chat
|
|
||||||
ollama:
|
|
||||||
base-url: http://localhost:11434
|
|
||||||
init:
|
|
||||||
pull-model-strategy: always
|
|
||||||
timeout: 60s
|
|
||||||
max-retries: 1
|
|
||||||
mcp:
|
mcp:
|
||||||
client:
|
server:
|
||||||
enabled: true
|
name: webmvc-mcp-server
|
||||||
name: call-mcp-server
|
version: 1.0.0
|
||||||
sse:
|
type: SYNC
|
||||||
connections:
|
sse-message-endpoint: /mcp/messages
|
||||||
server1:
|
|
||||||
url: http://127.0.0.1:8080
|
|
||||||
|
|
||||||
|
|||||||
@@ -60,17 +60,12 @@
|
|||||||
|
|
||||||
<!-- <dependency>-->
|
<!-- <dependency>-->
|
||||||
<!-- <groupId>org.springframework.ai</groupId>-->
|
<!-- <groupId>org.springframework.ai</groupId>-->
|
||||||
<!-- <artifactId>spring-ai-mcp</artifactId>-->
|
<!-- <artifactId>spring-ai-ollama-spring-boot-starter</artifactId>-->
|
||||||
<!-- </dependency>-->
|
|
||||||
|
|
||||||
<!-- <dependency>-->
|
|
||||||
<!-- <groupId>org.springframework.ai</groupId>-->
|
|
||||||
<!-- <artifactId>spring-ai-openai</artifactId>-->
|
|
||||||
<!-- </dependency>-->
|
<!-- </dependency>-->
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.ai</groupId>
|
<groupId>org.springframework.ai</groupId>
|
||||||
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
|
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|||||||
@@ -113,12 +113,6 @@
|
|||||||
<groupId>org.ruoyi</groupId>
|
<groupId>org.ruoyi</groupId>
|
||||||
<artifactId>ruoyi-system-api</artifactId>
|
<artifactId>ruoyi-system-api</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.ai</groupId>
|
|
||||||
<artifactId>spring-ai-ollama</artifactId>
|
|
||||||
<version>1.0.0-M6</version>
|
|
||||||
<scope>compile</scope>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import org.ruoyi.common.core.utils.SpringUtils;
|
|||||||
import org.ruoyi.common.core.utils.StringUtils;
|
import org.ruoyi.common.core.utils.StringUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@@ -34,13 +34,13 @@ import java.util.Objects;
|
|||||||
public class SSEEventSourceListener extends EventSourceListener {
|
public class SSEEventSourceListener extends EventSourceListener {
|
||||||
|
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
public SSEEventSourceListener(ResponseBodyEmitter emitter) {
|
public SSEEventSourceListener(SseEmitter emitter) {
|
||||||
this.emitter = emitter;
|
this.emitter = emitter;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResponseBodyEmitter emitter;
|
private SseEmitter emitter;
|
||||||
|
|
||||||
private StringBuilder stringBuffer;
|
private StringBuilder stringBuffer = new StringBuilder();
|
||||||
|
|
||||||
private String modelName;
|
private String modelName;
|
||||||
|
|
||||||
@@ -61,7 +61,6 @@ public class SSEEventSourceListener extends EventSourceListener {
|
|||||||
@Override
|
@Override
|
||||||
public void onEvent(@NotNull EventSource eventSource, String id, String type, String data) {
|
public void onEvent(@NotNull EventSource eventSource, String id, String type, String data) {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
if ("[DONE]".equals(data)) {
|
if ("[DONE]".equals(data)) {
|
||||||
//成功响应
|
//成功响应
|
||||||
emitter.complete();
|
emitter.complete();
|
||||||
@@ -72,25 +71,23 @@ public class SSEEventSourceListener extends EventSourceListener {
|
|||||||
chatCostService.deductToken(chatRequest);
|
chatCostService.deductToken(chatRequest);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 解析返回内容
|
|
||||||
ObjectMapper mapper = new ObjectMapper();
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
ChatCompletionResponse completionResponse = mapper.readValue(data, ChatCompletionResponse.class);
|
ChatCompletionResponse completionResponse = mapper.readValue(data, ChatCompletionResponse.class);
|
||||||
if(completionResponse == null || CollectionUtil.isEmpty(completionResponse.getChoices())){
|
if(completionResponse == null || CollectionUtil.isEmpty(completionResponse.getChoices())){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Object content = completionResponse.getChoices().get(0).getDelta().getContent();
|
Object content = completionResponse.getChoices().get(0).getDelta().getContent();
|
||||||
if(content == null){
|
|
||||||
content = completionResponse.getChoices().get(0).getDelta().getReasoningContent();
|
if(content != null ){
|
||||||
if(content == null) return;
|
if(StringUtils.isEmpty(modelName)){
|
||||||
|
modelName = completionResponse.getModel();
|
||||||
|
}
|
||||||
|
stringBuffer.append(content);
|
||||||
|
emitter.send(content);
|
||||||
}
|
}
|
||||||
if(StringUtils.isEmpty(modelName)){
|
|
||||||
modelName = completionResponse.getModel();
|
|
||||||
}
|
|
||||||
stringBuffer.append(content);
|
|
||||||
emitter.send(data);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("sse信息推送失败{}内容:{}",e.getMessage(),data);
|
emitter.completeWithError(e);
|
||||||
eventSource.cancel();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,4 +40,9 @@ public interface IChatCostService {
|
|||||||
* 判断用户是否付费
|
* 判断用户是否付费
|
||||||
*/
|
*/
|
||||||
void checkUserGrade();
|
void checkUserGrade();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取登录用户id
|
||||||
|
*/
|
||||||
|
Long getUserId();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,5 +22,5 @@ public interface IChatService {
|
|||||||
* 客户端发送消息到服务端
|
* 客户端发送消息到服务端
|
||||||
* @param chatRequest 请求对象
|
* @param chatRequest 请求对象
|
||||||
*/
|
*/
|
||||||
SseEmitter mcpChat(ChatRequest chatRequest,SseEmitter emitter);
|
void mcpChat(ChatRequest chatRequest,SseEmitter emitter);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.ruoyi.chat.enums.BillingType;
|
import org.ruoyi.chat.enums.BillingType;
|
||||||
import org.ruoyi.chat.enums.UserGradeType;
|
import org.ruoyi.chat.enums.UserGradeType;
|
||||||
import org.ruoyi.chat.service.chat.IChatCostService;
|
import org.ruoyi.chat.service.chat.IChatCostService;
|
||||||
|
import org.ruoyi.common.chat.config.LocalCache;
|
||||||
import org.ruoyi.common.chat.request.ChatRequest;
|
import org.ruoyi.common.chat.request.ChatRequest;
|
||||||
import org.ruoyi.common.chat.utils.TikTokensUtil;
|
import org.ruoyi.common.chat.utils.TikTokensUtil;
|
||||||
import org.ruoyi.common.core.domain.model.LoginUser;
|
import org.ruoyi.common.core.domain.model.LoginUser;
|
||||||
@@ -96,6 +97,12 @@ public class ChatCostServiceImpl implements IChatCostService {
|
|||||||
chatToken.setUserId(chatMessageBo.getUserId());
|
chatToken.setUserId(chatMessageBo.getUserId());
|
||||||
chatTokenService.editToken(chatToken);
|
chatTokenService.editToken(chatToken);
|
||||||
}
|
}
|
||||||
|
Object userId = LocalCache.CACHE.get("userId");
|
||||||
|
if(userId!=null){
|
||||||
|
chatMessageBo.setUserId((Long) userId);
|
||||||
|
}else {
|
||||||
|
chatMessageBo.setUserId(getUserId());
|
||||||
|
}
|
||||||
// 保存消息记录
|
// 保存消息记录
|
||||||
chatMessageService.insertByBo(chatMessageBo);
|
chatMessageService.insertByBo(chatMessageBo);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,24 +7,13 @@ import io.github.ollama4j.models.chat.OllamaChatRequestBuilder;
|
|||||||
import io.github.ollama4j.models.chat.OllamaChatRequestModel;
|
import io.github.ollama4j.models.chat.OllamaChatRequestModel;
|
||||||
import io.github.ollama4j.models.generate.OllamaStreamHandler;
|
import io.github.ollama4j.models.generate.OllamaStreamHandler;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.ruoyi.chat.service.chat.IChatService;
|
|
||||||
import org.ruoyi.chat.util.SSEUtil;
|
import org.ruoyi.chat.util.SSEUtil;
|
||||||
import org.ruoyi.common.chat.entity.chat.Message;
|
import org.ruoyi.common.chat.entity.chat.Message;
|
||||||
import org.ruoyi.common.chat.request.ChatRequest;
|
import org.ruoyi.common.chat.request.ChatRequest;
|
||||||
import org.ruoyi.domain.vo.ChatModelVo;
|
import org.ruoyi.domain.vo.ChatModelVo;
|
||||||
import org.ruoyi.service.IChatModelService;
|
import org.ruoyi.service.IChatModelService;
|
||||||
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.memory.InMemoryChatMemory;
|
|
||||||
import org.springframework.ai.chat.messages.UserMessage;
|
|
||||||
import org.springframework.ai.ollama.api.OllamaModel;
|
|
||||||
import org.springframework.ai.ollama.api.OllamaOptions;
|
|
||||||
import org.springframework.ai.tool.ToolCallbackProvider;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.http.MediaType;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -35,27 +24,11 @@ import java.util.concurrent.CompletableFuture;
|
|||||||
|
|
||||||
@Service
|
@Service
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class OllamaServiceImpl implements IChatService {
|
public class OllamaServiceImpl {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private IChatModelService chatModelService;
|
private IChatModelService chatModelService;
|
||||||
|
|
||||||
private final ChatClient chatClient;
|
|
||||||
|
|
||||||
private final ChatMemory chatMemory = new InMemoryChatMemory();
|
|
||||||
|
|
||||||
public OllamaServiceImpl(ChatClient.Builder chatClientBuilder,ToolCallbackProvider tools) {
|
|
||||||
this.chatClient = chatClientBuilder
|
|
||||||
.defaultTools(tools)
|
|
||||||
.defaultOptions(
|
|
||||||
OllamaOptions.builder()
|
|
||||||
.model(OllamaModel.QWEN_2_5_7B)
|
|
||||||
.temperature(0.4)
|
|
||||||
.build())
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SseEmitter chat(ChatRequest chatRequest,SseEmitter emitter) {
|
public SseEmitter chat(ChatRequest chatRequest,SseEmitter emitter) {
|
||||||
ChatModelVo chatModelVo = chatModelService.selectModelByName(chatRequest.getModel());
|
ChatModelVo chatModelVo = chatModelService.selectModelByName(chatRequest.getModel());
|
||||||
String host = chatModelVo.getApiHost();
|
String host = chatModelVo.getApiHost();
|
||||||
@@ -100,44 +73,4 @@ public class OllamaServiceImpl implements IChatService {
|
|||||||
return emitter;
|
return emitter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public SseEmitter mcpChat(ChatRequest chatRequest, SseEmitter emitter) {
|
|
||||||
List<Message> msgList = chatRequest.getMessages();
|
|
||||||
// 添加记忆
|
|
||||||
for (int i = 0; i < msgList.size(); i++) {
|
|
||||||
org.springframework.ai.chat.messages.Message springAiMessage = new UserMessage(msgList.get(i).getContent().toString());
|
|
||||||
chatMemory.add(String.valueOf(i),springAiMessage);
|
|
||||||
}
|
|
||||||
var messageChatMemoryAdvisor = new MessageChatMemoryAdvisor(chatMemory, chatRequest.getUserId(), 10);
|
|
||||||
|
|
||||||
this.chatClient.prompt(chatRequest.getPrompt())
|
|
||||||
.advisors(messageChatMemoryAdvisor)
|
|
||||||
.stream()
|
|
||||||
.chatResponse()
|
|
||||||
.subscribe(
|
|
||||||
chatResponse -> {
|
|
||||||
try {
|
|
||||||
emitter.send(chatResponse, MediaType.APPLICATION_JSON);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error -> {
|
|
||||||
try {
|
|
||||||
emitter.completeWithError(error);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
() -> {
|
|
||||||
try {
|
|
||||||
emitter.complete();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return emitter;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package org.ruoyi.chat.service.chat.impl;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.ruoyi.chat.service.chat.IChatService;
|
||||||
|
import org.ruoyi.common.chat.entity.chat.Message;
|
||||||
|
import org.ruoyi.common.chat.request.ChatRequest;
|
||||||
|
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.memory.InMemoryChatMemory;
|
||||||
|
import org.springframework.ai.chat.messages.UserMessage;
|
||||||
|
import org.springframework.ai.openai.OpenAiChatOptions;
|
||||||
|
import org.springframework.ai.tool.ToolCallbackProvider;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
|
import reactor.core.publisher.Flux;
|
||||||
|
import reactor.core.scheduler.Schedulers;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@Slf4j
|
||||||
|
public class OpenAIServiceImpl implements IChatService {
|
||||||
|
|
||||||
|
private final ChatClient chatClient;
|
||||||
|
|
||||||
|
private final ChatMemory chatMemory = new InMemoryChatMemory();
|
||||||
|
|
||||||
|
|
||||||
|
public OpenAIServiceImpl(ChatClient.Builder chatClientBuilder, ToolCallbackProvider tools) {
|
||||||
|
this.chatClient = chatClientBuilder
|
||||||
|
.defaultTools(tools)
|
||||||
|
.defaultOptions(
|
||||||
|
OpenAiChatOptions.builder()
|
||||||
|
.model("gpt-4o-mini")
|
||||||
|
.temperature(0.4)
|
||||||
|
.build())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SseEmitter chat(ChatRequest chatRequest,SseEmitter emitter) {
|
||||||
|
return emitter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mcpChat(ChatRequest chatRequest, SseEmitter emitter) {
|
||||||
|
List<Message> msgList = chatRequest.getMessages();
|
||||||
|
// 添加记忆
|
||||||
|
for (int i = 0; i < msgList.size(); i++) {
|
||||||
|
org.springframework.ai.chat.messages.Message springAiMessage = new UserMessage(msgList.get(i).getContent().toString());
|
||||||
|
chatMemory.add(String.valueOf(i), springAiMessage);
|
||||||
|
}
|
||||||
|
var messageChatMemoryAdvisor = new MessageChatMemoryAdvisor(chatMemory, chatRequest.getUserId().toString(), 10);
|
||||||
|
|
||||||
|
Flux<String> content = chatClient
|
||||||
|
.prompt(chatRequest.getPrompt())
|
||||||
|
.advisors(messageChatMemoryAdvisor)
|
||||||
|
.stream().content();
|
||||||
|
|
||||||
|
content.publishOn(Schedulers.boundedElastic())
|
||||||
|
.doOnNext(text -> {
|
||||||
|
try {
|
||||||
|
emitter.send(text);
|
||||||
|
} catch (IOException e) {
|
||||||
|
emitter.completeWithError(e);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.doOnError(error -> {
|
||||||
|
log.error("Error in SSE stream: ", error);
|
||||||
|
emitter.completeWithError(error);
|
||||||
|
})
|
||||||
|
.doOnComplete(emitter::complete)
|
||||||
|
.subscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,11 +11,13 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import okhttp3.*;
|
import okhttp3.*;
|
||||||
|
|
||||||
|
import org.ruoyi.chat.config.ChatConfig;
|
||||||
|
import org.ruoyi.chat.listener.SSEEventSourceListener;
|
||||||
import org.ruoyi.chat.service.chat.IChatCostService;
|
import org.ruoyi.chat.service.chat.IChatCostService;
|
||||||
import org.ruoyi.chat.service.chat.IChatService;
|
import org.ruoyi.chat.service.chat.IChatService;
|
||||||
import org.ruoyi.chat.service.chat.ISseService;
|
import org.ruoyi.chat.service.chat.ISseService;
|
||||||
import org.ruoyi.chat.util.IpUtil;
|
import org.ruoyi.chat.util.IpUtil;
|
||||||
import org.ruoyi.chat.util.SSEUtil;
|
import org.ruoyi.common.chat.config.LocalCache;
|
||||||
import org.ruoyi.common.chat.request.ChatRequest;
|
import org.ruoyi.common.chat.request.ChatRequest;
|
||||||
import org.ruoyi.common.chat.entity.Tts.TextToSpeech;
|
import org.ruoyi.common.chat.entity.Tts.TextToSpeech;
|
||||||
import org.ruoyi.common.chat.entity.chat.ChatCompletion;
|
import org.ruoyi.common.chat.entity.chat.ChatCompletion;
|
||||||
@@ -33,7 +35,9 @@ import org.ruoyi.common.core.utils.file.MimeTypeUtils;
|
|||||||
|
|
||||||
import org.ruoyi.common.redis.utils.RedisUtils;
|
import org.ruoyi.common.redis.utils.RedisUtils;
|
||||||
|
|
||||||
|
import org.ruoyi.domain.vo.ChatModelVo;
|
||||||
import org.ruoyi.service.EmbeddingService;
|
import org.ruoyi.service.EmbeddingService;
|
||||||
|
import org.ruoyi.service.IChatModelService;
|
||||||
import org.ruoyi.service.VectorStoreService;
|
import org.ruoyi.service.VectorStoreService;
|
||||||
import org.springframework.core.io.InputStreamResource;
|
import org.springframework.core.io.InputStreamResource;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
@@ -74,27 +78,35 @@ public class SseServiceImpl implements ISseService {
|
|||||||
|
|
||||||
private final IChatService chatService;
|
private final IChatService chatService;
|
||||||
|
|
||||||
|
private final IChatModelService chatModelService;
|
||||||
|
|
||||||
private static final String requestIdTemplate = "company-%d";
|
private static final String requestIdTemplate = "company-%d";
|
||||||
|
|
||||||
private static final ObjectMapper mapper = new ObjectMapper();
|
private static final ObjectMapper mapper = new ObjectMapper();
|
||||||
|
|
||||||
|
private final ChatConfig chatConfig;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SseEmitter sseChat(ChatRequest chatRequest, HttpServletRequest request) {
|
public SseEmitter sseChat(ChatRequest chatRequest, HttpServletRequest request) {
|
||||||
SseEmitter sseEmitter = new SseEmitter(0L);
|
SseEmitter sseEmitter = new SseEmitter();
|
||||||
try {
|
try {
|
||||||
// 构建消息列表增加联网、知识库等内容
|
// 构建消息列表增加联网、知识库等内容
|
||||||
buildChatMessageList(chatRequest);
|
buildChatMessageList(chatRequest);
|
||||||
|
if (!StpUtil.isLogin()) {
|
||||||
|
// 未登录用户限制对话次数
|
||||||
|
checkUnauthenticatedUserChatLimit(request);
|
||||||
|
}else {
|
||||||
|
LocalCache.CACHE.put("userId", chatCostService.getUserId());
|
||||||
|
|
||||||
|
chatRequest.setUserId(chatCostService.getUserId());
|
||||||
|
// 保存消息记录 并扣除费用
|
||||||
|
// chatCostService.deductToken(chatRequest);
|
||||||
|
}
|
||||||
// 根据模型名称前缀调用不同的处理逻辑
|
// 根据模型名称前缀调用不同的处理逻辑
|
||||||
switchModelAndHandle(chatRequest,sseEmitter);
|
switchModelAndHandle(chatRequest,sseEmitter);
|
||||||
// 未登录用户限制对话次数
|
|
||||||
checkUnauthenticatedUserChatLimit(request);
|
|
||||||
// 保存消息记录 并扣除费用
|
|
||||||
chatCostService.deductToken(chatRequest);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
String message = e.getMessage();
|
log.error(e.getMessage(),e);
|
||||||
SSEUtil.sendErrorEvent(sseEmitter, message);
|
sseEmitter.completeWithError(e);
|
||||||
return sseEmitter;
|
|
||||||
}
|
}
|
||||||
return sseEmitter;
|
return sseEmitter;
|
||||||
}
|
}
|
||||||
@@ -106,8 +118,7 @@ public class SseServiceImpl implements ISseService {
|
|||||||
* @throws ServiceException 如果当日免费次数已用完
|
* @throws ServiceException 如果当日免费次数已用完
|
||||||
*/
|
*/
|
||||||
public void checkUnauthenticatedUserChatLimit(HttpServletRequest request) throws ServiceException {
|
public void checkUnauthenticatedUserChatLimit(HttpServletRequest request) throws ServiceException {
|
||||||
// 未登录用户限制对话次数
|
|
||||||
if (!StpUtil.isLogin()) {
|
|
||||||
String clientIp = IpUtil.getClientIp(request);
|
String clientIp = IpUtil.getClientIp(request);
|
||||||
// 访客每天默认只能对话5次
|
// 访客每天默认只能对话5次
|
||||||
int timeWindowInSeconds = 5;
|
int timeWindowInSeconds = 5;
|
||||||
@@ -125,13 +136,14 @@ public class SseServiceImpl implements ISseService {
|
|||||||
count++;
|
count++;
|
||||||
RedisUtils.setCacheObject(redisKey, count);
|
RedisUtils.setCacheObject(redisKey, count);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据模型名称前缀调用不同的处理逻辑
|
* 根据模型名称前缀调用不同的处理逻辑
|
||||||
*/
|
*/
|
||||||
private void switchModelAndHandle(ChatRequest chatRequest,SseEmitter emitter) {
|
private void switchModelAndHandle(ChatRequest chatRequest,SseEmitter emitter) {
|
||||||
|
SSEEventSourceListener openAIEventSourceListener = new SSEEventSourceListener(emitter);
|
||||||
String model = chatRequest.getModel();
|
String model = chatRequest.getModel();
|
||||||
// 如果模型名称以ollama开头,则调用ollama中部署的本地模型
|
// 如果模型名称以ollama开头,则调用ollama中部署的本地模型
|
||||||
if (model.startsWith("ollama-")) {
|
if (model.startsWith("ollama-")) {
|
||||||
@@ -142,8 +154,24 @@ public class SseServiceImpl implements ISseService {
|
|||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("Invalid ollama model name: " + chatRequest.getModel());
|
throw new IllegalArgumentException("Invalid ollama model name: " + chatRequest.getModel());
|
||||||
}
|
}
|
||||||
} else if (model.startsWith("gpt-4-gizmo")) {
|
} else {
|
||||||
chatRequest.setModel("gpt-4-gizmo");
|
|
||||||
|
if (model.startsWith("gpt-4-gizmo")) {
|
||||||
|
chatRequest.setModel("gpt-4-gizmo");
|
||||||
|
}
|
||||||
|
ChatModelVo chatModelVo = chatModelService.selectModelByName(chatRequest.getModel());
|
||||||
|
//openAiStreamClient = chatConfig.createOpenAiStreamClient(chatModelVo.getApiHost(), chatModelVo.getApiKey());
|
||||||
|
|
||||||
|
ChatCompletion completion = ChatCompletion
|
||||||
|
.builder()
|
||||||
|
.messages(chatRequest.getMessages())
|
||||||
|
.model(chatRequest.getModel())
|
||||||
|
.temperature(0.2)
|
||||||
|
.topP(1.0)
|
||||||
|
.stream(true)
|
||||||
|
.build();
|
||||||
|
openAiStreamClient.streamChatCompletion(completion, openAIEventSourceListener);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,9 +179,10 @@ public class SseServiceImpl implements ISseService {
|
|||||||
* 构建消息列表
|
* 构建消息列表
|
||||||
*/
|
*/
|
||||||
private void buildChatMessageList(ChatRequest chatRequest){
|
private void buildChatMessageList(ChatRequest chatRequest){
|
||||||
|
ChatModelVo chatModelVo = chatModelService.selectModelByName(chatRequest.getModel());
|
||||||
// 获取对话消息列表
|
// 获取对话消息列表
|
||||||
List<Message> messages = chatRequest.getMessages();
|
List<Message> messages = chatRequest.getMessages();
|
||||||
String sysPrompt = chatRequest.getSysPrompt();
|
String sysPrompt = chatModelVo.getSystemPrompt();
|
||||||
if(StringUtils.isEmpty(sysPrompt)){
|
if(StringUtils.isEmpty(sysPrompt)){
|
||||||
sysPrompt ="你是一个由RuoYI-AI开发的人工智能助手,名字叫熊猫助手。你擅长中英文对话,能够理解并处理各种问题,提供安全、有帮助、准确的回答。" +
|
sysPrompt ="你是一个由RuoYI-AI开发的人工智能助手,名字叫熊猫助手。你擅长中英文对话,能够理解并处理各种问题,提供安全、有帮助、准确的回答。" +
|
||||||
"当前时间:"+ DateUtils.getDate();
|
"当前时间:"+ DateUtils.getDate();
|
||||||
@@ -162,8 +191,9 @@ public class SseServiceImpl implements ISseService {
|
|||||||
Message sysMessage = Message.builder().content(sysPrompt).role(Message.Role.SYSTEM).build();
|
Message sysMessage = Message.builder().content(sysPrompt).role(Message.Role.SYSTEM).build();
|
||||||
messages.add(0,sysMessage);
|
messages.add(0,sysMessage);
|
||||||
|
|
||||||
|
chatRequest.setSysPrompt(sysPrompt);
|
||||||
// 查询向量库相关信息加入到上下文
|
// 查询向量库相关信息加入到上下文
|
||||||
if(chatRequest.getKid()!=null){
|
if(StringUtils.isNotEmpty(chatRequest.getKid())){
|
||||||
List<Message> knMessages = new ArrayList<>();
|
List<Message> knMessages = new ArrayList<>();
|
||||||
String content = messages.get(messages.size() - 1).getContent().toString();
|
String content = messages.get(messages.size() - 1).getContent().toString();
|
||||||
List<String> nearestList;
|
List<String> nearestList;
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.ruoyi.chat.util;
|
package org.ruoyi.chat.util;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -19,7 +20,7 @@ public class SSEUtil {
|
|||||||
* @param sseEmitter sse事件对象
|
* @param sseEmitter sse事件对象
|
||||||
* @param errorMessage 错误信息
|
* @param errorMessage 错误信息
|
||||||
*/
|
*/
|
||||||
public static void sendErrorEvent(SseEmitter sseEmitter, String errorMessage) {
|
public static void sendErrorEvent(ResponseBodyEmitter sseEmitter, String errorMessage) {
|
||||||
SseEmitter.SseEventBuilder event = SseEmitter.event()
|
SseEmitter.SseEventBuilder event = SseEmitter.event()
|
||||||
.name("error")
|
.name("error")
|
||||||
.data(errorMessage);
|
.data(errorMessage);
|
||||||
|
|||||||
@@ -1,99 +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>org.ruoyi</groupId>
|
|
||||||
<artifactId>ruoyi-modules</artifactId>
|
|
||||||
<version>${revision}</version>
|
|
||||||
<relativePath>../pom.xml</relativePath>
|
|
||||||
</parent>
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
|
|
||||||
<artifactId>ruoyi-demo</artifactId>
|
|
||||||
|
|
||||||
<description>
|
|
||||||
demo模块
|
|
||||||
</description>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
|
|
||||||
<!-- 通用工具-->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.ruoyi</groupId>
|
|
||||||
<artifactId>ruoyi-common-core</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.ruoyi</groupId>
|
|
||||||
<artifactId>ruoyi-common-doc</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.ruoyi</groupId>
|
|
||||||
<artifactId>ruoyi-common-sms</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.ruoyi</groupId>
|
|
||||||
<artifactId>ruoyi-common-mail</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.ruoyi</groupId>
|
|
||||||
<artifactId>ruoyi-common-redis</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.ruoyi</groupId>
|
|
||||||
<artifactId>ruoyi-common-idempotent</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.ruoyi</groupId>
|
|
||||||
<artifactId>ruoyi-common-mybatis</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.ruoyi</groupId>
|
|
||||||
<artifactId>ruoyi-common-log</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.ruoyi</groupId>
|
|
||||||
<artifactId>ruoyi-common-excel</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.ruoyi</groupId>
|
|
||||||
<artifactId>ruoyi-common-web</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.ruoyi</groupId>
|
|
||||||
<artifactId>ruoyi-common-ratelimiter</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.ruoyi</groupId>
|
|
||||||
<artifactId>ruoyi-common-translation</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.ruoyi</groupId>
|
|
||||||
<artifactId>ruoyi-common-sensitive</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.ruoyi</groupId>
|
|
||||||
<artifactId>ruoyi-common-encrypt</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.ruoyi</groupId>
|
|
||||||
<artifactId>ruoyi-common-tenant</artifactId>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
</project>
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
package org.ruoyi.demo.controller;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.ruoyi.common.core.domain.R;
|
|
||||||
import org.ruoyi.common.mail.utils.MailUtils;
|
|
||||||
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 java.io.File;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 邮件发送案例
|
|
||||||
*
|
|
||||||
* @author Michelle.Chung
|
|
||||||
*/
|
|
||||||
@Validated
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/demo/mail")
|
|
||||||
public class MailController {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送邮件
|
|
||||||
*
|
|
||||||
* @param to 接收人
|
|
||||||
* @param subject 标题
|
|
||||||
* @param text 内容
|
|
||||||
*/
|
|
||||||
@GetMapping("/sendSimpleMessage")
|
|
||||||
public R<Void> sendSimpleMessage(String to, String subject, String text) {
|
|
||||||
MailUtils.sendText(to, subject, text);
|
|
||||||
return R.ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送邮件(带附件)
|
|
||||||
*
|
|
||||||
* @param to 接收人
|
|
||||||
* @param subject 标题
|
|
||||||
* @param text 内容
|
|
||||||
* @param filePath 附件路径
|
|
||||||
*/
|
|
||||||
@GetMapping("/sendMessageWithAttachment")
|
|
||||||
public R<Void> sendMessageWithAttachment(String to, String subject, String text, String filePath) {
|
|
||||||
MailUtils.sendText(to, subject, text, new File(filePath));
|
|
||||||
return R.ok();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
package org.ruoyi.demo.controller;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.ruoyi.common.core.constant.CacheNames;
|
|
||||||
import org.ruoyi.common.core.domain.R;
|
|
||||||
import org.ruoyi.common.redis.utils.RedisUtils;
|
|
||||||
import org.springframework.cache.annotation.CacheEvict;
|
|
||||||
import org.springframework.cache.annotation.CachePut;
|
|
||||||
import org.springframework.cache.annotation.Cacheable;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import java.time.Duration;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* spring-cache 演示案例
|
|
||||||
*
|
|
||||||
* @author Lion Li
|
|
||||||
*/
|
|
||||||
// 类级别 缓存统一配置
|
|
||||||
//@CacheConfig(cacheNames = CacheNames.DEMO_CACHE)
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/demo/cache")
|
|
||||||
public class RedisCacheController {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试 @Cacheable
|
|
||||||
* <p>
|
|
||||||
* 表示这个方法有了缓存的功能,方法的返回值会被缓存下来
|
|
||||||
* 下一次调用该方法前,会去检查是否缓存中已经有值
|
|
||||||
* 如果有就直接返回,不调用方法
|
|
||||||
* 如果没有,就调用方法,然后把结果缓存起来
|
|
||||||
* 这个注解「一般用在查询方法上」
|
|
||||||
* <p>
|
|
||||||
* 重点说明: 缓存注解严谨与其他筛选数据功能一起使用
|
|
||||||
* 例如: 数据权限注解 会造成 缓存击穿 与 数据不一致问题
|
|
||||||
* <p>
|
|
||||||
* cacheNames 命名规则 查看 {@link CacheNames} 注释 支持多参数
|
|
||||||
*/
|
|
||||||
@Cacheable(cacheNames = "demo:cache#60s#10m#20", key = "#key", condition = "#key != null")
|
|
||||||
@GetMapping("/test1")
|
|
||||||
public R<String> test1(String key, String value) {
|
|
||||||
return R.ok("操作成功", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试 @CachePut
|
|
||||||
* <p>
|
|
||||||
* 加了@CachePut注解的方法,会把方法的返回值put到缓存里面缓存起来,供其它地方使用
|
|
||||||
* 它「通常用在新增或者实时更新方法上」
|
|
||||||
* <p>
|
|
||||||
* cacheNames 命名规则 查看 {@link CacheNames} 注释 支持多参数
|
|
||||||
*/
|
|
||||||
@CachePut(cacheNames = CacheNames.DEMO_CACHE, key = "#key", condition = "#key != null")
|
|
||||||
@GetMapping("/test2")
|
|
||||||
public R<String> test2(String key, String value) {
|
|
||||||
return R.ok("操作成功", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试 @CacheEvict
|
|
||||||
* <p>
|
|
||||||
* 使用了CacheEvict注解的方法,会清空指定缓存
|
|
||||||
* 「一般用在删除的方法上」
|
|
||||||
* <p>
|
|
||||||
* cacheNames 命名规则 查看 {@link CacheNames} 注释 支持多参数
|
|
||||||
*/
|
|
||||||
@CacheEvict(cacheNames = CacheNames.DEMO_CACHE, key = "#key", condition = "#key != null")
|
|
||||||
@GetMapping("/test3")
|
|
||||||
public R<String> test3(String key, String value) {
|
|
||||||
return R.ok("操作成功", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试设置过期时间
|
|
||||||
* 手动设置过期时间10秒
|
|
||||||
* 11秒后获取 判断是否相等
|
|
||||||
*/
|
|
||||||
@GetMapping("/test6")
|
|
||||||
public R<Boolean> test6(String key, String value) {
|
|
||||||
RedisUtils.setCacheObject(key, value);
|
|
||||||
boolean flag = RedisUtils.expire(key, Duration.ofSeconds(10));
|
|
||||||
System.out.println("***********" + flag);
|
|
||||||
try {
|
|
||||||
Thread.sleep(11 * 1000);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
Object obj = RedisUtils.getCacheObject(key);
|
|
||||||
return R.ok(value.equals(obj));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
package org.ruoyi.demo.controller;
|
|
||||||
|
|
||||||
import com.baomidou.lock.LockInfo;
|
|
||||||
import com.baomidou.lock.LockTemplate;
|
|
||||||
import com.baomidou.lock.annotation.Lock4j;
|
|
||||||
import com.baomidou.lock.executor.RedissonLockExecutor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.ruoyi.common.core.domain.R;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import java.time.LocalTime;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试分布式锁的样例
|
|
||||||
*
|
|
||||||
* @author shenxinquan
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/demo/redisLock")
|
|
||||||
public class RedisLockController {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private LockTemplate lockTemplate;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试lock4j 注解
|
|
||||||
*/
|
|
||||||
@Lock4j(keys = {"#key"})
|
|
||||||
@GetMapping("/testLock4j")
|
|
||||||
public R<String> testLock4j(String key, String value) {
|
|
||||||
System.out.println("start:" + key + ",time:" + LocalTime.now().toString());
|
|
||||||
try {
|
|
||||||
Thread.sleep(10000);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
System.out.println("end :" + key + ",time:" + LocalTime.now().toString());
|
|
||||||
return R.ok("操作成功", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试lock4j 工具
|
|
||||||
*/
|
|
||||||
@GetMapping("/testLock4jLockTemplate")
|
|
||||||
public R<String> testLock4jLockTemplate(String key, String value) {
|
|
||||||
final LockInfo lockInfo = lockTemplate.lock(key, 30000L, 5000L, RedissonLockExecutor.class);
|
|
||||||
if (null == lockInfo) {
|
|
||||||
throw new RuntimeException("业务处理中,请稍后再试");
|
|
||||||
}
|
|
||||||
// 获取锁成功,处理业务
|
|
||||||
try {
|
|
||||||
try {
|
|
||||||
Thread.sleep(8000);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
System.out.println("执行简单方法1 , 当前线程:" + Thread.currentThread().getName());
|
|
||||||
} finally {
|
|
||||||
//释放锁
|
|
||||||
lockTemplate.releaseLock(lockInfo);
|
|
||||||
}
|
|
||||||
//结束
|
|
||||||
return R.ok("操作成功", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
package org.ruoyi.demo.controller;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.ruoyi.common.core.domain.R;
|
|
||||||
import org.ruoyi.common.redis.utils.RedisUtils;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Redis 发布订阅 演示案例
|
|
||||||
*
|
|
||||||
* @author Lion Li
|
|
||||||
*/
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/demo/redis/pubsub")
|
|
||||||
public class RedisPubSubController {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发布消息
|
|
||||||
*
|
|
||||||
* @param key 通道Key
|
|
||||||
* @param value 发送内容
|
|
||||||
*/
|
|
||||||
@GetMapping("/pub")
|
|
||||||
public R<Void> pub(String key, String value) {
|
|
||||||
RedisUtils.publish(key, value, consumer -> {
|
|
||||||
System.out.println("发布通道 => " + key + ", 发送值 => " + value);
|
|
||||||
});
|
|
||||||
return R.ok("操作成功");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 订阅消息
|
|
||||||
*
|
|
||||||
* @param key 通道Key
|
|
||||||
*/
|
|
||||||
@GetMapping("/sub")
|
|
||||||
public R<Void> sub(String key) {
|
|
||||||
RedisUtils.subscribe(key, String.class, msg -> {
|
|
||||||
System.out.println("订阅通道 => " + key + ", 接收值 => " + msg);
|
|
||||||
});
|
|
||||||
return R.ok("操作成功");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
package org.ruoyi.demo.controller;
|
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.ruoyi.common.core.domain.R;
|
|
||||||
import org.ruoyi.common.ratelimiter.annotation.RateLimiter;
|
|
||||||
import org.ruoyi.common.ratelimiter.enums.LimitType;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试分布式限流样例
|
|
||||||
*
|
|
||||||
* @author Lion Li
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/demo/rateLimiter")
|
|
||||||
public class RedisRateLimiterController {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试全局限流
|
|
||||||
* 全局影响
|
|
||||||
*/
|
|
||||||
@RateLimiter(count = 2, time = 10)
|
|
||||||
@GetMapping("/test")
|
|
||||||
public R<String> test(String value) {
|
|
||||||
return R.ok("操作成功", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试请求IP限流
|
|
||||||
* 同一IP请求受影响
|
|
||||||
*/
|
|
||||||
@RateLimiter(count = 2, time = 10, limitType = LimitType.IP)
|
|
||||||
@GetMapping("/testip")
|
|
||||||
public R<String> testip(String value) {
|
|
||||||
return R.ok("操作成功", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试集群实例限流
|
|
||||||
* 启动两个后端服务互不影响
|
|
||||||
*/
|
|
||||||
@RateLimiter(count = 2, time = 10, limitType = LimitType.CLUSTER)
|
|
||||||
@GetMapping("/testcluster")
|
|
||||||
public R<String> testcluster(String value) {
|
|
||||||
return R.ok("操作成功", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试请求IP限流(key基于参数获取)
|
|
||||||
* 同一IP请求受影响
|
|
||||||
*
|
|
||||||
* 简单变量获取 #变量 复杂表达式 #{#变量 != 1 ? 1 : 0}
|
|
||||||
*/
|
|
||||||
@RateLimiter(count = 2, time = 10, limitType = LimitType.IP, key = "#value")
|
|
||||||
@GetMapping("/testObj")
|
|
||||||
public R<String> testObj(String value) {
|
|
||||||
return R.ok("操作成功", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
package org.ruoyi.demo.controller;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.ruoyi.common.core.domain.R;
|
|
||||||
import org.ruoyi.common.core.utils.SpringUtils;
|
|
||||||
import org.ruoyi.common.sms.config.properties.SmsProperties;
|
|
||||||
import org.ruoyi.common.sms.core.SmsTemplate;
|
|
||||||
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 java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 短信演示案例
|
|
||||||
* 请先阅读文档 否则无法使用
|
|
||||||
*
|
|
||||||
* @author Lion Li
|
|
||||||
* @version 4.2.0
|
|
||||||
*/
|
|
||||||
@Validated
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/demo/sms")
|
|
||||||
public class SmsController {
|
|
||||||
|
|
||||||
private final SmsProperties smsProperties;
|
|
||||||
// private final SmsTemplate smsTemplate; // 可以使用spring注入
|
|
||||||
// private final AliyunSmsTemplate smsTemplate; // 也可以注入某个厂家的模板工具
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送短信Aliyun
|
|
||||||
*
|
|
||||||
* @param phones 电话号
|
|
||||||
* @param templateId 模板ID
|
|
||||||
*/
|
|
||||||
@GetMapping("/sendAliyun")
|
|
||||||
public R<Object> sendAliyun(String phones, String templateId) {
|
|
||||||
if (!smsProperties.getEnabled()) {
|
|
||||||
return R.fail("当前系统没有开启短信功能!");
|
|
||||||
}
|
|
||||||
if (!SpringUtils.containsBean("aliyunSmsTemplate")) {
|
|
||||||
return R.fail("阿里云依赖未引入!");
|
|
||||||
}
|
|
||||||
SmsTemplate smsTemplate = SpringUtils.getBean(SmsTemplate.class);
|
|
||||||
Map<String, String> map = new HashMap<>(1);
|
|
||||||
map.put("code", "1234");
|
|
||||||
Object send = smsTemplate.send(phones, templateId, map);
|
|
||||||
return R.ok(send);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 发送短信Tencent
|
|
||||||
*
|
|
||||||
* @param phones 电话号
|
|
||||||
* @param templateId 模板ID
|
|
||||||
*/
|
|
||||||
@GetMapping("/sendTencent")
|
|
||||||
public R<Object> sendTencent(String phones, String templateId) {
|
|
||||||
if (!smsProperties.getEnabled()) {
|
|
||||||
return R.fail("当前系统没有开启短信功能!");
|
|
||||||
}
|
|
||||||
if (!SpringUtils.containsBean("tencentSmsTemplate")) {
|
|
||||||
return R.fail("腾讯云依赖未引入!");
|
|
||||||
}
|
|
||||||
SmsTemplate smsTemplate = SpringUtils.getBean(SmsTemplate.class);
|
|
||||||
Map<String, String> map = new HashMap<>(1);
|
|
||||||
// map.put("2", "测试测试");
|
|
||||||
map.put("1", "1234");
|
|
||||||
Object send = smsTemplate.send(phones, templateId, map);
|
|
||||||
return R.ok(send);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
package org.ruoyi.demo.controller;
|
|
||||||
|
|
||||||
import org.ruoyi.common.core.domain.R;
|
|
||||||
import org.springframework.http.MediaType;
|
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestPart;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* swagger3 用法示例
|
|
||||||
*
|
|
||||||
* @author Lion Li
|
|
||||||
*/
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/swagger/demo")
|
|
||||||
public class Swagger3DemoController {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 上传请求
|
|
||||||
* 必须使用 @RequestPart 注解标注为文件
|
|
||||||
*
|
|
||||||
* @param file 文件
|
|
||||||
*/
|
|
||||||
@PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
|
||||||
public R<String> upload(@RequestPart("file") MultipartFile file) {
|
|
||||||
return R.ok("操作成功", file.getOriginalFilename());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
package org.ruoyi.demo.controller;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import org.ruoyi.common.core.domain.R;
|
|
||||||
import org.ruoyi.common.web.core.BaseController;
|
|
||||||
import org.ruoyi.demo.domain.TestDemo;
|
|
||||||
import org.ruoyi.demo.mapper.TestDemoMapper;
|
|
||||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
|
||||||
import org.springframework.web.bind.annotation.PostMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试批量方法
|
|
||||||
*
|
|
||||||
* @author Lion Li
|
|
||||||
* @date 2021-05-30
|
|
||||||
*/
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/demo/batch")
|
|
||||||
public class TestBatchController extends BaseController {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 为了便于测试 直接引入mapper
|
|
||||||
*/
|
|
||||||
private final TestDemoMapper testDemoMapper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 新增批量方法 可完美替代 saveBatch 秒级插入上万数据 (对mysql负荷较大)
|
|
||||||
* <p>
|
|
||||||
* 3.5.0 版本 增加 rewriteBatchedStatements=true 批处理参数 使 MP 原生批处理可以达到同样的速度
|
|
||||||
*/
|
|
||||||
@PostMapping("/add")
|
|
||||||
// @DS("slave")
|
|
||||||
public R<Void> add() {
|
|
||||||
List<TestDemo> list = new ArrayList<>();
|
|
||||||
for (int i = 0; i < 1000; i++) {
|
|
||||||
TestDemo testDemo = new TestDemo();
|
|
||||||
testDemo.setOrderNum(-1);
|
|
||||||
testDemo.setTestKey("批量新增");
|
|
||||||
testDemo.setValue("测试新增");
|
|
||||||
list.add(testDemo);
|
|
||||||
}
|
|
||||||
return toAjax(testDemoMapper.insertBatch(list));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 新增或更新 可完美替代 saveOrUpdateBatch 高性能
|
|
||||||
* <p>
|
|
||||||
* 3.5.0 版本 增加 rewriteBatchedStatements=true 批处理参数 使 MP 原生批处理可以达到同样的速度
|
|
||||||
*/
|
|
||||||
@PostMapping("/addOrUpdate")
|
|
||||||
// @DS("slave")
|
|
||||||
public R<Void> addOrUpdate() {
|
|
||||||
List<TestDemo> list = new ArrayList<>();
|
|
||||||
for (int i = 0; i < 1000; i++) {
|
|
||||||
TestDemo testDemo = new TestDemo();
|
|
||||||
testDemo.setOrderNum(-1);
|
|
||||||
testDemo.setTestKey("批量新增");
|
|
||||||
testDemo.setValue("测试新增");
|
|
||||||
list.add(testDemo);
|
|
||||||
}
|
|
||||||
testDemoMapper.insertBatch(list);
|
|
||||||
for (int i = 0; i < list.size(); i++) {
|
|
||||||
TestDemo testDemo = list.get(i);
|
|
||||||
testDemo.setTestKey("批量新增或修改");
|
|
||||||
testDemo.setValue("批量新增或修改");
|
|
||||||
if (i % 2 == 0) {
|
|
||||||
testDemo.setId(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return toAjax(testDemoMapper.insertOrUpdateBatch(list));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除批量方法
|
|
||||||
*/
|
|
||||||
@DeleteMapping()
|
|
||||||
// @DS("slave")
|
|
||||||
public R<Void> remove() {
|
|
||||||
return toAjax(testDemoMapper.delete(new LambdaQueryWrapper<TestDemo>()
|
|
||||||
.eq(TestDemo::getOrderNum, -1L)));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
package org.ruoyi.demo.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.ruoyi.common.core.domain.R;
|
|
||||||
import org.ruoyi.common.core.utils.MapstructUtils;
|
|
||||||
import org.ruoyi.common.core.utils.ValidatorUtils;
|
|
||||||
import org.ruoyi.common.core.validate.AddGroup;
|
|
||||||
import org.ruoyi.common.core.validate.EditGroup;
|
|
||||||
import org.ruoyi.common.core.validate.QueryGroup;
|
|
||||||
import org.ruoyi.common.excel.core.ExcelResult;
|
|
||||||
import org.ruoyi.common.excel.utils.ExcelUtil;
|
|
||||||
import org.ruoyi.common.idempotent.annotation.RepeatSubmit;
|
|
||||||
import org.ruoyi.common.log.annotation.Log;
|
|
||||||
import org.ruoyi.common.log.enums.BusinessType;
|
|
||||||
import org.ruoyi.core.page.PageQuery;
|
|
||||||
import org.ruoyi.core.page.TableDataInfo;
|
|
||||||
import org.ruoyi.common.web.core.BaseController;
|
|
||||||
import org.ruoyi.demo.domain.TestDemo;
|
|
||||||
import org.ruoyi.demo.domain.bo.TestDemoBo;
|
|
||||||
import org.ruoyi.demo.domain.bo.TestDemoImportVo;
|
|
||||||
import org.ruoyi.demo.domain.vo.TestDemoVo;
|
|
||||||
import org.ruoyi.demo.service.ITestDemoService;
|
|
||||||
import org.springframework.http.MediaType;
|
|
||||||
import org.springframework.validation.annotation.Validated;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试单表Controller
|
|
||||||
*
|
|
||||||
* @author Lion Li
|
|
||||||
* @date 2021-07-26
|
|
||||||
*/
|
|
||||||
@Validated
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/demo/demo")
|
|
||||||
public class TestDemoController extends BaseController {
|
|
||||||
|
|
||||||
private final ITestDemoService testDemoService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询测试单表列表
|
|
||||||
*/
|
|
||||||
@SaCheckPermission("demo:demo:list")
|
|
||||||
@GetMapping("/list")
|
|
||||||
public TableDataInfo<TestDemoVo> list(@Validated(QueryGroup.class) TestDemoBo bo, PageQuery pageQuery) {
|
|
||||||
return testDemoService.queryPageList(bo, pageQuery);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 自定义分页查询
|
|
||||||
*/
|
|
||||||
@SaCheckPermission("demo:demo:list")
|
|
||||||
@GetMapping("/page")
|
|
||||||
public TableDataInfo<TestDemoVo> page(@Validated(QueryGroup.class) TestDemoBo bo, PageQuery pageQuery) {
|
|
||||||
return testDemoService.customPageList(bo, pageQuery);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 导入数据
|
|
||||||
*
|
|
||||||
* @param file 导入文件
|
|
||||||
*/
|
|
||||||
@Log(title = "测试单表", businessType = BusinessType.IMPORT)
|
|
||||||
@SaCheckPermission("demo:demo:import")
|
|
||||||
@PostMapping(value = "/importData", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
|
|
||||||
public R<Void> importData(@RequestPart("file") MultipartFile file) throws Exception {
|
|
||||||
ExcelResult<TestDemoImportVo> excelResult = ExcelUtil.importExcel(file.getInputStream(), TestDemoImportVo.class, true);
|
|
||||||
List<TestDemo> list = MapstructUtils.convert(excelResult.getList(), TestDemo.class);
|
|
||||||
testDemoService.saveBatch(list);
|
|
||||||
return R.ok(excelResult.getAnalysis());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 导出测试单表列表
|
|
||||||
*/
|
|
||||||
@SaCheckPermission("demo:demo:export")
|
|
||||||
@Log(title = "测试单表", businessType = BusinessType.EXPORT)
|
|
||||||
@PostMapping("/export")
|
|
||||||
public void export(@Validated TestDemoBo bo, HttpServletResponse response) {
|
|
||||||
List<TestDemoVo> list = testDemoService.queryList(bo);
|
|
||||||
// 测试雪花id导出
|
|
||||||
// for (TestDemoVo vo : list) {
|
|
||||||
// vo.setId(1234567891234567893L);
|
|
||||||
// }
|
|
||||||
ExcelUtil.exportExcel(list, "测试单表", TestDemoVo.class, response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取测试单表详细信息
|
|
||||||
*
|
|
||||||
* @param id 测试ID
|
|
||||||
*/
|
|
||||||
@SaCheckPermission("demo:demo:query")
|
|
||||||
@GetMapping("/{id}")
|
|
||||||
public R<TestDemoVo> getInfo(@NotNull(message = "主键不能为空")
|
|
||||||
@PathVariable("id") Long id) {
|
|
||||||
return R.ok(testDemoService.queryById(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 新增测试单表
|
|
||||||
*/
|
|
||||||
@SaCheckPermission("demo:demo:add")
|
|
||||||
@Log(title = "测试单表", businessType = BusinessType.INSERT)
|
|
||||||
@RepeatSubmit(interval = 2, timeUnit = TimeUnit.SECONDS, message = "{repeat.submit.message}")
|
|
||||||
@PostMapping()
|
|
||||||
public R<Void> add(@RequestBody TestDemoBo bo) {
|
|
||||||
// 使用校验工具对标 @Validated(AddGroup.class) 注解
|
|
||||||
// 用于在非 Controller 的地方校验对象
|
|
||||||
ValidatorUtils.validate(bo, AddGroup.class);
|
|
||||||
return toAjax(testDemoService.insertByBo(bo));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 修改测试单表
|
|
||||||
*/
|
|
||||||
@SaCheckPermission("demo:demo:edit")
|
|
||||||
@Log(title = "测试单表", businessType = BusinessType.UPDATE)
|
|
||||||
@RepeatSubmit
|
|
||||||
@PutMapping()
|
|
||||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody TestDemoBo bo) {
|
|
||||||
return toAjax(testDemoService.updateByBo(bo));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除测试单表
|
|
||||||
*
|
|
||||||
* @param ids 测试ID串
|
|
||||||
*/
|
|
||||||
@SaCheckPermission("demo:demo:remove")
|
|
||||||
@Log(title = "测试单表", businessType = BusinessType.DELETE)
|
|
||||||
@DeleteMapping("/{ids}")
|
|
||||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
|
||||||
@PathVariable Long[] ids) {
|
|
||||||
return toAjax(testDemoService.deleteWithValidByIds(Arrays.asList(ids), true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
package org.ruoyi.demo.controller;
|
|
||||||
|
|
||||||
import org.ruoyi.common.core.domain.R;
|
|
||||||
import org.ruoyi.demo.domain.TestDemoEncrypt;
|
|
||||||
import org.ruoyi.demo.mapper.TestDemoEncryptMapper;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
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 java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试数据库加解密功能
|
|
||||||
*
|
|
||||||
* @author Lion Li
|
|
||||||
*/
|
|
||||||
@Validated
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/demo/encrypt")
|
|
||||||
public class TestEncryptController {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private TestDemoEncryptMapper mapper;
|
|
||||||
@Value("${mybatis-encryptor.enable}")
|
|
||||||
private Boolean encryptEnable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试数据库加解密
|
|
||||||
*
|
|
||||||
* @param key 测试key
|
|
||||||
* @param value 测试value
|
|
||||||
*/
|
|
||||||
@GetMapping()
|
|
||||||
public R<Map<String, TestDemoEncrypt>> test(String key, String value) {
|
|
||||||
if (!encryptEnable) {
|
|
||||||
throw new RuntimeException("加密功能未开启!");
|
|
||||||
}
|
|
||||||
Map<String, TestDemoEncrypt> map = new HashMap<>(2);
|
|
||||||
TestDemoEncrypt demo = new TestDemoEncrypt();
|
|
||||||
demo.setTestKey(key);
|
|
||||||
demo.setValue(value);
|
|
||||||
mapper.insert(demo);
|
|
||||||
map.put("加密", demo);
|
|
||||||
TestDemoEncrypt testDemo = mapper.selectById(demo.getId());
|
|
||||||
map.put("解密", testDemo);
|
|
||||||
return R.ok(map);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,97 +0,0 @@
|
|||||||
package org.ruoyi.demo.controller;
|
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
|
||||||
import jakarta.servlet.http.HttpServletResponse;
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Data;
|
|
||||||
import org.ruoyi.common.excel.utils.ExcelUtil;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试Excel功能
|
|
||||||
*
|
|
||||||
* @author Lion Li
|
|
||||||
*/
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/demo/excel")
|
|
||||||
public class TestExcelController {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 单列表多数据
|
|
||||||
*/
|
|
||||||
@GetMapping("/exportTemplateOne")
|
|
||||||
public void exportTemplateOne(HttpServletResponse response) {
|
|
||||||
Map<String, String> map = new HashMap<>();
|
|
||||||
map.put("title", "单列表多数据");
|
|
||||||
map.put("test1", "数据测试1");
|
|
||||||
map.put("test2", "数据测试2");
|
|
||||||
map.put("test3", "数据测试3");
|
|
||||||
map.put("test4", "数据测试4");
|
|
||||||
map.put("testTest", "666");
|
|
||||||
List<TestObj> list = new ArrayList<>();
|
|
||||||
list.add(new TestObj("单列表测试1", "列表测试1", "列表测试2", "列表测试3", "列表测试4"));
|
|
||||||
list.add(new TestObj("单列表测试2", "列表测试5", "列表测试6", "列表测试7", "列表测试8"));
|
|
||||||
list.add(new TestObj("单列表测试3", "列表测试9", "列表测试10", "列表测试11", "列表测试12"));
|
|
||||||
ExcelUtil.exportTemplate(CollUtil.newArrayList(map, list), "单列表.xlsx", "excel/单列表.xlsx", response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 多列表多数据
|
|
||||||
*/
|
|
||||||
@GetMapping("/exportTemplateMuliti")
|
|
||||||
public void exportTemplateMuliti(HttpServletResponse response) {
|
|
||||||
Map<String, String> map = new HashMap<>();
|
|
||||||
map.put("title1", "标题1");
|
|
||||||
map.put("title2", "标题2");
|
|
||||||
map.put("title3", "标题3");
|
|
||||||
map.put("title4", "标题4");
|
|
||||||
map.put("author", "Lion Li");
|
|
||||||
List<TestObj1> list1 = new ArrayList<>();
|
|
||||||
list1.add(new TestObj1("list1测试1", "list1测试2", "list1测试3"));
|
|
||||||
list1.add(new TestObj1("list1测试4", "list1测试5", "list1测试6"));
|
|
||||||
list1.add(new TestObj1("list1测试7", "list1测试8", "list1测试9"));
|
|
||||||
List<TestObj1> list2 = new ArrayList<>();
|
|
||||||
list2.add(new TestObj1("list2测试1", "list2测试2", "list2测试3"));
|
|
||||||
list2.add(new TestObj1("list2测试4", "list2测试5", "list2测试6"));
|
|
||||||
List<TestObj1> list3 = new ArrayList<>();
|
|
||||||
list3.add(new TestObj1("list3测试1", "list3测试2", "list3测试3"));
|
|
||||||
List<TestObj1> list4 = new ArrayList<>();
|
|
||||||
list4.add(new TestObj1("list4测试1", "list4测试2", "list4测试3"));
|
|
||||||
list4.add(new TestObj1("list4测试4", "list4测试5", "list4测试6"));
|
|
||||||
list4.add(new TestObj1("list4测试7", "list4测试8", "list4测试9"));
|
|
||||||
list4.add(new TestObj1("list4测试10", "list4测试11", "list4测试12"));
|
|
||||||
Map<String, Object> multiListMap = new HashMap<>();
|
|
||||||
multiListMap.put("map", map);
|
|
||||||
multiListMap.put("data1", list1);
|
|
||||||
multiListMap.put("data2", list2);
|
|
||||||
multiListMap.put("data3", list3);
|
|
||||||
multiListMap.put("data4", list4);
|
|
||||||
ExcelUtil.exportTemplateMultiList(multiListMap, "多列表.xlsx", "excel/多列表.xlsx", response);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@AllArgsConstructor
|
|
||||||
static class TestObj1 {
|
|
||||||
private String test1;
|
|
||||||
private String test2;
|
|
||||||
private String test3;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@AllArgsConstructor
|
|
||||||
static class TestObj {
|
|
||||||
private String name;
|
|
||||||
private String list1;
|
|
||||||
private String list2;
|
|
||||||
private String list3;
|
|
||||||
private String list4;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
package org.ruoyi.demo.controller;
|
|
||||||
|
|
||||||
import jakarta.validation.constraints.NotBlank;
|
|
||||||
import jakarta.validation.constraints.NotNull;
|
|
||||||
import lombok.Data;
|
|
||||||
import org.ruoyi.common.core.domain.R;
|
|
||||||
import org.ruoyi.common.core.utils.MessageUtils;
|
|
||||||
import org.hibernate.validator.constraints.Range;
|
|
||||||
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;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试国际化
|
|
||||||
*
|
|
||||||
* @author Lion Li
|
|
||||||
*/
|
|
||||||
@Validated
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/demo/i18n")
|
|
||||||
public class TestI18nController {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 通过code获取国际化内容
|
|
||||||
* code为 messages.properties 中的 key
|
|
||||||
* <p>
|
|
||||||
* 测试使用 user.register.success
|
|
||||||
*
|
|
||||||
* @param code 国际化code
|
|
||||||
*/
|
|
||||||
@GetMapping()
|
|
||||||
public R<Void> get(String code) {
|
|
||||||
return R.ok(MessageUtils.message(code));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validator 校验国际化
|
|
||||||
* 不传值 分别查看异常返回
|
|
||||||
* <p>
|
|
||||||
* 测试使用 not.null
|
|
||||||
*/
|
|
||||||
@GetMapping("/test1")
|
|
||||||
public R<Void> test1(@NotBlank(message = "{not.null}") String str) {
|
|
||||||
return R.ok(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bean 校验国际化
|
|
||||||
* 不传值 分别查看异常返回
|
|
||||||
* <p>
|
|
||||||
* 测试使用 not.null
|
|
||||||
*/
|
|
||||||
@GetMapping("/test2")
|
|
||||||
public R<TestI18nBo> test2(@Validated TestI18nBo bo) {
|
|
||||||
return R.ok(bo);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Data
|
|
||||||
public static class TestI18nBo {
|
|
||||||
|
|
||||||
@NotBlank(message = "{not.null}")
|
|
||||||
private String name;
|
|
||||||
|
|
||||||
@NotNull(message = "{not.null}")
|
|
||||||
@Range(min = 0, max = 100, message = "{length.not.valid}")
|
|
||||||
private Integer age;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
package org.ruoyi.demo.controller;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import org.ruoyi.common.core.domain.R;
|
|
||||||
import org.ruoyi.common.sensitive.annotation.Sensitive;
|
|
||||||
import org.ruoyi.common.sensitive.core.SensitiveService;
|
|
||||||
import org.ruoyi.common.sensitive.core.SensitiveStrategy;
|
|
||||||
import org.ruoyi.common.web.core.BaseController;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试数据脱敏控制器
|
|
||||||
* <p>
|
|
||||||
* 默认管理员不过滤
|
|
||||||
* 需自行根据业务重写实现
|
|
||||||
*
|
|
||||||
* @author Lion Li
|
|
||||||
* @version 3.6.0
|
|
||||||
* @see SensitiveService
|
|
||||||
*/
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/demo/sensitive")
|
|
||||||
public class TestSensitiveController extends BaseController {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试数据脱敏
|
|
||||||
*/
|
|
||||||
@GetMapping("/test")
|
|
||||||
public R<TestSensitive> test() {
|
|
||||||
TestSensitive testSensitive = new TestSensitive();
|
|
||||||
testSensitive.setIdCard("210397198608215431");
|
|
||||||
testSensitive.setPhone("17640125371");
|
|
||||||
testSensitive.setAddress("北京市朝阳区某某四合院1203室");
|
|
||||||
testSensitive.setEmail("17640125371@163.com");
|
|
||||||
testSensitive.setBankCard("6226456952351452853");
|
|
||||||
return R.ok(testSensitive);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Data
|
|
||||||
static class TestSensitive {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 身份证
|
|
||||||
*/
|
|
||||||
@Sensitive(strategy = SensitiveStrategy.ID_CARD)
|
|
||||||
private String idCard;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 电话
|
|
||||||
*/
|
|
||||||
@Sensitive(strategy = SensitiveStrategy.PHONE)
|
|
||||||
private String phone;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 地址
|
|
||||||
*/
|
|
||||||
@Sensitive(strategy = SensitiveStrategy.ADDRESS)
|
|
||||||
private String address;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 邮箱
|
|
||||||
*/
|
|
||||||
@Sensitive(strategy = SensitiveStrategy.EMAIL)
|
|
||||||
private String email;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 银行卡
|
|
||||||
*/
|
|
||||||
@Sensitive(strategy = SensitiveStrategy.BANK_CARD)
|
|
||||||
private String bankCard;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
package org.ruoyi.demo.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.ruoyi.common.core.domain.R;
|
|
||||||
import org.ruoyi.common.core.validate.AddGroup;
|
|
||||||
import org.ruoyi.common.core.validate.EditGroup;
|
|
||||||
import org.ruoyi.common.core.validate.QueryGroup;
|
|
||||||
import org.ruoyi.common.excel.utils.ExcelUtil;
|
|
||||||
import org.ruoyi.common.idempotent.annotation.RepeatSubmit;
|
|
||||||
import org.ruoyi.common.log.annotation.Log;
|
|
||||||
import org.ruoyi.common.log.enums.BusinessType;
|
|
||||||
import org.ruoyi.common.web.core.BaseController;
|
|
||||||
import org.ruoyi.demo.domain.bo.TestTreeBo;
|
|
||||||
import org.ruoyi.demo.domain.vo.TestTreeVo;
|
|
||||||
import org.ruoyi.demo.service.ITestTreeService;
|
|
||||||
import org.springframework.validation.annotation.Validated;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试树表Controller
|
|
||||||
*
|
|
||||||
* @author Lion Li
|
|
||||||
* @date 2021-07-26
|
|
||||||
*/
|
|
||||||
@Validated
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/demo/tree")
|
|
||||||
public class TestTreeController extends BaseController {
|
|
||||||
|
|
||||||
private final ITestTreeService testTreeService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 查询测试树表列表
|
|
||||||
*/
|
|
||||||
@SaCheckPermission("demo:tree:list")
|
|
||||||
@GetMapping("/list")
|
|
||||||
public R<List<TestTreeVo>> list(@Validated(QueryGroup.class) TestTreeBo bo) {
|
|
||||||
List<TestTreeVo> list = testTreeService.queryList(bo);
|
|
||||||
return R.ok(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 导出测试树表列表
|
|
||||||
*/
|
|
||||||
@SaCheckPermission("demo:tree:export")
|
|
||||||
@Log(title = "测试树表", businessType = BusinessType.EXPORT)
|
|
||||||
@GetMapping("/export")
|
|
||||||
public void export(@Validated TestTreeBo bo, HttpServletResponse response) {
|
|
||||||
List<TestTreeVo> list = testTreeService.queryList(bo);
|
|
||||||
ExcelUtil.exportExcel(list, "测试树表", TestTreeVo.class, response);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取测试树表详细信息
|
|
||||||
*
|
|
||||||
* @param id 测试树ID
|
|
||||||
*/
|
|
||||||
@SaCheckPermission("demo:tree:query")
|
|
||||||
@GetMapping("/{id}")
|
|
||||||
public R<TestTreeVo> getInfo(@NotNull(message = "主键不能为空")
|
|
||||||
@PathVariable("id") Long id) {
|
|
||||||
return R.ok(testTreeService.queryById(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 新增测试树表
|
|
||||||
*/
|
|
||||||
@SaCheckPermission("demo:tree:add")
|
|
||||||
@Log(title = "测试树表", businessType = BusinessType.INSERT)
|
|
||||||
@RepeatSubmit
|
|
||||||
@PostMapping()
|
|
||||||
public R<Void> add(@Validated(AddGroup.class) @RequestBody TestTreeBo bo) {
|
|
||||||
return toAjax(testTreeService.insertByBo(bo));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 修改测试树表
|
|
||||||
*/
|
|
||||||
@SaCheckPermission("demo:tree:edit")
|
|
||||||
@Log(title = "测试树表", businessType = BusinessType.UPDATE)
|
|
||||||
@RepeatSubmit
|
|
||||||
@PutMapping()
|
|
||||||
public R<Void> edit(@Validated(EditGroup.class) @RequestBody TestTreeBo bo) {
|
|
||||||
return toAjax(testTreeService.updateByBo(bo));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除测试树表
|
|
||||||
*
|
|
||||||
* @param ids 测试树ID串
|
|
||||||
*/
|
|
||||||
@SaCheckPermission("demo:tree:remove")
|
|
||||||
@Log(title = "测试树表", businessType = BusinessType.DELETE)
|
|
||||||
@DeleteMapping("/{ids}")
|
|
||||||
public R<Void> remove(@NotEmpty(message = "主键不能为空")
|
|
||||||
@PathVariable Long[] ids) {
|
|
||||||
return toAjax(testTreeService.deleteWithValidByIds(Arrays.asList(ids), true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
package org.ruoyi.demo.controller.queue;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.ruoyi.common.core.domain.R;
|
|
||||||
import org.ruoyi.common.redis.utils.QueueUtils;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 有界队列 演示案例
|
|
||||||
* <p>
|
|
||||||
* 轻量级队列 重量级数据量 请使用 MQ
|
|
||||||
* <p>
|
|
||||||
* 集群测试通过 同一个数据只会被消费一次 做好事务补偿
|
|
||||||
* 集群测试流程 在其中一台发送数据 两端分别调用获取接口 一次获取一条
|
|
||||||
*
|
|
||||||
* @author Lion Li
|
|
||||||
* @version 3.6.0
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/demo/queue/bounded")
|
|
||||||
public class BoundedQueueController {
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加队列数据
|
|
||||||
*
|
|
||||||
* @param queueName 队列名
|
|
||||||
* @param capacity 容量
|
|
||||||
*/
|
|
||||||
@GetMapping("/add")
|
|
||||||
public R<Void> add(String queueName, int capacity) {
|
|
||||||
// 用完了一定要销毁 否则会一直存在
|
|
||||||
boolean b = QueueUtils.destroyQueue(queueName);
|
|
||||||
log.info("通道: {} , 删除: {}", queueName, b);
|
|
||||||
// 初始化设置一次即可
|
|
||||||
if (QueueUtils.trySetBoundedQueueCapacity(queueName, capacity)) {
|
|
||||||
log.info("通道: {} , 设置容量: {}", queueName, capacity);
|
|
||||||
} else {
|
|
||||||
log.info("通道: {} , 设置容量失败", queueName);
|
|
||||||
return R.fail("操作失败");
|
|
||||||
}
|
|
||||||
for (int i = 0; i < 11; i++) {
|
|
||||||
String data = "data-" + i;
|
|
||||||
boolean flag = QueueUtils.addBoundedQueueObject(queueName, data);
|
|
||||||
if (flag == false) {
|
|
||||||
log.info("通道: {} , 发送数据: {} 失败, 通道已满", queueName, data);
|
|
||||||
} else {
|
|
||||||
log.info("通道: {} , 发送数据: {}", queueName, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return R.ok("操作成功");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除队列数据
|
|
||||||
*
|
|
||||||
* @param queueName 队列名
|
|
||||||
*/
|
|
||||||
@GetMapping("/remove")
|
|
||||||
public R<Void> remove(String queueName) {
|
|
||||||
String data = "data-" + 5;
|
|
||||||
if (QueueUtils.removeQueueObject(queueName, data)) {
|
|
||||||
log.info("通道: {} , 删除数据: {}", queueName, data);
|
|
||||||
} else {
|
|
||||||
return R.fail("操作失败");
|
|
||||||
}
|
|
||||||
return R.ok("操作成功");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取队列数据
|
|
||||||
*
|
|
||||||
* @param queueName 队列名
|
|
||||||
*/
|
|
||||||
@GetMapping("/get")
|
|
||||||
public R<Void> get(String queueName) {
|
|
||||||
String data;
|
|
||||||
do {
|
|
||||||
data = QueueUtils.getQueueObject(queueName);
|
|
||||||
log.info("通道: {} , 获取数据: {}", queueName, data);
|
|
||||||
} while (data != null);
|
|
||||||
return R.ok("操作成功");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
package org.ruoyi.demo.controller.queue;
|
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.ruoyi.common.core.domain.R;
|
|
||||||
import org.ruoyi.common.redis.utils.QueueUtils;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 延迟队列 演示案例
|
|
||||||
* <p>
|
|
||||||
* 轻量级队列 重量级数据量 请使用 MQ
|
|
||||||
* 例如: 创建订单30分钟后过期处理
|
|
||||||
* <p>
|
|
||||||
* 集群测试通过 同一个数据只会被消费一次 做好事务补偿
|
|
||||||
* 集群测试流程 两台集群分别开启订阅 在其中一台发送数据 观察接收消息的规律
|
|
||||||
*
|
|
||||||
* @author Lion Li
|
|
||||||
* @version 3.6.0
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/demo/queue/delayed")
|
|
||||||
public class DelayedQueueController {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 订阅队列
|
|
||||||
*
|
|
||||||
* @param queueName 队列名
|
|
||||||
*/
|
|
||||||
@GetMapping("/subscribe")
|
|
||||||
public R<Void> subscribe(String queueName) {
|
|
||||||
log.info("通道: {} 监听中......", queueName);
|
|
||||||
// 项目初始化设置一次即可
|
|
||||||
QueueUtils.subscribeBlockingQueue(queueName, (String orderNum) -> {
|
|
||||||
// 观察接收时间
|
|
||||||
log.info("通道: {}, 收到数据: {}", queueName, orderNum);
|
|
||||||
});
|
|
||||||
return R.ok("操作成功");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加队列数据
|
|
||||||
*
|
|
||||||
* @param queueName 队列名
|
|
||||||
* @param orderNum 订单号
|
|
||||||
* @param time 延迟时间(秒)
|
|
||||||
*/
|
|
||||||
@GetMapping("/add")
|
|
||||||
public R<Void> add(String queueName, String orderNum, Long time) {
|
|
||||||
QueueUtils.addDelayedQueueObject(queueName, orderNum, time, TimeUnit.SECONDS);
|
|
||||||
// 观察发送时间
|
|
||||||
log.info("通道: {} , 发送数据: {}", queueName, orderNum);
|
|
||||||
return R.ok("操作成功");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除队列数据
|
|
||||||
*
|
|
||||||
* @param queueName 队列名
|
|
||||||
* @param orderNum 订单号
|
|
||||||
*/
|
|
||||||
@GetMapping("/remove")
|
|
||||||
public R<Void> remove(String queueName, String orderNum) {
|
|
||||||
if (QueueUtils.removeDelayedQueueObject(queueName, orderNum)) {
|
|
||||||
log.info("通道: {} , 删除数据: {}", queueName, orderNum);
|
|
||||||
} else {
|
|
||||||
return R.fail("操作失败");
|
|
||||||
}
|
|
||||||
return R.ok("操作成功");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 销毁队列
|
|
||||||
*
|
|
||||||
* @param queueName 队列名
|
|
||||||
*/
|
|
||||||
@GetMapping("/destroy")
|
|
||||||
public R<Void> destroy(String queueName) {
|
|
||||||
// 用完了一定要销毁 否则会一直存在
|
|
||||||
QueueUtils.destroyDelayedQueue(queueName);
|
|
||||||
return R.ok("操作成功");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package org.ruoyi.demo.controller.queue;
|
|
||||||
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 实体类 注意不允许使用内部类 否则会找不到类
|
|
||||||
*
|
|
||||||
* @author Lion Li
|
|
||||||
* @version 3.6.0
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@NoArgsConstructor
|
|
||||||
public class PriorityDemo implements Comparable<PriorityDemo> {
|
|
||||||
private String name;
|
|
||||||
private Integer orderNum;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compareTo(PriorityDemo other) {
|
|
||||||
return Integer.compare(getOrderNum(), other.getOrderNum());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
package org.ruoyi.demo.controller.queue;
|
|
||||||
|
|
||||||
import cn.hutool.core.util.RandomUtil;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.ruoyi.common.core.domain.R;
|
|
||||||
import org.ruoyi.common.redis.utils.QueueUtils;
|
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 优先队列 演示案例
|
|
||||||
* <p>
|
|
||||||
* 轻量级队列 重量级数据量 请使用 MQ
|
|
||||||
* <p>
|
|
||||||
* 集群测试通过 同一个消息只会被消费一次 做好事务补偿
|
|
||||||
* 集群测试流程 在其中一台发送数据 两端分别调用获取接口 一次获取一条
|
|
||||||
*
|
|
||||||
* @author Lion Li
|
|
||||||
* @version 3.6.0
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
@RequiredArgsConstructor
|
|
||||||
@RestController
|
|
||||||
@RequestMapping("/demo/queue/priority")
|
|
||||||
public class PriorityQueueController {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加队列数据
|
|
||||||
*
|
|
||||||
* @param queueName 队列名
|
|
||||||
*/
|
|
||||||
@GetMapping("/add")
|
|
||||||
public R<Void> add(String queueName) {
|
|
||||||
// 用完了一定要销毁 否则会一直存在
|
|
||||||
boolean b = QueueUtils.destroyQueue(queueName);
|
|
||||||
log.info("通道: {} , 删除: {}", queueName, b);
|
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
int randomNum = RandomUtil.randomInt(10);
|
|
||||||
PriorityDemo data = new PriorityDemo();
|
|
||||||
data.setName("data-" + i);
|
|
||||||
data.setOrderNum(randomNum);
|
|
||||||
if (QueueUtils.addPriorityQueueObject(queueName, data)) {
|
|
||||||
log.info("通道: {} , 发送数据: {}", queueName, data);
|
|
||||||
} else {
|
|
||||||
log.info("通道: {} , 发送数据: {}, 发送失败", queueName, data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return R.ok("操作成功");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除队列数据
|
|
||||||
*
|
|
||||||
* @param queueName 队列名
|
|
||||||
* @param name 对象名
|
|
||||||
* @param orderNum 排序号
|
|
||||||
*/
|
|
||||||
@GetMapping("/remove")
|
|
||||||
public R<Void> remove(String queueName, String name, Integer orderNum) {
|
|
||||||
PriorityDemo data = new PriorityDemo();
|
|
||||||
data.setName(name);
|
|
||||||
data.setOrderNum(orderNum);
|
|
||||||
if (QueueUtils.removeQueueObject(queueName, data)) {
|
|
||||||
log.info("通道: {} , 删除数据: {}", queueName, data);
|
|
||||||
} else {
|
|
||||||
return R.fail("操作失败");
|
|
||||||
}
|
|
||||||
return R.ok("操作成功");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取队列数据
|
|
||||||
*
|
|
||||||
* @param queueName 队列名
|
|
||||||
*/
|
|
||||||
@GetMapping("/get")
|
|
||||||
public R<Void> get(String queueName) {
|
|
||||||
PriorityDemo data;
|
|
||||||
do {
|
|
||||||
data = QueueUtils.getQueueObject(queueName);
|
|
||||||
log.info("通道: {} , 获取数据: {}", queueName, data);
|
|
||||||
} while (data != null);
|
|
||||||
return R.ok("操作成功");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
package org.ruoyi.demo.domain;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.*;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
import org.ruoyi.common.tenant.core.TenantEntity;
|
|
||||||
|
|
||||||
import java.io.Serial;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试单表对象 test_demo
|
|
||||||
*
|
|
||||||
* @author Lion Li
|
|
||||||
* @date 2021-07-26
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@TableName("test_demo")
|
|
||||||
public class TestDemo extends TenantEntity {
|
|
||||||
|
|
||||||
@Serial
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 主键
|
|
||||||
*/
|
|
||||||
@TableId(value = "id")
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 部门id
|
|
||||||
*/
|
|
||||||
private Long deptId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户id
|
|
||||||
*/
|
|
||||||
private Long userId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 排序号
|
|
||||||
*/
|
|
||||||
@OrderBy(asc = false, sort = 1)
|
|
||||||
private Integer orderNum;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* key键
|
|
||||||
*/
|
|
||||||
private String testKey;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 值
|
|
||||||
*/
|
|
||||||
private String value;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 版本
|
|
||||||
*/
|
|
||||||
@Version
|
|
||||||
private Long version;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除标志
|
|
||||||
*/
|
|
||||||
@TableLogic
|
|
||||||
private Long delFlag;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
package org.ruoyi.demo.domain;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
import org.ruoyi.common.encrypt.annotation.EncryptField;
|
|
||||||
import org.ruoyi.common.encrypt.enumd.AlgorithmType;
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@TableName("test_demo")
|
|
||||||
public class TestDemoEncrypt extends TestDemo {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* key键
|
|
||||||
*/
|
|
||||||
// @EncryptField(algorithm=AlgorithmType.SM2, privateKey = "MIGTAgEAMBMGByqGSM49AgEGCCqBHM9VAYItBHkwdwIBAQQgZSlOvw8FBiH+aFJWLYZP/VRjg9wjfRarTkGBZd/T3N+gCgYIKoEcz1UBgi2hRANCAAR5DGuQwJqkxnbCsP+iPSDoHWIF4RwcR5EsSvT8QPxO1wRkR2IhCkzvRb32x2CUgJFdvoqVqfApFDPZzShqzBwX", publicKey = "MFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAEeQxrkMCapMZ2wrD/oj0g6B1iBeEcHEeRLEr0/ED8TtcEZEdiIQpM70W99sdglICRXb6KlanwKRQz2c0oaswcFw==")
|
|
||||||
@EncryptField(algorithm = AlgorithmType.RSA, privateKey = "MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBANBBEeueWlXlkkj2+WY5l+IWe42d8b5K28g+G/CFKC/yYAEHtqGlCsBOrb+YBkG9mPzmuYA/n9k0NFIc8E8yY5vZQaroyFBrTTWEzG9RY2f7Y3svVyybs6jpXSUs4xff8abo7wL1Y/wUaeatTViamxYnyTvdTmLm3d+JjRij68rxAgMBAAECgYAB0TnhXraSopwIVRfmboea1b0upl+BUdTJcmci412UjrKr5aE695ZLPkXbFXijVu7HJlyyv94NVUdaMACV7Ku/S2RuNB70M7YJm8rAjHFC3/i2ZeIM60h1Ziy4QKv0XM3pRATlDCDNhC1WUrtQCQSgU8kcp6eUUppruOqDzcY04QJBAPm9+sBP9CwDRgy3e5+V8aZtJkwDstb0lVVV/KY890cydVxiCwvX3fqVnxKMlb+x0YtH0sb9v+71xvK2lGobaRECQQDVePU6r/cCEfpc+nkWF6osAH1f8Mux3rYv2DoBGvaPzV2BGfsLed4neRfCwWNCKvGPCdW+L0xMJg8+RwaoBUPhAkAT5kViqXxFPYWJYd1h2+rDXhMdH3ZSlm6HvDBDdrwlWinr0Iwcx3iSjPV93uHXwm118aUj4fg3LDJMCKxOwBxhAkByrQXfvwOMYygBprRBf/j0plazoWFrbd6lGR0f1uI5IfNnFRPdeFw1DEINZ2Hw+6zEUF44SqRMC+4IYJNc02dBAkBCgy7RvfyV/A7N6kKXxTHauY0v6XwSSvpeKtRJkbIcRWOdIYvaHO9L7cklj3vIEdwjSUp9K4VTBYYlmAz1xh03", publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQQRHrnlpV5ZJI9vlmOZfiFnuNnfG+StvIPhvwhSgv8mABB7ahpQrATq2/mAZBvZj85rmAP5/ZNDRSHPBPMmOb2UGq6MhQa001hMxvUWNn+2N7L1csm7Oo6V0lLOMX3/Gm6O8C9WP8FGnmrU1YmpsWJ8k73U5i5t3fiY0Yo+vK8QIDAQAB")
|
|
||||||
private String testKey;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 值
|
|
||||||
*/
|
|
||||||
// @EncryptField // 什么也不写走默认yml配置
|
|
||||||
// @EncryptField(algorithm = AlgorithmType.SM4, password = "10rfylhtccpuyke5")
|
|
||||||
@EncryptField(algorithm = AlgorithmType.AES, password = "10rfylhtccpuyke5")
|
|
||||||
private String value;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
package org.ruoyi.demo.domain;
|
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableLogic;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
|
||||||
import com.baomidou.mybatisplus.annotation.Version;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
import org.ruoyi.common.tenant.core.TenantEntity;
|
|
||||||
|
|
||||||
import java.io.Serial;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试树表对象 test_tree
|
|
||||||
*
|
|
||||||
* @author Lion Li
|
|
||||||
* @date 2021-07-26
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@TableName("test_tree")
|
|
||||||
public class TestTree extends TenantEntity {
|
|
||||||
|
|
||||||
@Serial
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 主键
|
|
||||||
*/
|
|
||||||
@TableId(value = "id")
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 父ID
|
|
||||||
*/
|
|
||||||
private Long parentId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 部门id
|
|
||||||
*/
|
|
||||||
private Long deptId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户id
|
|
||||||
*/
|
|
||||||
private Long userId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 树节点名
|
|
||||||
*/
|
|
||||||
private String treeName;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 版本
|
|
||||||
*/
|
|
||||||
@Version
|
|
||||||
private Long version;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除标志
|
|
||||||
*/
|
|
||||||
@TableLogic
|
|
||||||
private Long delFlag;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
package org.ruoyi.demo.domain.bo;
|
|
||||||
|
|
||||||
import io.github.linpeilie.annotations.AutoMapper;
|
|
||||||
import jakarta.validation.constraints.NotBlank;
|
|
||||||
import jakarta.validation.constraints.NotNull;
|
|
||||||
import lombok.Data;
|
|
||||||
import lombok.EqualsAndHashCode;
|
|
||||||
import org.ruoyi.common.core.validate.AddGroup;
|
|
||||||
import org.ruoyi.common.core.validate.EditGroup;
|
|
||||||
import org.ruoyi.core.domain.BaseEntity;
|
|
||||||
import org.ruoyi.demo.domain.TestDemo;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试单表业务对象 test_demo
|
|
||||||
*
|
|
||||||
* @author Lion Li
|
|
||||||
* @date 2021-07-26
|
|
||||||
*/
|
|
||||||
|
|
||||||
@Data
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
|
||||||
@AutoMapper(target = TestDemo.class, reverseConvertGenerate = false)
|
|
||||||
public class TestDemoBo extends BaseEntity {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 主键
|
|
||||||
*/
|
|
||||||
@NotNull(message = "主键不能为空", groups = {EditGroup.class})
|
|
||||||
private Long id;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 部门id
|
|
||||||
*/
|
|
||||||
@NotNull(message = "部门id不能为空", groups = {AddGroup.class, EditGroup.class})
|
|
||||||
private Long deptId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户id
|
|
||||||
*/
|
|
||||||
@NotNull(message = "用户id不能为空", groups = {AddGroup.class, EditGroup.class})
|
|
||||||
private Long userId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 排序号
|
|
||||||
*/
|
|
||||||
@NotNull(message = "排序号不能为空", groups = {AddGroup.class, EditGroup.class})
|
|
||||||
private Integer orderNum;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* key键
|
|
||||||
*/
|
|
||||||
@NotBlank(message = "key键不能为空", groups = {AddGroup.class, EditGroup.class})
|
|
||||||
private String testKey;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 值
|
|
||||||
*/
|
|
||||||
@NotBlank(message = "值不能为空", groups = {AddGroup.class, EditGroup.class})
|
|
||||||
private String value;
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
package org.ruoyi.demo.domain.bo;
|
|
||||||
|
|
||||||
import com.alibaba.excel.annotation.ExcelProperty;
|
|
||||||
import jakarta.validation.constraints.NotBlank;
|
|
||||||
import jakarta.validation.constraints.NotNull;
|
|
||||||
import lombok.Data;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 测试单表业务对象 test_demo
|
|
||||||
*
|
|
||||||
* @author Lion Li
|
|
||||||
* @date 2021-07-26
|
|
||||||
*/
|
|
||||||
@Data
|
|
||||||
public class TestDemoImportVo {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 部门id
|
|
||||||
*/
|
|
||||||
@NotNull(message = "部门id不能为空")
|
|
||||||
@ExcelProperty(value = "部门id")
|
|
||||||
private Long deptId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 用户id
|
|
||||||
*/
|
|
||||||
@NotNull(message = "用户id不能为空")
|
|
||||||
@ExcelProperty(value = "用户id")
|
|
||||||
private Long userId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 排序号
|
|
||||||
*/
|
|
||||||
@NotNull(message = "排序号不能为空")
|
|
||||||
@ExcelProperty(value = "排序号")
|
|
||||||
private Long orderNum;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* key键
|
|
||||||
*/
|
|
||||||
@NotBlank(message = "key键不能为空")
|
|
||||||
@ExcelProperty(value = "key键")
|
|
||||||
private String testKey;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 值
|
|
||||||
*/
|
|
||||||
@NotBlank(message = "值不能为空")
|
|
||||||
@ExcelProperty(value = "值")
|
|
||||||
private String value;
|
|
||||||
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user