init: 导入RuoYi‑Vue‑Plus 6.X完整代码

This commit is contained in:
admin
2026-08-09 17:51:11 +08:00
commit 51cac459b3
914 changed files with 103833 additions and 0 deletions
@@ -0,0 +1,23 @@
# 贝尔实验室 Spring 官方推荐镜像 JDK下载地址 https://bell-sw.com/pages/downloads/
FROM bellsoft/liberica-openjdk-rocky:21.0.12-cds
# FROM bellsoft/liberica-openjdk-rocky:25.0.4-cds
# FROM findepi/graalvm:java21-native
LABEL maintainer="Lion Li"
RUN mkdir -p /ruoyi/snailai/logs
WORKDIR /ruoyi/snailai
ENV LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS=""
EXPOSE 8900
EXPOSE 18888
ADD ./target/ruoyi-snailai-server.jar ./app.jar
SHELL ["/bin/bash", "-c"]
ENTRYPOINT java -Djava.security.egd=file:/dev/./urandom \
-XX:+HeapDumpOnOutOfMemoryError -XX:+UseZGC ${JAVA_OPTS} \
-jar app.jar
+59
View File
@@ -0,0 +1,59 @@
<?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.dromara</groupId>
<artifactId>ruoyi-extend</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ruoyi-snailai-server</artifactId>
<dependencies>
<!-- Snail AI 服务端 -->
<dependency>
<groupId>com.aizuda</groupId>
<artifactId>snail-ai-starter</artifactId>
<version>${snailai.version}</version>
</dependency>
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-rest5-client</artifactId>
<version>9.4.2</version>
</dependency>
<dependency>
<groupId>co.elastic.clients</groupId>
<artifactId>elasticsearch-java</artifactId>
<version>9.4.2</version>
</dependency>
<!-- Spring Boot Admin 客户端 -->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>${spring-boot-admin.version}</version>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring-boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,92 @@
package com.aizuda.snail.ai.starter.filter;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
/**
* Actuator Basic Auth 认证过滤器。
*
* @author Lion Li
*/
public class ActuatorAuthFilter implements Filter {
/**
* 认证用户名。
*/
private final String username;
/**
* 认证密码。
*/
private final String password;
/**
* 创建 Actuator 认证过滤器。
*
* @param username 认证用户名
* @param password 认证密码
*/
public ActuatorAuthFilter(String username, String password) {
this.username = username;
this.password = password;
}
/**
* 校验 Actuator Basic Auth 请求。
*
* @param servletRequest 原始请求
* @param servletResponse 原始响应
* @param filterChain 过滤器链
* @throws IOException IO 异常
* @throws ServletException Servlet 异常
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String authHeader = request.getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("Basic ")) {
response.setHeader("WWW-Authenticate", "Basic realm=\"realm\"");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
return;
}
String base64Credentials = authHeader.substring("Basic ".length());
byte[] credDecoded = Base64.getDecoder().decode(base64Credentials);
String credentials = new String(credDecoded, StandardCharsets.UTF_8);
String[] split = credentials.split(":");
if (split.length != 2) {
response.setHeader("WWW-Authenticate", "Basic realm=\"realm\"");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
return;
}
if (!username.equals(split[0]) || !password.equals(split[1])) {
response.setHeader("WWW-Authenticate", "Basic realm=\"realm\"");
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized");
return;
}
filterChain.doFilter(request, response);
}
/**
* 初始化过滤器。
*
* @param filterConfig 过滤器配置
*/
@Override
public void init(FilterConfig filterConfig) {
}
/**
* 销毁过滤器。
*/
@Override
public void destroy() {
}
}
@@ -0,0 +1,40 @@
package com.aizuda.snail.ai.starter.filter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 权限安全配置
*
* @author Lion Li
*/
@Configuration
public class SecurityConfig {
/**
* 认证用户名。
*/
@Value("${spring.boot.admin.client.username}")
private String username;
/**
* 认证密码。
*/
@Value("${spring.boot.admin.client.password}")
private String password;
/**
* 注册 Actuator Basic Auth 过滤器。
*
* @return Actuator 过滤器注册对象
*/
@Bean
public FilterRegistrationBean<ActuatorAuthFilter> actuatorFilterRegistrationBean() {
FilterRegistrationBean<ActuatorAuthFilter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new ActuatorAuthFilter(username, password));
registrationBean.addUrlPatterns("/actuator", "/actuator/*");
return registrationBean;
}
}
@@ -0,0 +1,23 @@
package org.dromara.snailai;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* Snail AI Server 启动程序
*
* @author Lion Li
* @date 2026-05-26
*/
@SpringBootApplication
public class SnailAiServerApplication {
/**
* Snail AI 服务启动入口。
*
* @param args 启动参数
*/
public static void main(String[] args) {
com.aizuda.snail.ai.starter.SnailAiApplication.main(args);
}
}
@@ -0,0 +1,28 @@
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: root
password: root
hikari:
connection-timeout: 30000
validation-timeout: 5000
minimum-idle: 10
maximum-pool-size: 20
idle-timeout: 600000
max-lifetime: 900000
keepaliveTime: 30000
--- # 监控中心配置
spring.boot.admin.client:
# 增加客户端开关
enabled: true
url: http://localhost:9090/admin
instance:
service-host-type: IP
metadata:
username: ${spring.boot.admin.client.username}
userpassword: ${spring.boot.admin.client.password}
username: @monitor.username@
password: @monitor.password@
@@ -0,0 +1,28 @@
spring:
datasource:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: root
password: root
hikari:
connection-timeout: 30000
validation-timeout: 5000
minimum-idle: 10
maximum-pool-size: 20
idle-timeout: 600000
max-lifetime: 900000
keepaliveTime: 30000
--- # 监控中心配置
spring.boot.admin.client:
# 增加客户端开关
enabled: true
url: http://localhost:9090/admin
instance:
service-host-type: IP
metadata:
username: ${spring.boot.admin.client.username}
userpassword: ${spring.boot.admin.client.password}
username: @monitor.username@
password: @monitor.password@
@@ -0,0 +1,87 @@
server:
port: 8900
servlet:
context-path: /snail-ai
spring:
application:
name: ruoyi-snailai-server
profiles:
active: @profiles.active@
servlet:
multipart:
enabled: true
# 单个文件的最大限制 (根据 RAG 需求建议设为 50MB - 100MB)
max-file-size: 50MB
# 单次请求的总文件大小限制 (如果支持多文件并行上传,调大此项)
max-request-size: 100MB
web:
resources:
static-locations: classpath:admin/
snail-ai:
server:
grpc-port: 18888
skill:
upload-dir: ./upload/skills
crypto:
secret-key: ${SNAIL_AI_CRYPTO_KEY:0123456789abcdef0123456789abcdef}
iv: ${SNAIL_AI_CRYPTO_IV:fedcba9876543210fedcba9876543210}
resource:
storage-type: LOCAL
upload-dir: ./upload/resource
minio:
endpoint: http://localhost:9000
access-key: minioadmin
secret-key: minioadmin
bucket: snail-ai
# RAG 文档解析与图片 OCR 配置
rag:
docling:
enabled: ${SNAIL_AI_RAG_DOCLING_ENABLED:true}
url: ${SNAIL_AI_RAG_DOCLING_URL:http://127.0.0.1:5100}
timeout-seconds: ${SNAIL_AI_RAG_DOCLING_TIMEOUT_SECONDS:300}
concurrency: ${SNAIL_AI_RAG_DOCLING_CONCURRENCY:3}
health-timeout-millis: ${SNAIL_AI_RAG_DOCLING_HEALTH_TIMEOUT_MILLIS:3000}
poll-interval-millis: ${SNAIL_AI_RAG_DOCLING_POLL_INTERVAL_MILLIS:3000}
status-timeout-millis: ${SNAIL_AI_RAG_DOCLING_STATUS_TIMEOUT_MILLIS:10000}
result-timeout-millis: ${SNAIL_AI_RAG_DOCLING_RESULT_TIMEOUT_MILLIS:30000}
max-image-count: ${SNAIL_AI_RAG_DOCLING_MAX_IMAGE_COUNT:100}
max-image-bytes: ${SNAIL_AI_RAG_DOCLING_MAX_IMAGE_BYTES:10485760}
paddle-ocr-enabled: ${SNAIL_AI_RAG_DOCLING_PADDLE_OCR_ENABLED:true}
paddle-ocr-url: ${SNAIL_AI_RAG_DOCLING_PADDLE_OCR_URL:http://127.0.0.1:8866/ocr}
paddle-ocr-timeout-millis: ${SNAIL_AI_RAG_DOCLING_PADDLE_OCR_TIMEOUT_MILLIS:180000}
paddle-ocr-batch-size: ${SNAIL_AI_RAG_DOCLING_PADDLE_OCR_BATCH_SIZE:5}
vision-ocr-fallback-enabled: ${SNAIL_AI_RAG_DOCLING_VISION_OCR_FALLBACK_ENABLED:false}
# 短期记忆配置
memory:
short-term:
# 存储类型选项: memory(内存) | db(数据库)
# - memory: 适用于单机部署,高性能,重启后数据丢失
# - db: 适用于分布式部署,数据持久化,性能较低
store-type: db
mybatis-plus:
typeAliasesPackage: com.aizuda.snail.ai.persistence
global-config:
db-config:
capital-mode: false
logic-delete-value: 1
logic-not-delete-value: 0
configuration:
map-underscore-to-camel-case: true
cache-enabled: true
logging:
config: classpath:logback-plus.xml
management:
endpoints:
web:
exposure:
include: '*'
endpoint:
health:
show-details: ALWAYS
logfile:
external-file: ./logs/ruoyi-snailai-server/console.log
@@ -0,0 +1,9 @@
Application Version: ${revision}
Spring Boot Version: ${spring-boot.version}
_ _ _
(_) | (_)
___ _ __ __ _ _| |______ __ _ _
/ __| '_ \ / _` | | |______/ _` | |
\__ \ | | | (_| | | | | (_| | |
|___/_| |_|\__,_|_|_| \__,_|_|
@@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="log.path" value="./logs/ruoyi-snailai-server" />
<property name="console.log.pattern"
value="%cyan(%d{yyyy-MM-dd HH:mm:ss}) %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{36}%n) - %msg%n"/>
<property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"/>
<!-- 控制台输出 -->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${console.log.pattern}</pattern>
<charset>utf-8</charset>
</encoder>
</appender>
<!-- 控制台输出 -->
<appender name="file_console" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/console.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 日志文件名格式 -->
<fileNamePattern>${log.path}/console.%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- 日志最大 1天 -->
<maxHistory>1</maxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
<charset>utf-8</charset>
</encoder>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<!-- 过滤的级别 -->
<level>INFO</level>
</filter>
</appender>
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/info.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${log.path}/info.%d{yyyy-MM-dd}.log</FileNamePattern>
<MaxHistory>60</MaxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${log.path}/error.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<FileNamePattern>${log.path}/error.%d{yyyy-MM-dd}.log
</FileNamePattern>
<MaxHistory>60</MaxHistory>
</rollingPolicy>
<encoder>
<pattern>${log.pattern}</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender>
<appender name ="async_info" class= "ch.qos.logback.classic.AsyncAppender">
<discardingThreshold >100</discardingThreshold>
<queueSize>1024</queueSize>
<appender-ref ref ="file_info"/>
</appender>
<appender name ="async_error" class= "ch.qos.logback.classic.AsyncAppender">
<discardingThreshold >100</discardingThreshold>
<queueSize>1024</queueSize>
<appender-ref ref ="file_error"/>
</appender>
<!-- 控制台输出日志级别 -->
<root level="info">
<appender-ref ref="console" />
<appender-ref ref="file_console" />
<appender-ref ref="async_info" />
<appender-ref ref="async_error" />
</root>
</configuration>