【v3.8.3开源版本发布】

Merge remote-tracking branch 'origin/master' into springboot3

# Conflicts:
#	README.md
#	jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/exception/JeecgBootExceptionHandler.java
#	jeecg-boot/jeecg-boot-base-core/src/main/java/org/jeecg/common/util/FileDownloadUtils.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/controller/SysUserAgentController.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/ISysDepartService.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/ISysUserService.java
#	jeecg-boot/jeecg-module-system/jeecg-system-biz/src/main/java/org/jeecg/modules/system/service/impl/SysAnnouncementServiceImpl.java
#	jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-sentinel/pom.xml
#	jeecg-boot/jeecg-server-cloud/jeecg-visual/jeecg-cloud-test/jeecg-cloud-test-seata/pom.xml
#	jeecg-boot/pom.xml
This commit is contained in:
JEECG
2025-09-24 12:01:11 +08:00
308 changed files with 12213 additions and 84594 deletions

View File

@ -1,5 +1,6 @@
package org.jeecg.common.api;
import org.jeecg.common.api.dto.AiragFlowDTO;
import org.jeecg.common.system.vo.*;
import java.util.List;
@ -144,4 +145,15 @@ public interface CommonAPI {
List<DictModel> translateDictFromTableByKeys(String table, String text, String code, String keys, String dataSource);
//update-end---author:chenrui ---date:20231221 for[issues/#5643]解决分布式下表字典跨库无法查询问题------------
/**
* 16 运行AIRag流程
* for [QQYUN-13634]在baseapi里面封装方法方便其他模块调用
*
* @param airagFlowDTO
* @return 流程执行结果,可能是String或者Map
* @author chenrui
* @date 2025/9/2 11:43
*/
Object runAiragFlow(AiragFlowDTO airagFlowDTO);
}

View File

@ -0,0 +1,36 @@
package org.jeecg.common.api.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Map;
/**
* 调用AI流程入参
* for [QQYUN-13634]在baseapi里面封装方法方便其他模块调用
* @author chenrui
* @date 2025/9/2 14:11
*/
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
public class AiragFlowDTO implements Serializable {
private static final long serialVersionUID = 7431775881170684867L;
/**
* 流程id
*/
private String flowId;
/**
* 输入参数
*/
private Map<String, Object> inputParams;
}

View File

@ -642,11 +642,12 @@ public interface CommonConstant {
/**
* 自定义首页关联关系(ROLE:表示角色 USER:表示用户)
* 自定义首页关联关系(ROLE:表示角色 USER:表示用户 DEFAULT:默认首页)
*
*/
String HOME_RELATION_ROLE = "ROLE";
String HOME_RELATION_USER = "USER";
String HOME_RELATION_DEFAULT = "DEFAULT";
/**
* 是否置顶(0否 1是)
@ -659,4 +660,53 @@ public interface CommonConstant {
String FLOW_FOCUS_NOTICE_PREFIX = "flow:runtimeData:focus:notice:";
//任务缓办时间缓存前缀
String FLOW_TASK_DELAY_PREFIX = "flow:runtimeData:task:delay:";
/**
* 用户代理类型离职quit 代理agent
*/
String USER_AGENT_TYPE_QUIT = "quit";
String USER_AGENT_TYPE_AGENT = "agent";
/**
* 督办流程首节点任务taskKey
*/
String SUPERVISE_FIRST_TASK_KEY = "Task_1bhxpt0";
/**
* wps模板预览数据缓存前缀
*/
String EOA_WPS_TEMPLATE_VIEW_DATA ="eoa:wps:templateViewData:";
/**
* wps模板预览版本号缓存前缀
*/
String EOA_WPS_TEMPLATE_VIEW_VERSION ="eoa:wps:templateViewVersion:";
/**
* 表单设计器oa新增字段
* x_oa_timeout_date:逾期时间
* x_oa_archive_status:归档状态
*/
String X_OA_TIMEOUT_DATE ="x_oa_timeout_date";
String X_OA_ARCHIVE_STATUS ="x_oa_archive_status";
/**
* 流程状态
* 待提交: 1
* 处理中: 2
* 已完成: 3
* 已作废: 4
* 已挂起: 5
*/
String BPM_STATUS_1 ="1";
String BPM_STATUS_2 ="2";
String BPM_STATUS_3 ="3";
String BPM_STATUS_4 ="4";
String BPM_STATUS_5 ="5";
/**
* 默认租户产品包
*/
String TENANT_PACK_DEFAULT = "default";
/**
* 部门名称redisKey(全路径)
*/
String DEPART_NAME_REDIS_KEY_PRE = "sys:cache:departPathName:";
}

View File

@ -0,0 +1,15 @@
package org.jeecg.common.constant;
/**
* @Description: 密码常量类
*
* @author: wangshuai
* @date: 2025/8/27 20:10
*/
public interface PasswordConstant {
/**
* 导入用户默认密码
*/
String DEFAULT_PASSWORD = "123456";
}

View File

