feat: 发布3.0版本,新增文档处理能力和演示模式

- 升级langchain4j版本至1.13.0
- 新增docx/pdf/xlsx文档处理技能模块
- 添加演示模式配置和切面拦截
- 优化聊天服务和可观测性监听器

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
wangle
2026-04-13 18:02:27 +08:00
parent bf7b5eac72
commit c1fc02894b
100 changed files with 27576 additions and 189 deletions

View File

@@ -0,0 +1,86 @@
package org.ruoyi.common.web.aspectj;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.ruoyi.common.core.exception.ServiceException;
import org.ruoyi.common.core.utils.ServletUtils;
import org.ruoyi.common.web.config.properties.DemoProperties;
import java.util.Set;
/**
* 演示模式切面 - 拦截写操作
*
* @author ruoyi
*/
@Slf4j
@Aspect
@RequiredArgsConstructor
public class DemoAspect {
private final DemoProperties demoProperties;
/**
* 需要拦截的 HTTP 方法
*/
private static final Set<String> WRITE_METHODS = Set.of(
"POST", "PUT", "DELETE", "PATCH"
);
/**
* 拦截所有 Controller 的写操作
*/
@Around("execution(* org.ruoyi..controller..*.*(..))")
public Object around(ProceedingJoinPoint point) throws Throwable {
// 未开启演示模式,直接放行
if (!Boolean.TRUE.equals(demoProperties.getEnabled())) {
return point.proceed();
}
HttpServletRequest request = ServletUtils.getRequest();
if (request == null) {
return point.proceed();
}
String method = request.getMethod();
String requestUri = request.getRequestURI();
// 非写操作,放行
if (!WRITE_METHODS.contains(method.toUpperCase())) {
return point.proceed();
}
// 检查排除路径
for (String exclude : demoProperties.getExcludes()) {
if (match(exclude, requestUri)) {
return point.proceed();
}
}
log.info("演示模式拦截写操作: {} {}", method, requestUri);
throw new ServiceException(demoProperties.getMessage());
}
/**
* 路径匹配(支持通配符 * 和 **
*/
private boolean match(String pattern, String path) {
if (pattern.endsWith("/**")) {
String prefix = pattern.substring(0, pattern.length() - 3);
return path.startsWith(prefix);
}
if (pattern.endsWith("/*")) {
String prefix = pattern.substring(0, pattern.length() - 2);
if (!path.startsWith(prefix)) {
return false;
}
String suffix = path.substring(prefix.length());
return suffix.indexOf('/') == -1;
}
return path.equals(pattern);
}
}

View File

@@ -0,0 +1,25 @@
package org.ruoyi.common.web.config;
import org.ruoyi.common.web.aspectj.DemoAspect;
import org.ruoyi.common.web.config.properties.DemoProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 演示模式自动配置
*
* @author ruoyi
*/
@Configuration
@EnableConfigurationProperties(DemoProperties.class)
public class DemoAutoConfiguration {
/**
* 注册演示模式切面
*/
@Bean
public DemoAspect demoAspect(DemoProperties demoProperties) {
return new DemoAspect(demoProperties);
}
}

View File

@@ -0,0 +1,32 @@
package org.ruoyi.common.web.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.ArrayList;
import java.util.List;
/**
* 演示模式 配置属性
*
* @author ruoyi
*/
@Data
@ConfigurationProperties(prefix = "demo")
public class DemoProperties {
/**
* 是否开启演示模式
*/
private Boolean enabled = false;
/**
* 提示消息
*/
private String message = "演示模式,不允许进行写操作";
/**
* 排除的路径(这些路径不受演示模式限制)
*/
private List<String> excludes = new ArrayList<>();
}

View File

@@ -1,4 +1,5 @@
org.ruoyi.common.web.config.CaptchaConfig
org.ruoyi.common.web.config.DemoAutoConfiguration
org.ruoyi.common.web.config.FilterConfig
org.ruoyi.common.web.config.I18nConfig
org.ruoyi.common.web.config.ResourcesConfig