@ -121,7 +121,7 @@ public class ProvinceCityArea {
public void getAreaByCode(String code,List<String> ls){
for(Area area: areaList){
if(area.getId().equals(code)){
if(null != area && area.getId().equals(code)){
String pid = area.getPid();
ls.add(0,area.getText());
getAreaByCode(pid,ls);

View File

@ -0,0 +1,97 @@
package org.jeecg.common.constant.enums;
import org.jeecg.common.util.oConvertUtils;
/**
* @Description: 部门类型枚举类
*
* @author: wangshuai
* @date: 2025/8/19 21:37
*/
public enum DepartCategoryEnum {
DEPART_CATEGORY_COMPANY("部门类型:公司","公司","1"),
DEPART_CATEGORY_DEPART("部门类型:部门","部门","2"),
DEPART_CATEGORY_POST("部门类型:岗位","岗位","3"),
DEPART_CATEGORY_SUB_COMPANY("部门类型:子公司","子公司","4");
DepartCategoryEnum(String described, String name, String value) {
this.value = value;
this.name = name;
this.described = described;
}
/**
* 描述
*/
private String described;
/**
* 值
*/
private String value;
/**
* 名称
*/
private String name;
public String getDescribed() {
return described;
}
public void setDescribed(String described) {
this.described = described;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* 根据值获取名称
*
* @param value
* @return
*/
public static String getNameByValue(String value){
if (oConvertUtils.isEmpty(value)) {
return null;
}
for (DepartCategoryEnum val : values()) {
if (val.getValue().equals(value)) {
return val.getName();
}
}
return value;
}
/**
* 根据名称获取值
*
* @param name
* @return
*/
public static String getValueByName(String name){
if (oConvertUtils.isEmpty(name)) {
return null;
}
for (DepartCategoryEnum val : values()) {
if (val.getName().equals(name)) {
return val.getValue();
}
}
return name;
}
}

View File

@ -8,21 +8,30 @@ import java.util.List;
/**
* 消息类型
*
* @author: jeecg-boot
*/
@EnumDict("messageType")
public enum MessageTypeEnum {
/** 系统消息 */
XT("system", "系统消息"),
/** 邮件消息 */
YJ("email", "邮件消息"),
/** 钉钉消息 */
/**
* 系统消息
*/
XT("system", "系统消息"),
/**
* 邮件消息
*/
YJ("email", "邮件消息"),
/**
* 钉钉消息
*/
DD("dingtalk", "钉钉消息"),
/** 企业微信 */
/**
* 企业微信
*/
QYWX("wechat_enterprise", "企业微信");
MessageTypeEnum(String type, String note){
MessageTypeEnum(String type, String note) {
this.type = type;
this.note = note;
}
@ -56,12 +65,13 @@ public enum MessageTypeEnum {
/**
* 获取字典数据
*
* @return
*/
public static List<DictModel> getDictList(){
public static List<DictModel> getDictList() {
List<DictModel> list = new ArrayList<>();
DictModel dictModel = null;
for(MessageTypeEnum e: MessageTypeEnum.values()){
for (MessageTypeEnum e : MessageTypeEnum.values()) {
dictModel = new DictModel();
dictModel.setValue(e.getType());
dictModel.setText(e.getNote());

View File

@ -14,7 +14,16 @@ public enum NoticeTypeEnum {
NOTICE_TYPE_PLAN("日程消息","plan"),
//暂时没用到
NOTICE_TYPE_MEETING("会议消息","meeting"),
NOTICE_TYPE_SYSTEM("系统消息","system");
NOTICE_TYPE_SYSTEM("系统消息","system"),
/**
* 协同工作
* for [JHHB-136]【vue3】协同工作系统消息需要添加一个类型
*/
NOTICE_TYPE_COLLABORATION("协同工作", "collab"),
/**
* 督办
*/
NOTICE_TYPE_SUPERVISE("督办管理", "supe");
/**
* 文件类型名称

View File

@ -0,0 +1,180 @@
package org.jeecg.common.constant.enums;
import java.util.Arrays;
import java.util.List;
/**
* 职级枚举类
*
* 注意此枚举仅适用于天津临港控股OA项目,职级的名称和等级均为写死(需要与数据库配置一致)
* @date 2025-08-26
* @author scott
*/
public enum PositionLevelEnum {
// 领导层级等级1-3
CHAIRMAN("董事长", 1, PositionType.LEADER),
GENERAL_MANAGER("总经理", 2, PositionType.LEADER),
VICE_GENERAL_MANAGER("副总经理", 3, PositionType.LEADER),
// 职员层级等级4-6
MINISTER("部长", 4, PositionType.STAFF),
VICE_MINISTER("副部长", 5, PositionType.STAFF),
STAFF("职员", 6, PositionType.STAFF);
private final String name;
private final int level;
private final PositionType type;
PositionLevelEnum(String name, int level, PositionType type) {
this.name = name;
this.level = level;
this.type = type;
}
public String getName() {
return name;
}
public int getLevel() {
return level;
}
public PositionType getType() {
return type;
}
/**
* 职级类型枚举
*/
public enum PositionType {
STAFF("职员层级"),
LEADER("领导层级");
private final String desc;
PositionType(String desc) {
this.desc = desc;
}
public String getDesc() {
return desc;
}
}
/**
* 根据职级名称获取枚举
* @param name 职级名称
* @return 职级枚举
*/
public static PositionLevelEnum getByName(String name) {
for (PositionLevelEnum position : values()) {
if (position.getName().equals(name)) {
return position;
}
}
return null;
}
/**
* 根据职级等级获取枚举
* @param level 职级等级
* @return 职级枚举
*/
public static PositionLevelEnum getByLevel(int level) {
for (PositionLevelEnum position : values()) {
if (position.getLevel() == level) {
return position;
}
}
return null;
}
/**
* 根据职级名称判断是否为职员层级
* @param name 职级名称
* @return true-职员层级false-非职员层级
*/
public static boolean isStaffLevel(String name) {
PositionLevelEnum position = getByName(name);
return position != null && position.getType() == PositionType.STAFF;
}
/**
* 根据职级名称判断是否为领导层级
* @param name 职级名称
* @return true-领导层级false-非领导层级
*/
public static boolean isLeaderLevel(String name) {
PositionLevelEnum position = getByName(name);
return position != null && position.getType() == PositionType.LEADER;
}
/**
* 比较两个职级的等级高低
* @param name1 职级名称1
* @param name2 职级名称2
* @return 正数表示name1等级更高负数表示name2等级更高0表示等级相同
*/
public static int compareLevel(String name1, String name2) {
PositionLevelEnum pos1 = getByName(name1);
PositionLevelEnum pos2 = getByName(name2);
if (pos1 == null || pos2 == null) {
return 0;
}
// 等级数字越小代表职级越高
return pos2.getLevel() - pos1.getLevel();
}
/**
* 判断是否为更高等级
* @param currentName 当前职级名称
* @param targetName 目标职级名称
* @return true-目标职级更高false-目标职级不高于当前职级
*/
public static boolean isHigherLevel(String currentName, String targetName) {
return compareLevel(targetName, currentName) > 0;
}
/**
* 获取所有职员层级名称
* @return 职员层级名称列表
*/
public static List<String> getStaffLevelNames() {
return Arrays.asList(MINISTER.getName(), VICE_MINISTER.getName(), STAFF.getName());
}
/**
* 获取所有领导层级名称
* @return 领导层级名称列表
*/
public static List<String> getLeaderLevelNames() {
return Arrays.asList(CHAIRMAN.getName(), GENERAL_MANAGER.getName(), VICE_GENERAL_MANAGER.getName());
}
/**
* 获取所有职级名称(按等级排序)
* @return 所有职级名称列表
*/
public static List<String> getAllPositionNames() {
return Arrays.asList(
CHAIRMAN.getName(), GENERAL_MANAGER.getName(), VICE_GENERAL_MANAGER.getName(),
MINISTER.getName(), VICE_MINISTER.getName(), STAFF.getName()
);
}
/**
* 获取指定等级范围的职级
* @param minLevel 最小等级
* @param maxLevel 最大等级
* @return 职级名称列表
*/
public static List<String> getPositionsByLevelRange(int minLevel, int maxLevel) {
return Arrays.stream(values())
.filter(p -> p.getLevel() >= minLevel && p.getLevel() <= maxLevel)
.map(PositionLevelEnum::getName)
.collect(java.util.stream.Collectors.toList());
}
}

View File

@ -23,7 +23,25 @@ public enum SysAnnmentTypeEnum {
/**
* 邀请用户跳转到个人设置
*/
TENANT_INVITE("tenant_invite", "url", "/system/usersetting");
TENANT_INVITE("tenant_invite", "url", "/system/usersetting"),
/**
* 协同工作-待办通知
* for [JHHB-136]【vue3】协同工作系统消息需要添加一个类型
*/
EOA_CO_NOTIFY("eoa_co_notify", "url", "/collaboration/pending"),
/**
* 协同工作-催办通知
* for [JHHB-136]【vue3】协同工作系统消息需要添加一个类型
*/
EOA_CO_REMIND("eoa_co_remind", "url", "/collaboration/pending"),
/**
* 督办管理-催办
*/
EOA_SUP_REMIND("eoa_sup_remind", "url", "/superivse/list"),
/**
* 督办管理-通知
*/
EOA_SUP_NOTIFY("eoa_sup_notify", "url", "/superivse/list");
/**
* 业务类型(email:邮件 bpm:流程)

View File

@ -3,7 +3,6 @@ package org.jeecg.common.exception;
import cn.hutool.core.util.ObjectUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import io.undertow.server.RequestTooBigException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.shiro.SecurityUtils;
@ -180,7 +179,7 @@ public class JeecgBootExceptionHandler {
@ExceptionHandler(MultipartException.class)
public Result<?> handleMaxUploadSizeExceededException(MultipartException e) {
Throwable cause = e.getCause();
if (cause instanceof IllegalStateException && cause.getCause() instanceof RequestTooBigException) {
if (cause instanceof IllegalStateException) {
log.error("文件大小超出限制: {}", cause.getMessage(), e);
addSysLog(e);
return Result.error("文件大小超出限制, 请压缩或降低文件质量!");
@ -291,7 +290,7 @@ public class JeecgBootExceptionHandler {
boolean isTooBigException = false;
if(e instanceof MultipartException){
Throwable cause = e.getCause();
if (cause instanceof IllegalStateException && cause.getCause() instanceof RequestTooBigException){
if (cause instanceof IllegalStateException){
isTooBigException = true;
}
}

View File

@ -4,6 +4,11 @@ import java.lang.annotation.*;
/**
* 将枚举类转化成字典数据
*
* <<使用说明>>
* 1. 枚举类需以 `Enum` 结尾,并且在类上添加 `@EnumDict` 注解。
* 2. 需要手动将枚举类所在包路径** 添加到 `org.jeecg.common.system.util.ResourceUtil.BASE_SCAN_PACKAGES` 配置数组中。
*
* @Author taoYan
* @Date 2022/7/8 10:34
**/

View File

@ -18,6 +18,7 @@ import org.jeecgframework.poi.excel.entity.ExportParams;
import org.jeecgframework.poi.excel.entity.ImportParams;
import org.jeecgframework.poi.excel.entity.enmus.ExcelType;
import org.jeecgframework.poi.excel.view.JeecgEntityExcelView;
import org.jeecgframework.poi.handler.inter.IExcelExportServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
@ -127,6 +128,53 @@ public class JeecgController<T, S extends IService<T>> {
return mv;
}
/**
* 大数据导出
* @param request
* @param object
* @param clazz
* @param title
* @param pageSize 每次查询的数据量
* @return
* @author chenrui
* @date 2025/8/11 16:11
*/
protected ModelAndView exportXlsForBigData(HttpServletRequest request, T object, Class<T> clazz, String title,Integer pageSize) {
// 组装查询条件
QueryWrapper<T> queryWrapper = QueryGenerator.initQueryWrapper(object, request.getParameterMap());
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
// 计算分页数
double total = service.count();
int count = (int) Math.ceil(total / pageSize);
// 过滤选中数据
String selections = request.getParameter("selections");
if (oConvertUtils.isNotEmpty(selections)) {
List<String> selectionList = Arrays.asList(selections.split(","));
queryWrapper.in("id", selectionList);
}
// 定义IExcelExportServer
IExcelExportServer excelExportServer = (queryParams, pageNum) -> {
if (pageNum > count) {
return null;
}
Page<T> page = new Page<T>(pageNum, pageSize);
IPage<T> pageList = service.page(page, (QueryWrapper<T>) queryParams);
return new ArrayList<>(pageList.getRecords());
};
// AutoPoi 导出Excel
ModelAndView mv = new ModelAndView(new JeecgEntityExcelView());
//此处设置的filename无效 ,前端会重更新设置一下
mv.addObject(NormalExcelConstants.FILE_NAME, title);
mv.addObject(NormalExcelConstants.CLASS, clazz);
ExportParams exportParams = new ExportParams(title + "报表", "导出人:" + sysUser.getRealname(), title, jeecgBaseConfig.getPath().getUpload());
mv.addObject(NormalExcelConstants.PARAMS, exportParams);
mv.addObject(NormalExcelConstants.EXPORT_SERVER, excelExportServer);
mv.addObject(NormalExcelConstants.QUERY_PARAMS, queryWrapper);
return mv;
}
/**
* 根据权限导出excel传入导出字段参数

View File

@ -414,9 +414,11 @@ public class QueryGenerator {
}
// update-begin-author:sunjianlei date:20220119 for: 【JTC-573】 过滤空条件查询,防止 sql 拼接多余的 and
List<QueryCondition> filterConditions = conditions.stream().filter(
rule -> oConvertUtils.isNotEmpty(rule.getField())
&& oConvertUtils.isNotEmpty(rule.getRule())
&& oConvertUtils.isNotEmpty(rule.getVal())
rule -> (oConvertUtils.isNotEmpty(rule.getField())
&& oConvertUtils.isNotEmpty(rule.getRule())
&& oConvertUtils.isNotEmpty(rule.getVal())
)
|| "empty".equals(rule.getRule())
).collect(Collectors.toList());
if (filterConditions.size() == 0) {
return;
@ -427,9 +429,12 @@ public class QueryGenerator {
queryWrapper.and(andWrapper -> {
for (int i = 0; i < filterConditions.size(); i++) {
QueryCondition rule = filterConditions.get(i);
if (oConvertUtils.isNotEmpty(rule.getField())
&& oConvertUtils.isNotEmpty(rule.getRule())
&& oConvertUtils.isNotEmpty(rule.getVal())) {
if (
(
oConvertUtils.isNotEmpty(rule.getField()) && oConvertUtils.isNotEmpty(rule.getRule()) && oConvertUtils.isNotEmpty(rule.getVal())
)
|| "empty".equals(rule.getRule())
) {
log.debug("SuperQuery ==> " + rule.toString());
@ -716,7 +721,11 @@ public class QueryGenerator {
* @param value 查询条件值
*/
public static void addEasyQuery(QueryWrapper<?> queryWrapper, String name, QueryRuleEnum rule, Object value) {
if (name==null || value == null || rule == null || oConvertUtils.isEmpty(value)) {
if (
(
name==null || value == null || rule == null || oConvertUtils.isEmpty(value)
)
&& !QueryRuleEnum.EMPTY.equals(rule)) {
return;
}
name = oConvertUtils.camelToUnderline(name);
@ -728,6 +737,9 @@ public class QueryGenerator {
case GE:
queryWrapper.ge(name, value);
break;
case EMPTY:
queryWrapper.isNull(name);
break;
case LT:
queryWrapper.lt(name, value);
break;

View File

@ -51,24 +51,24 @@ public class JwtUtil {
* @param code
* @param errorMsg
*/
public static void responseError(ServletResponse response, Integer code, String errorMsg) {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
// issues/I4YH95浏览器显示乱码问题
httpServletResponse.setHeader("Content-type", "text/html;charset=UTF-8");
Result jsonResult = new Result(code, errorMsg);
jsonResult.setSuccess(false);
OutputStream os = null;
try {
os = httpServletResponse.getOutputStream();
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.setStatus(code);
os.write(new ObjectMapper().writeValueAsString(jsonResult).getBytes("UTF-8"));
os.flush();
os.close();
} catch (IOException e) {
public static void responseError(HttpServletResponse response, Integer code, String errorMsg) {
try {
Result jsonResult = new Result(code, errorMsg);
jsonResult.setSuccess(false);
// 设置响应头和内容类型
response.setStatus(code);
response.setHeader("Content-type", "text/html;charset=UTF-8");
response.setContentType("application/json;charset=UTF-8");
// 使用 ObjectMapper 序列化为 JSON 字符串
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(jsonResult);
response.getWriter().write(json);
response.getWriter().flush();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
}
}
/**
* 校验token是否正确
@ -101,7 +101,7 @@ public class JwtUtil {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("username").asString();
} catch (JWTDecodeException e) {
log.warn(e.getMessage(), e);
log.error(e.getMessage(), e);
return null;
}
}

View File

@ -13,31 +13,33 @@ import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.lang.reflect.Method;
import java.util.*;
/**
* 资源加载工具类
* 枚举字典数据 资源加载工具类
*
* @Author taoYan
* @Date 2022/7/8 10:40
**/
@Slf4j
public class ResourceUtil {
/**
* 多个包扫描根路径
*
* 之所以让用户手工配置扫描路径,是为了避免不必要的类加载开销,提升启动性能。
* 请务必将所有枚举类所在包路径添加到此配置中。
*/
private final static String[] BASE_SCAN_PACKAGES = {
"org.jeecg.common.constant.enums",
"org.jeecg.modules.message.enums"
};
/**
* 枚举字典数据
*/
private final static Map<String, List<DictModel>> enumDictData = new HashMap<>(5);
/**
* 所有java类
*/
private final static String CLASS_PATTERN="/**/*.class";
/**
* 所有枚举java类
*/
@ -45,9 +47,9 @@ public class ResourceUtil {
private final static String CLASS_ENUM_PATTERN="/**/*Enum.class";
/**
* 包路径 org.jeecg
* 初始化状态标识
*/
private final static String BASE_PACKAGE = "org.jeecg";
private static volatile boolean initialized = false;
/**
* 枚举类中获取字典数据的方法名
@ -55,59 +57,135 @@ public class ResourceUtil {
private final static String METHOD_NAME = "getDictList";
/**
* 获取枚举字典数据
* 获取枚举类对应的字典数据 SysDictServiceImpl#queryAllDictItems()
* @return
*
* @return 枚举字典数据
*/
public static Map<String, List<DictModel>> getEnumDictData(){
if(enumDictData.keySet().size()>0){
return enumDictData;
}
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(BASE_PACKAGE) + CLASS_ENUM_PATTERN;
try {
Resource[] resources = resourcePatternResolver.getResources(pattern);
MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
for (Resource resource : resources) {
MetadataReader reader = readerFactory.getMetadataReader(resource);
String classname = reader.getClassMetadata().getClassName();
Class<?> clazz = Class.forName(classname);
EnumDict enumDict = clazz.getAnnotation(EnumDict.class);
if (enumDict != null) {
EnumDict annotation = clazz.getAnnotation(EnumDict.class);
String key = annotation.value();
if(oConvertUtils.isNotEmpty(key)){
List<DictModel> list = (List<DictModel>) clazz.getDeclaredMethod(METHOD_NAME).invoke(null);
enumDictData.put(key, list);
}
public static Map<String, List<DictModel>> getEnumDictData() {
if (!initialized) {
synchronized (ResourceUtil.class) {
if (!initialized) {
long startTime = System.currentTimeMillis();
log.info("【枚举字典加载】开始初始化枚举字典数据...");
initEnumDictData();
initialized = true;
long endTime = System.currentTimeMillis();
log.info("【枚举字典加载】枚举字典数据初始化完成,共加载 {} 个字典,总耗时: {}ms", enumDictData.size(), endTime - startTime);
}
}
}catch (Exception e){
log.error("获取枚举类字典数据异常", e.getMessage());
// e.printStackTrace();
}
return enumDictData;
}
/**
* 用于后端字典翻译 SysDictServiceImpl#queryManyDictByKeys(java.util.List, java.util.List)
* @param dictCodeList
* @param keys
* @return
* 使用多包路径扫描方式初始化枚举字典数据
*/
public static Map<String, List<DictModel>> queryManyDictByKeys(List<String> dictCodeList, List<String> keys){
if(enumDictData.keySet().size()==0){
getEnumDictData();
private static void initEnumDictData() {
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
long scanStartTime = System.currentTimeMillis();
List<Resource> allResources = new ArrayList<>();
// 扫描多个包路径
for (String basePackage : BASE_SCAN_PACKAGES) {
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(basePackage) + CLASS_ENUM_PATTERN;
try {
Resource[] resources = resourcePatternResolver.getResources(pattern);
allResources.addAll(Arrays.asList(resources));
log.debug("【枚举字典加载】扫描包 {} 找到 {} 个枚举类文件", basePackage, resources.length);
} catch (Exception e) {
log.warn("【枚举字典加载】扫描包 {} 时出现异常: {}", basePackage, e.getMessage());
}
}
long scanEndTime = System.currentTimeMillis();
log.info("【枚举字典加载】文件扫描完成,总共找到 {} 个枚举类文件,扫描耗时: {}ms", allResources.size(), scanEndTime - scanStartTime);
MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
long processStartTime = System.currentTimeMillis();
int processedCount = 0;
for (Resource resource : allResources) {
try {
MetadataReader reader = readerFactory.getMetadataReader(resource);
String classname = reader.getClassMetadata().getClassName();
// 提前检查是否有@EnumDict注解避免不必要的Class.forName
if (hasEnumDictAnnotation(reader)) {
processEnumClass(classname);
processedCount++;
}
} catch (Exception e) {
log.debug("处理资源异常: {} - {}", resource.getFilename(), e.getMessage());
}
}
long processEndTime = System.currentTimeMillis();
log.info("【枚举字典加载】处理完成,实际处理 {} 个带注解的枚举类,处理耗时: {}ms", processedCount, processEndTime - processStartTime);
}
/**
* 检查类是否有EnumDict注解通过元数据避免类加载
*/
private static boolean hasEnumDictAnnotation(MetadataReader reader) {
try {
return reader.getAnnotationMetadata().hasAnnotation(EnumDict.class.getName());
} catch (Exception e) {
return false;
}
}
/**
* 处理单个枚举类
*/
private static void processEnumClass(String classname) {
try {
Class<?> clazz = Class.forName(classname);
EnumDict enumDict = clazz.getAnnotation(EnumDict.class);
if (enumDict != null) {
String key = enumDict.value();
if (oConvertUtils.isNotEmpty(key)) {
Method method = clazz.getDeclaredMethod(METHOD_NAME);
List<DictModel> list = (List<DictModel>) method.invoke(null);
enumDictData.put(key, list);
log.debug("成功加载枚举字典: {} -> {}", key, classname);
}
}
} catch (Exception e) {
log.debug("处理枚举类异常: {} - {}", classname, e.getMessage());
}
}
/**
* 用于后端字典翻译 SysDictServiceImpl#queryManyDictByKeys(java.util.List, java.util.List)
*
* @param dictCodeList 字典编码列表
* @param keys 键值列表
* @return 字典数据映射
*/
public static Map<String, List<DictModel>> queryManyDictByKeys(List<String> dictCodeList, List<String> keys) {
Map<String, List<DictModel>> enumDict = getEnumDictData();
Map<String, List<DictModel>> map = new HashMap<>();
for (String code : enumDictData.keySet()) {
if(dictCodeList.indexOf(code)>=0){
List<DictModel> dictItemList = enumDictData.get(code);
for(DictModel dm: dictItemList){
// 使用更高效的查找方式
Set<String> dictCodeSet = new HashSet<>(dictCodeList);
Set<String> keySet = new HashSet<>(keys);
for (String code : enumDict.keySet()) {
if (dictCodeSet.contains(code)) {
List<DictModel> dictItemList = enumDict.get(code);
for (DictModel dm : dictItemList) {
String value = dm.getValue();
if(keys.indexOf(value)>=0){
if (keySet.contains(value)) {
List<DictModel> list = new ArrayList<>();
list.add(new DictModel(value, dm.getText()));
map.put(code,list);
map.put(code, list);
break;
}
}
@ -115,22 +193,5 @@ public class ResourceUtil {
}
return map;
}
/**
* 获取实现类
*
* @param classPath
*/
public static Object getImplementationClass(String classPath){
try {
Class<?> aClass = Class.forName(classPath);
return SpringContextUtils.getBean(aClass);
} catch (ClassNotFoundException e) {
log.error("类没有找到",e);
return null;
} catch (NoSuchBeanDefinitionException e){
log.error(classPath + "没有实现",e);
return null;
}
}
}
}

View File

@ -144,4 +144,8 @@ public class LoginUser {
/**设备id uniapp推送用*/
private String clientId;
/**
* 主岗位
*/
private String mainDepPostId;
}

View File

@ -25,6 +25,7 @@ import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
@ -163,6 +164,10 @@ public class CommonUtils {
}
// 获取文件名
String orgName = mf.getOriginalFilename();
// 无中文情况下进行转码
if (orgName != null && !CommonUtils.ifContainChinese(orgName)) {
orgName = new String(orgName.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);
}
orgName = CommonUtils.getFileName(orgName);
if(orgName.indexOf(SymbolConstant.SPOT)!=-1){
fileName = orgName.substring(0, orgName.lastIndexOf(".")) + "_" + System.currentTimeMillis() + orgName.substring(orgName.lastIndexOf("."));
@ -242,6 +247,10 @@ public class CommonUtils {
try {
DataSource dataSource = SpringContextUtils.getApplicationContext().getBean(DataSource.class);
dbTypeEnum = JdbcUtils.getDbType(dataSource.getConnection().getMetaData().getURL());
//【采用SQL_SERVER2005引擎】QQYUN-13298 解决升级mybatisPlus后SqlServer分页使用OFFSET无排序字段报错问题
if (dbTypeEnum == DbType.SQL_SERVER) {
dbTypeEnum = DbType.SQL_SERVER2005;
}
return dbTypeEnum;
} catch (SQLException e) {
log.warn(e.getMessage(), e);

View File

@ -13,6 +13,8 @@ import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
@ -814,4 +816,44 @@ public class DateUtils extends PropertyEditorSupport {
return calendar1.get(Calendar.YEAR) == calendar2.get(Calendar.YEAR);
}
/**
* 获取两个日期之间的所有日期列表,包含开始和结束日期
*
* @param begin
* @param end
* @return
*/
public static List<Date> getDateRangeList(Date begin, Date end) {
List<Date> dateList = new ArrayList<>();
if (begin == null || end == null) {
return dateList;
}
// 清除时间部分,只比较日期
Calendar beginCal = Calendar.getInstance();
beginCal.setTime(begin);
beginCal.set(Calendar.HOUR_OF_DAY, 0);
beginCal.set(Calendar.MINUTE, 0);
beginCal.set(Calendar.SECOND, 0);
beginCal.set(Calendar.MILLISECOND, 0);
Calendar endCal = Calendar.getInstance();
endCal.setTime(end);
endCal.set(Calendar.HOUR_OF_DAY, 0);
endCal.set(Calendar.MINUTE, 0);
endCal.set(Calendar.SECOND, 0);
endCal.set(Calendar.MILLISECOND, 0);
if (endCal.before(beginCal)) {
return dateList;
}
dateList.add(beginCal.getTime());
while (beginCal.before(endCal)) {
beginCal.add(Calendar.DAY_OF_YEAR, 1);
dateList.add(beginCal.getTime());
}
return dateList;
}
}

View File

@ -1,14 +1,22 @@
package org.jeecg.common.util;
import jakarta.servlet.http.HttpServletResponse;
import cn.hutool.core.io.IoUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.exception.JeecgBootException;
import org.jeecg.common.util.filter.SsrfFileTypeFilter;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.List;
import java.util.zip.ZipEntry;
@ -203,4 +211,150 @@ public class FileDownloadUtils {
dir.mkdirs();
}
}
/**
* 下载单个文件到ZIP流
* 核心功能获取文件流写入ZIP条目
* @param fileUrl 文件URL可以是HTTP URL或本地路径
* @param fileName ZIP内的文件名
* @param zous ZIP输出流
*/
public static void downLoadSingleFile(String fileUrl, String fileName, String uploadUrl,ZipArchiveOutputStream zous) {
InputStream inputStream = null;
try {
// 创建ZIP条目每个文件在ZIP中都是一个独立条目
ZipArchiveEntry entry = new ZipArchiveEntry(fileName);
zous.putArchiveEntry(entry);
// 获取文件输入流:区分普通文件和快捷方式
if (fileUrl.endsWith(".url")) {
// 处理快捷方式:生成.url文件内容
inputStream = FileDownloadUtils.createInternetShortcut(fileName, fileUrl, "");
} else {
// 普通文件下载从URL或本地路径获取流
inputStream = getDownInputStream(fileUrl,uploadUrl);
}
if (inputStream != null) {
// 将文件流写入ZIP
IOUtils.copy(inputStream, zous);
}
// 关闭当前ZIP条目
zous.closeArchiveEntry();
} catch (IOException e) {
log.error("文件下载失败: {}", e);
} finally {
// 确保输入流关闭
IoUtil.close(inputStream);
}
}
/**
* 获取下载文件输入流
* 功能根据URL类型HTTP或本地获取文件流
* @param fileUrl 文件URL支持HTTP和本地路径
* @return 文件输入流失败返回null
*/
public static InputStream getDownInputStream(String fileUrl, String uploadUrl) {
try {
// 处理HTTP URL通过网络下载
if (oConvertUtils.isNotEmpty(fileUrl) && fileUrl.startsWith(CommonConstant.STR_HTTP)) {
URL url = new URL(fileUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(5000); // 连接超时5秒
connection.setReadTimeout(30000); // 读取超时30秒
return connection.getInputStream();
} else {
// 处理本地文件:直接读取文件系统
String downloadFilePath = uploadUrl + File.separator + fileUrl;
// 安全检查:防止下载危险文件类型
SsrfFileTypeFilter.checkDownloadFileType(downloadFilePath);
return new BufferedInputStream(new FileInputStream(downloadFilePath));
}
} catch (IOException e) {
// 异常时返回null上层会处理空流情况
return null;
}
}
/**
* 获取文件扩展名
* 功能:从文件名中提取扩展名
* @param fileName 文件名
* @return 文件扩展名(不含点),如"txt"、"png"
*/
public static String getFileExtension(String fileName) {
int dotIndex = fileName.lastIndexOf('.');
return (dotIndex == -1) ? "" : fileName.substring(dotIndex + 1);
}
/**
* 创建快捷方式(.url文件内容
* 功能生成Internet快捷方式文件内容
* @param name 快捷方式名称
* @param url 目标URL地址
* @param icon 图标路径(可选)
* @return 包含.url文件内容的输入流
*/
public static InputStream createInternetShortcut(String name, String url, String icon) {
StringWriter sw = new StringWriter();
try {
// 按照Windows快捷方式格式写入内容
sw.write("[InternetShortcut]\n");
sw.write("URL=" + url + "\n");
if (oConvertUtils.isNotEmpty(icon)) {
sw.write("IconFile=" + icon + "\n");
}
// 将字符串内容转换为输入流
return new ByteArrayInputStream(sw.toString().getBytes(StandardCharsets.UTF_8));
} finally {
IoUtil.close(sw);
}
}
/**
* 从URL中提取文件名
* 功能从HTTP URL或本地路径中提取纯文件名
* @param fileUrl 文件URL
* @return 文件名(不含路径)
*/
public static String getFileNameFromUrl(String fileUrl) {
try {
// 处理HTTP URL从路径部分提取文件名
if (fileUrl.startsWith(CommonConstant.STR_HTTP)) {
URL url = new URL(fileUrl);
String path = url.getPath();
return path.substring(path.lastIndexOf('/') + 1);
}
// 处理本地文件路径:从文件路径提取文件名
return fileUrl.substring(fileUrl.lastIndexOf(File.separator) + 1);
} catch (Exception e) {
// 如果解析失败,使用时间戳作为文件名
return "file_" + System.currentTimeMillis();
}
}
/**
* 生成ZIP中的文件名
* 功能:避免文件名冲突,为多个文件添加序号
* @param fileUrl 文件URL用于提取原始文件名
* @param index 文件序号从0开始
* @param total 文件总数
* @return 处理后的文件名(带序号)
*/
public static String generateFileName(String fileUrl, int index, int total) {
// 从URL中提取原始文件名
String originalFileName = getFileNameFromUrl(fileUrl);
// 如果只有一个文件,直接使用原始文件名
if (total == 1) {
return originalFileName;
}
// 多个文件时,使用序号+原始文件名
String extension = getFileExtension(originalFileName);
String nameWithoutExtension = originalFileName.replace("." + extension, "");
return String.format("%s_%d.%s", nameWithoutExtension, index + 1, extension);
}
}

View File

@ -2,6 +2,7 @@ package org.jeecg.common.util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import java.io.BufferedWriter;
@ -16,6 +17,7 @@ import java.util.List;
*/
@Slf4j
@Component
@Lazy(false)
public class PmsUtil {

View File

@ -221,6 +221,63 @@ public class RestUtil {
return RT.exchange(url, method, request, responseType);
}
/**
* 发送请求(支持自定义超时时间)
*
* @param url 请求地址
* @param method 请求方式
* @param headers 请求头 可空
* @param variables 请求url参数 可空
* @param params 请求body参数 可空
* @param responseType 返回类型
* @param timeout 超时时间毫秒如果为0或负数则使用默认超时
* @return ResponseEntity<responseType>
*/
public static <T> ResponseEntity<T> request(String url, HttpMethod method, HttpHeaders headers,
JSONObject variables, Object params, Class<T> responseType, int timeout) {
log.info(" RestUtil --- request --- url = "+ url + ", timeout = " + timeout);
if (StringUtils.isEmpty(url)) {
throw new RuntimeException("url 不能为空");
}
if (method == null) {
throw new RuntimeException("method 不能为空");
}
if (headers == null) {
headers = new HttpHeaders();
}
// 创建自定义RestTemplate如果需要设置超时
RestTemplate restTemplate = RT;
if (timeout > 0) {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(timeout);
requestFactory.setReadTimeout(timeout);
restTemplate = new RestTemplate(requestFactory);
// 解决乱码问题
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
}
// 请求体
String body = "";
if (params != null) {
if (params instanceof JSONObject) {
body = ((JSONObject) params).toJSONString();
} else {
body = params.toString();
}
}
// 拼接 url 参数
if (variables != null && !variables.isEmpty()) {
url += ("?" + asUrlVariables(variables));
}
// 发送请求
HttpEntity<String> request = new HttpEntity<>(body, headers);
return restTemplate.exchange(url, method, request, responseType);
}
/**
* 获取JSON请求头
*/

View File

@ -0,0 +1,37 @@
package org.jeecg.common.util;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.ThreadContext;
import java.util.concurrent.*;
/**
* @date 2025-09-04
* @author scott
*
* @Description: 支持shiro的API获取当前登录人方法的线程池
*/
public class ShiroThreadPoolExecutor extends ThreadPoolExecutor {
public ShiroThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
@Override
public void execute(Runnable command) {
Subject subject = SecurityUtils.getSubject();
SecurityManager securityManager = SecurityUtils.getSecurityManager();
super.execute(() -> {
try {
ThreadContext.bind(securityManager);
ThreadContext.bind(subject);
command.run();
} finally {
ThreadContext.unbindSubject();
ThreadContext.unbindSecurityManager();
}
});
}
}

View File

@ -65,6 +65,10 @@ public class TokenUtils {
if (tenantId == null) {
tenantId = oConvertUtils.getString(request.getHeader(CommonConstant.TENANT_ID));
}
if (oConvertUtils.isNotEmpty(tenantId) && "undefined".equals(tenantId)) {
return null;
}
return tenantId;
}

View File

@ -474,6 +474,23 @@ public class oConvertUtils {
return true;
}
/**
* 判断字符串是否为JSON格式
* @param str
* @return
*/
public static boolean isJson(String str) {
if (str == null || str.trim().isEmpty()) {
return false;
}
try {
com.alibaba.fastjson.JSON.parse(str);
return true;
} catch (Exception e) {
return false;
}
}
/**
* 获取Map对象
*/
@ -1132,7 +1149,15 @@ public class oConvertUtils {
* @date 2020/9/12 15:50
*/
public static <T> boolean isIn(T obj, T... objs) {
return isIn(obj, objs);
if (isEmpty(objs)) {
return false;
}
for (T obj1 : objs) {
if (isEqual(obj, obj1)) {
return true;
}
}
return false;
}
/**

View File

@ -3,13 +3,14 @@ package org.jeecg.config;
import org.jeecgframework.core.util.ApplicationContextUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
/**
* @Author: Scott
* @Date: 2018/2/7
* @description: autopoi 配置类
*/
@Lazy(false)
@Configuration
public class AutoPoiConfig {

View File

@ -25,6 +25,7 @@ import lombok.extern.slf4j.Slf4j;
* @Version:1.0
*/
@Slf4j
@Lazy(false)
@Service
public class AutoPoiDictConfig implements AutoPoiDictServiceI {
final static String EXCEL_SPLIT_TAG = "_";

View File

@ -75,11 +75,6 @@ public class JeecgBaseConfig {
* 百度开放API配置
*/
private BaiduApi baiduApi;
/**
* 高德开放API配置
*/
private GaoDeApi gaoDeApi;
public String getCustomResourcePrefixPath() {
return customResourcePrefixPath;
@ -177,11 +172,4 @@ public class JeecgBaseConfig {
this.baiduApi = baiduApi;
}
public GaoDeApi getGaoDeApi() {
return gaoDeApi;
}
public void setGaoDeApi(GaoDeApi gaoDeApi) {
this.gaoDeApi = gaoDeApi;
}
}

View File

@ -0,0 +1,29 @@
package org.jeecg.config;
import org.jeecg.config.vo.GaoDeApi;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
/**
* 高德账号配置
*/
@Lazy(false)
@Configuration("jeecgGaodeBaseConfig")
@ConfigurationProperties(prefix = "jeecg.jmreport")
public class JeecgGaodeBaseConfig {
/**
* 高德开放API配置
*/
private GaoDeApi gaoDeApi;
public GaoDeApi getGaoDeApi() {
return gaoDeApi;
}
public void setGaoDeApi(GaoDeApi gaoDeApi) {
this.gaoDeApi = gaoDeApi;
}
}

View File

@ -2,12 +2,14 @@ package org.jeecg.config;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
/**
* 设置静态参数初始化
* @author: jeecg-boot
*/
@Lazy(false)
@Component
@Data
public class StaticConfig {

View File

@ -117,7 +117,7 @@ public class Swagger3Config implements WebMvcConfigurer {
return new OpenAPI()
.info(new Info()
.title("JeecgBoot 后台服务API接口文档")
.version("3.8.2")
.version("3.8.3")
.contact(new Contact().name("北京国炬信息技术有限公司").url("www.jeccg.com").email("jeecgos@163.com"))
.description("后台API接口")
.termsOfService("NO terms of service")

View File

@ -1,19 +1,19 @@
package org.jeecg.config;
import io.undertow.server.DefaultByteBufferPool;
import io.undertow.websockets.jsr.WebSocketDeploymentInfo;
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.stereotype.Component;
@Component
public class UndertowCustomizer implements WebServerFactoryCustomizer<UndertowServletWebServerFactory> {
@Override
public void customize(UndertowServletWebServerFactory factory) {
factory.addDeploymentInfoCustomizers(deploymentInfo -> {
WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo();
webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(false, 1024));
deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo);
});
}
}
//package org.jeecg.config;
//
//import io.undertow.server.DefaultByteBufferPool;
//import io.undertow.websockets.jsr.WebSocketDeploymentInfo;
//import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory;
//import org.springframework.boot.web.server.WebServerFactoryCustomizer;
//import org.springframework.stereotype.Component;
//
//@Component
//public class UndertowCustomizer implements WebServerFactoryCustomizer<UndertowServletWebServerFactory> {
// @Override
// public void customize(UndertowServletWebServerFactory factory) {
// factory.addDeploymentInfoCustomizers(deploymentInfo -> {
// WebSocketDeploymentInfo webSocketDeploymentInfo = new WebSocketDeploymentInfo();
// webSocketDeploymentInfo.setBuffers(new DefaultByteBufferPool(false, 1024));
// deploymentInfo.addServletContextAttribute("io.undertow.websockets.jsr.WebSocketDeploymentInfo", webSocketDeploymentInfo);
// });
// }
//}

View File

@ -9,11 +9,13 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
/**
* Minio文件上传配置文件
* @author: jeecg-boot
*/
@Lazy(false)
@Slf4j
@Configuration
@ConditionalOnProperty(prefix = "jeecg.minio", name = "minio_url")

View File

@ -6,11 +6,13 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
/**
* 云存储 配置
* @author: jeecg-boot
*/
@Lazy(false)
@Configuration
@ConditionalOnProperty(prefix = "jeecg.oss", name = "endpoint")
public class OssConfiguration {

View File

@ -126,6 +126,7 @@ public class ShiroConfig {
filterChainDefinitionMap.put("/**/*.ttf", "anon");
filterChainDefinitionMap.put("/**/*.woff", "anon");
filterChainDefinitionMap.put("/**/*.woff2", "anon");
filterChainDefinitionMap.put("/**/*.glb", "anon");
filterChainDefinitionMap.put("/**/*.wasm", "anon");
//update-end--Author:scott Date:20221116 for排除静态资源后缀
@ -177,7 +178,9 @@ public class ShiroConfig {
filterChainDefinitionMap.put("/sys/version/app3version", "anon");
//仪表盘(按钮通信)
filterChainDefinitionMap.put("/dragChannelSocket/**","anon");
//App vue3版本查询版本接口
filterChainDefinitionMap.put("/sys/version/app3version", "anon");
//性能监控——安全隐患泄露TOEKNdurid连接池也有
//filterChainDefinitionMap.put("/actuator/**", "anon");
//测试模块排除
@ -187,7 +190,7 @@ public class ShiroConfig {
filterChainDefinitionMap.put("/error", "anon");
// 企业微信证书排除
filterChainDefinitionMap.put("/WW_verify*", "anon");
// 添加自己的过滤器并且取名为jwt
Map<String, Filter> filterMap = new HashMap<String, Filter>(1);
//如果cloudServer为空 则说明是单体 需要加载跨域配置【微服务跨域切换】
@ -228,6 +231,7 @@ public class ShiroConfig {
registration.addUrlPatterns("/airag/chat/send");
registration.addUrlPatterns("/airag/app/debug");
registration.addUrlPatterns("/airag/app/prompt/generate");
registration.addUrlPatterns("/airag/chat/receive/**");
//支持异步
registration.setAsyncSupported(true);
registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC);

View File

@ -109,8 +109,8 @@ public class ShiroRealm extends AuthorizingRealm {
try {
loginUser = this.checkUserTokenIsEffect(token);
} catch (AuthenticationException e) {
log.error("—————校验 check token 失败——————————"+ e.getMessage(), e);
JwtUtil.responseError(SpringContextUtils.getHttpServletResponse(),401,e.getMessage());
e.printStackTrace();
return null;
}
return new SimpleAuthenticationInfo(loginUser, token, getName());
@ -125,7 +125,7 @@ public class ShiroRealm extends AuthorizingRealm {
// 解密获得username用于和数据库进行对比
String username = JwtUtil.getUsername(token);
if (username == null) {
throw new AuthenticationException("token非法无效!");
throw new AuthenticationException("Token非法无效!");
}
// 查询用户信息

View File

@ -56,7 +56,7 @@ public class JwtFilter extends BasicHttpAuthenticationFilter {
executeLogin(request, response);
return true;
} catch (Exception e) {
JwtUtil.responseError(response,401,CommonConstant.TOKEN_IS_INVALID_MSG);
JwtUtil.responseError((HttpServletResponse)response,401,CommonConstant.TOKEN_IS_INVALID_MSG);
return false;
//throw new AuthenticationException("Token失效请重新登录", e);
}