Compare commits

40 Commits

Author SHA1 Message Date
羡民Coding
c5777c01c1 !822 fix: 文案错误
* fix: 文案错误
2026-01-12 06:01:54 +00:00
疯狂的狮子Li
459e9caf14 update 优化 兼容path大写开头搜索 2026-01-12 09:21:01 +08:00
疯狂的狮子Li
0940ba6762 update 优化 大家都认可用"账"统一改为账 2026-01-12 09:17:39 +08:00
疯狂的狮子Li
d8ed23f227 update 优化 添加菜单路由地址和名称的校验规则 2026-01-09 17:10:51 +08:00
疯狂的狮子Li
948eba6566 update 优化 添加菜单路由地址和名称的校验规则 2026-01-09 13:19:21 +08:00
疯狂的狮子Li
1a14bdf256 update 优化 统一用词 2026-01-09 11:50:28 +08:00
疯狂的狮子Li
bbc684b335 update 删除已经过期的配置类 2026-01-06 17:26:45 +08:00
疯狂的狮子Li
2b8f4e1d2c update 下架过期的赞助商 2026-01-06 17:22:44 +08:00
AprilWind
d634c2a292 update 优化oss日志侦听器打印级别 2026-01-05 14:39:40 +08:00
ColorDreams
8b97e7bc53 update ip2region version to 3.3.2 2025-12-24 19:09:36 +08:00
疯狂的狮子Li
874ad7c9b7 fix 修复 判断条件写反问题 2025-12-24 13:10:47 +08:00
miracle-bean
89d6f6f247 !815 fix websocket 多线程下IO阻塞的问题
* fix websocket 多线程下IO阻塞的问题
2025-12-23 07:55:24 +00:00
疯狂的狮子Li
1324a1cb16 update 优化 增加 HandlerMethodValidationException 参数校验异常连接 2025-12-23 15:30:32 +08:00
ColorDreams
961bca462e fix 临时修复Ip2Region InputStream读取函数导致的OOM问题 2025-12-23 14:32:48 +08:00
疯狂的狮子Li
496df8494e update 优化 翻译实现类逻辑 2025-12-23 10:38:18 +08:00
疯狂的狮子Li
2f1f9689e0 🧨🧨🧨发布 5.5.2 版本 2025年最后一版 2025-12-23 09:28:20 +08:00
疯狂的狮子Li
8110413fdf update 删除错误的配置 2025-12-23 09:22:42 +08:00
疯狂的狮子Li
c1f64d3450 update anyline 8.7.2-20250603 => 8.7.3-20251210 2025-12-22 13:11:38 +08:00
疯狂的狮子Li
cb00f4c9c1 update snailjob 1.8.0 => 1.9.0 2025-12-22 09:42:56 +08:00
疯狂的狮子Li
79512c69b2 fix 修复 创建租户同步工作流数据 在没有流程定义的情况下不会复制流程类别的问题 2025-12-19 19:38:45 +08:00
疯狂的狮子Li
a5fb128f11 update springboot 3.5.8 => 3.5.9 2025-12-19 11:31:53 +08:00
dr5hx
8a04e3c88f !811 feat(excel): 增强单元格合并处理逻辑
* feat(excel): 增强单元格合并处理逻辑
2025-12-19 01:48:31 +00:00
疯狂的狮子Li
dac447b76f fix 修复 listenerVariable.getVariable() 获取null问题 2025-12-19 09:36:22 +08:00
AprilWind
35a9e4c8e8 update 增加高安全脱敏方法 2025-12-18 19:19:01 +08:00
AprilWind
0d87c12d3c update 优化灵活脱敏方法 2025-12-18 17:30:32 +08:00
AprilWind
f20a0c4342 update 优化构建流程参数 2025-12-16 18:27:21 +08:00
AprilWind
6c8d637bd2 update 优化报错信息提示 2025-12-16 17:05:18 +08:00
马铃薯头
20e9957db2 !807 update 代码生成字典类型字段新增更新验证策略
* update 代码生成字典类型字段新增更新验证策略
2025-12-16 08:36:03 +00:00
AprilWind
9baded9326 update 测试单表和测试树表增加搜索条件 2025-12-16 14:16:13 +08:00
疯狂的狮子Li
b5902debb6 update 优化 删除无用配置类代码 2025-12-15 15:44:54 +08:00
AprilWind
bcd5bb0f86 !805 update 优化我的待办时间展示
* update 优化我的待办时间展示
2025-12-15 06:19:29 +00:00
AprilWind
1a461f7d3d !804 update 优化登录提示语
* update 优化登录提示语
2025-12-15 03:10:49 +00:00
疯狂的狮子Li
e23d99d85b fix 修复 form_path 输入空字符串导致的问题 2025-12-15 09:36:12 +08:00
秋辞未寒
f07c20afab update Ip2Region version to 3.3.1,使用新的xdb文件加载函数优化xdb数据库的加载流程 2025-12-12 22:50:23 +08:00
疯狂的狮子Li
420553eaa6 fix 修复 工作流类别 顶节点父级可以被修改导致无法加载的问题 2025-12-12 17:15:35 +08:00
疯狂的狮子Li
1d8d93eaa3 fix 修复 微软三方对接参数缺失 2025-12-12 11:40:51 +08:00
疯狂的狮子Li
5f0d09fd45 update 优化 日志输出内容 2025-12-12 09:32:07 +08:00
AprilWind
1c2b7d7017 update 优化Ip2Region初始化打印日志 2025-12-12 09:25:18 +08:00
AprilWind
5fb2890167 update 增加对IPv6地址库的支持,优化Ip2Region初始化逻辑 2025-12-11 19:57:27 +08:00
秋辞未寒
1165c8dc06 !803 feat IP地址行政区域离线查询支持IPv6(已提供相关代码,开发者自行决定是否使用)
* update IP地址行政区域助手类重命名以匹配其工具类的功能定位
* feat IP地址行政区域离线查询支持IPv6(已提供相关代码,开发者自行决定是否使用)
2025-12-11 05:23:09 +00:00
62 changed files with 512 additions and 359 deletions

View File

@@ -2,7 +2,7 @@
<configuration default="false" name="ruoyi-monitor-admin" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="imageTag" value="ruoyi/ruoyi-monitor-admin:5.5.1" />
<option name="imageTag" value="ruoyi/ruoyi-monitor-admin:5.5.2" />
<option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-monitor-admin/Dockerfile" />
</settings>

View File

@@ -2,7 +2,7 @@
<configuration default="false" name="ruoyi-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="imageTag" value="ruoyi/ruoyi-server:5.5.1" />
<option name="imageTag" value="ruoyi/ruoyi-server:5.5.2" />
<option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-admin/Dockerfile" />
</settings>

View File

@@ -2,7 +2,7 @@
<configuration default="false" name="ruoyi-snailjob-server" type="docker-deploy" factoryName="dockerfile" server-name="Docker">
<deployment type="dockerfile">
<settings>
<option name="imageTag" value="ruoyi/ruoyi-snailjob-server:5.5.1" />
<option name="imageTag" value="ruoyi/ruoyi-snailjob-server:5.5.2" />
<option name="buildOnly" value="true" />
<option name="sourceFilePath" value="ruoyi-extend/ruoyi-snailjob-server/Dockerfile" />
</settings>

View File

@@ -10,7 +10,7 @@
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus/blob/5.X/LICENSE)
[![使用IntelliJ IDEA开发维护](https://img.shields.io/badge/IntelliJ%20IDEA-提供支持-blue.svg)](https://www.jetbrains.com/?from=RuoYi-Vue-Plus)
<br>
[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.5.1-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus)
[![RuoYi-Vue-Plus](https://img.shields.io/badge/RuoYi_Vue_Plus-5.5.2-success.svg)](https://gitee.com/dromara/RuoYi-Vue-Plus)
[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.4-blue.svg)]()
[![JDK-17](https://img.shields.io/badge/JDK-17-green.svg)]()
[![JDK-21](https://img.shields.io/badge/JDK-21-green.svg)]()
@@ -35,7 +35,6 @@ MaxKey 业界领先单点登录产品 - https://gitee.com/dromara/MaxKey <br>
CCFlow 驰聘低代码-流程-表单 - https://gitee.com/opencc/RuoYi-JFlow <br>
数舵科技 软件定制开发APP小程序等 - http://www.shuduokeji.com/ <br>
引迈信息 软件开发平台 - https://www.jnpfsoft.com/index.html?from=plus-doc <br>
<font color="red">**启山商城系统 多租户商城源码可免费商用可二次开发 - https://www.73app.cn/** </font><br>
Mall4J 高质量Java商城系统 - https://www.mall4j.com/cn/?statId=11 <br>
aizuda flowlong 工作流 - https://gitee.com/aizuda/flowlong <br>
Ruoyi-Plus-Uniapp - https://ruoyi.plus <br>

10
pom.xml
View File

@@ -13,8 +13,8 @@
<description>Dromara RuoYi-Vue-Plus多租户管理系统</description>
<properties>
<revision>5.5.1</revision>
<spring-boot.version>3.5.8</spring-boot.version>
<revision>5.5.2</revision>
<spring-boot.version>3.5.9</spring-boot.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>17</java.version>
@@ -31,14 +31,14 @@
<redisson.version>3.52.0</redisson.version>
<lock4j.version>2.2.7</lock4j.version>
<dynamic-ds.version>4.3.1</dynamic-ds.version>
<snailjob.version>1.8.0</snailjob.version>
<snailjob.version>1.9.0</snailjob.version>
<mapstruct-plus.version>1.5.0</mapstruct-plus.version>
<mapstruct-plus.lombok.version>0.2.0</mapstruct-plus.lombok.version>
<lombok.version>1.18.40</lombok.version>
<bouncycastle.version>1.80</bouncycastle.version>
<justauth.version>1.16.7</justauth.version>
<!-- 离线IP地址定位库 -->
<ip2region.version>2.7.0</ip2region.version>
<ip2region.version>3.3.2</ip2region.version>
<!-- OSS 配置 -->
<aws.sdk.version>2.28.22</aws.sdk.version>
<!-- SMS 配置 -->
@@ -46,7 +46,7 @@
<!-- 限制框架中的fastjson版本 -->
<fastjson.version>1.2.83</fastjson.version>
<!-- 面向运行时的D-ORM依赖 -->
<anyline.version>8.7.2-20250603</anyline.version>
<anyline.version>8.7.3-20251210</anyline.version>
<!-- 工作流配置 -->
<warm-flow.version>1.8.4</warm-flow.version>

View File

@@ -48,6 +48,7 @@ import org.springframework.web.bind.annotation.*;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -106,7 +107,7 @@ public class AuthController {
Long userId = LoginHelper.getUserId();
scheduledExecutorService.schedule(() -> {
SseMessageDto dto = new SseMessageDto();
dto.setMessage("欢迎登录RuoYi-Vue-Plus后台管理系统");
dto.setMessage(DateUtils.getTodayHour(new Date()) + "好,欢迎登录 RuoYi-Vue-Plus 后台管理系统");
dto.setUserIds(List.of(userId));
SseMessageUtils.publishMessage(dto);
}, 5, TimeUnit.SECONDS);
@@ -147,8 +148,8 @@ public class AuthController {
StpUtil.checkLogin();
// 获取第三方登录信息
AuthResponse<AuthUser> response = SocialUtils.loginAuth(
loginBody.getSource(), loginBody.getSocialCode(),
loginBody.getSocialState(), socialProperties);
loginBody.getSource(), loginBody.getSocialCode(),
loginBody.getSocialState(), socialProperties);
AuthUser authUserData = response.getData();
// 判断授权响应是否成功
if (!response.ok()) {

View File

@@ -5,7 +5,7 @@ user.jcaptcha.expire=验证码已失效
user.not.exists=对不起, 您的账号:{0} 不存在.
user.password.not.match=用户不存在/密码错误
user.password.retry.limit.count=密码输入错误{0}次
user.password.retry.limit.exceed=密码输入错误{0}次,户锁定{1}分钟
user.password.retry.limit.exceed=密码输入错误{0}次,户锁定{1}分钟
user.password.delete=对不起,您的账号:{0} 已被删除
user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员
role.blocked=角色已封禁,请联系管理员
@@ -47,10 +47,10 @@ repeat.submit.message=不允许重复提交,请稍候再试
rate.limiter.message=访问过于频繁,请稍候再试
sms.code.not.blank=短信验证码不能为空
sms.code.retry.limit.count=短信验证码输入错误{0}次
sms.code.retry.limit.exceed=短信验证码输入错误{0}次,户锁定{1}分钟
sms.code.retry.limit.exceed=短信验证码输入错误{0}次,户锁定{1}分钟
email.code.not.blank=邮箱验证码不能为空
email.code.retry.limit.count=邮箱验证码输入错误{0}次
email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,户锁定{1}分钟
email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,户锁定{1}分钟
xcx.code.not.blank=小程序[code]不能为空
social.source.not.blank=第三方登录平台[source]不能为空
social.code.not.blank=第三方登录平台[code]不能为空

View File

@@ -5,7 +5,7 @@ user.jcaptcha.expire=验证码已失效
user.not.exists=对不起, 您的账号:{0} 不存在.
user.password.not.match=用户不存在/密码错误
user.password.retry.limit.count=密码输入错误{0}次
user.password.retry.limit.exceed=密码输入错误{0}次,户锁定{1}分钟
user.password.retry.limit.exceed=密码输入错误{0}次,户锁定{1}分钟
user.password.delete=对不起,您的账号:{0} 已被删除
user.blocked=对不起,您的账号:{0} 已禁用,请联系管理员
role.blocked=角色已封禁,请联系管理员
@@ -47,10 +47,10 @@ repeat.submit.message=不允许重复提交,请稍候再试
rate.limiter.message=访问过于频繁,请稍候再试
sms.code.not.blank=短信验证码不能为空
sms.code.retry.limit.count=短信验证码输入错误{0}次
sms.code.retry.limit.exceed=短信验证码输入错误{0}次,户锁定{1}分钟
sms.code.retry.limit.exceed=短信验证码输入错误{0}次,户锁定{1}分钟
email.code.not.blank=邮箱验证码不能为空
email.code.retry.limit.count=邮箱验证码输入错误{0}次
email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,户锁定{1}分钟
email.code.retry.limit.exceed=邮箱验证码输入错误{0}次,户锁定{1}分钟
xcx.code.not.blank=小程序[code]不能为空
social.source.not.blank=第三方登录平台[source]不能为空
social.code.not.blank=第三方登录平台[code]不能为空

View File

@@ -14,7 +14,7 @@
</description>
<properties>
<revision>5.5.1</revision>
<revision>5.5.2</revision>
</properties>
<dependencyManagement>

View File

@@ -3,10 +3,8 @@ package org.dromara.common.core.config;
import jakarta.annotation.PreDestroy;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.dromara.common.core.config.properties.ThreadPoolProperties;
import org.dromara.common.core.utils.SpringUtils;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.core.task.VirtualThreadTaskExecutor;
@@ -19,7 +17,6 @@ import java.util.concurrent.*;
**/
@Slf4j
@AutoConfiguration
@EnableConfigurationProperties(ThreadPoolProperties.class)
public class ThreadPoolConfig {
/**

View File

@@ -1,30 +0,0 @@
package org.dromara.common.core.config.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* 线程池 配置属性
*
* @author Lion Li
*/
@Data
@ConfigurationProperties(prefix = "thread-pool")
public class ThreadPoolProperties {
/**
* 是否开启线程池
*/
private boolean enabled;
/**
* 队列最大长度
*/
private int queueCapacity;
/**
* 线程池维护线程所允许的空闲时间
*/
private int keepAliveSeconds;
}

View File

@@ -61,7 +61,7 @@ public class UserDTO implements Serializable {
private String sex;
/**
* 号状态0正常 1停用
* 号状态0正常 1停用
*/
private String status;

View File

@@ -1,5 +1,7 @@
package org.dromara.common.core.utils;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.dromara.common.core.enums.FormatsType;
import org.dromara.common.core.exception.ServiceException;
@@ -297,4 +299,80 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
}
}
/**
* 根据指定日期时间获取时间段(凌晨 / 上午 / 中午 / 下午 / 晚上)
*
* @param date 日期时间
* @return 时间段描述
*/
public static String getTodayHour(Date date) {
int hour = DateUtil.hour(date, true);
if (hour <= 6) {
return "凌晨";
} else if (hour < 12) {
return "上午";
} else if (hour == 12) {
return "中午";
} else if (hour <= 18) {
return "下午";
} else {
return "晚上";
}
}
/**
* 将日期格式化为仿微信的友好时间
* <p>
* 规则说明:
* 1. 未来时间yyyy-MM-dd HH:mm
* 2. 今天:
* - 1 分钟内:刚刚
* - 1 小时内X 分钟前
* - 超过 1 小时:凌晨/上午/中午/下午/晚上 HH:mm
* 3. 昨天:昨天 HH:mm
* 4. 本周周X HH:mm
* 5. 今年内MM-dd HH:mm
* 6. 非今年yyyy-MM-dd HH:mm
*
* @param date 日期时间
* @return 格式化后的时间描述
*/
public static String formatFriendlyTime(Date date) {
if (date == null) {
return "";
}
Date now = DateUtil.date();
// 未来时间或非今年
if (date.after(now) || DateUtil.year(date) != DateUtil.year(now)) {
return parseDateToStr(FormatsType.YYYY_MM_DD_HH_MM, date);
}
// 今天
if (DateUtil.isSameDay(date, now)) {
long minutes = DateUtil.between(date, now, DateUnit.MINUTE);
if (minutes < 1) {
return "刚刚";
}
if (minutes < 60) {
return minutes + "分钟前";
}
return getTodayHour(date) + " " + DateUtil.format(date, "HH:mm");
}
// 昨天
if (DateUtil.isSameDay(date, DateUtil.yesterday())) {
return "昨天 " + DateUtil.format(date, "HH:mm");
}
// 本周
if (DateUtil.isSameWeek(date, now, true)) {
return DateUtil.dayOfWeekEnum(date).toChinese("")
+ " " + DateUtil.format(date, "HH:mm");
}
// 今年内其它时间
return DateUtil.format(date, "MM-dd HH:mm");
}
}

View File

@@ -28,27 +28,60 @@ public class DesensitizedUtils extends DesensitizedUtil {
}
int len = value.length();
int prefixMaskLimit = prefixVisible + maskLength;
int fullLimit = prefixMaskLimit + suffixVisible;
// 总长度小于等于前后可见长度 → 全掩码
if (len <= prefixVisible + suffixVisible) {
// 规则 1长度 <= 中间掩码长度 → 全掩码
if (len <= maskLength) {
return StrUtil.repeat('*', len);
}
String mask = StrUtil.repeat('*', maskLength);
// 规则 2长度 <= 前缀 + 中间掩码
if (len <= prefixMaskLimit) {
return value.substring(0, len - maskLength) + mask;
}
String prefix = value.substring(0, prefixVisible);
// 规则 3长度 <= 前缀 + 中间掩码 + 后缀
if (len <= fullLimit) {
int suffixLen = len - prefixMaskLimit;
return prefix + mask + value.substring(len - suffixLen);
}
// 规则 4标准形态
return prefix + mask + value.substring(len - suffixVisible);
}
/**
* 高安全级别脱敏方法Token / 私钥)
*
* @param value 原始字符串
* @param prefixVisible 前面可见长度推荐0~4
* @param suffixVisible 后面可见长度推荐0~4
* @return 脱敏后字符串
*/
public static String maskHighSecurity(String value, int prefixVisible, int suffixVisible) {
if (StrUtil.isBlank(value)) {
return value;
}
int len = value.length();
// 规则1长度 <= 前缀可见长度 → 全部掩码
if (len <= prefixVisible) {
return StrUtil.repeat('*', len);
}
// 可用长度 = 总长度 - 前后可见长度
int available = len - prefixVisible - suffixVisible;
// 规则2长度 <= 前缀 + 后缀可见长度 → 优先掩码后面
if (len <= prefixVisible + suffixVisible) {
return value.substring(0, len - prefixVisible) + StrUtil.repeat('*', prefixVisible);
}
// 中间掩码长度不能超过可用长度
int actualMaskLength = Math.min(maskLength, available);
// 剩余字符尽量显示在中间掩码旁
int remaining = available - actualMaskLength;
String middleChars = remaining > 0 ? value.substring(prefixVisible, prefixVisible + remaining) : "";
String middleMask = StrUtil.repeat('*', actualMaskLength);
String prefix = value.substring(0, prefixVisible);
String suffix = value.substring(len - suffixVisible);
return prefix + middleChars + middleMask + suffix;
// 规则3标准形态 → 前后可见,中间全部掩码
return value.substring(0, prefixVisible)
+ StrUtil.repeat('*', len - prefixVisible - suffixVisible)
+ value.substring(len - suffixVisible);
}
}

View File

@@ -20,51 +20,24 @@ public class AddressUtils {
public static final String UNKNOWN_IP = "XX XX";
// 内网地址
public static final String LOCAL_ADDRESS = "内网IP";
// 未知地址
public static final String UNKNOWN_ADDRESS = "未知";
public static String getRealAddressByIP(String ip) {
// 处理空串并过滤HTML标签
ip = HtmlUtil.cleanHtmlTag(StringUtils.blankToDefault(ip,""));
// 判断是否为IPv4
if (NetUtils.isIPv4(ip)) {
return resolverIPv4Region(ip);
}
boolean isIPv4 = NetUtils.isIPv4(ip);
// 判断是否为IPv6
if (NetUtils.isIPv6(ip)) {
return resolverIPv6Region(ip);
}
boolean isIPv6 = NetUtils.isIPv6(ip);
// 如果不是IPv4或IPv6则返回未知IP
return UNKNOWN_IP;
}
/**
* 根据IPv4地址查询IP归属行政区域
* @param ip ipv4地址
* @return 归属行政区域
*/
private static String resolverIPv4Region(String ip){
if (!isIPv4 && !isIPv6) {
return UNKNOWN_IP;
}
// 内网不查询
if (NetUtils.isInnerIP(ip)) {
if ((isIPv4 && NetUtils.isInnerIP(ip)) || (isIPv6 && NetUtils.isInnerIPv6(ip))) {
return LOCAL_ADDRESS;
}
return RegionUtils.getCityInfo(ip);
}
/**
* 根据IPv6地址查询IP归属行政区域
* @param ip ipv6地址
* @return 归属行政区域
*/
private static String resolverIPv6Region(String ip){
// 内网不查询
if (NetUtils.isInnerIPv6(ip)) {
return LOCAL_ADDRESS;
}
log.warn("ip2region不支持IPV6地址解析{}", ip);
// 不支持IPv6不再进行没有必要的IP地址信息的解析直接返回
// 如有需要可自行实现IPv6地址信息解析逻辑并在这里返回
return UNKNOWN_ADDRESS;
// TipsIp2Region 提供了精简的IPv6地址库精简的IPv6地址库并不能完全支持IPv6地址的查询且准确度上可能会存在问题如需要准确的IPv6地址查询建议自行实现
return RegionUtils.getRegion(ip);
}
}

View File

@@ -1,50 +1,154 @@
package org.dromara.common.core.utils.ip;
import cn.hutool.core.io.resource.NoResourceException;
import cn.hutool.core.io.resource.ResourceUtil;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.StringUtils;
import org.lionsoul.ip2region.xdb.Searcher;
import org.lionsoul.ip2region.service.Config;
import org.lionsoul.ip2region.service.Ip2Region;
import org.lionsoul.ip2region.xdb.Util;
import java.io.InputStream;
import java.time.Duration;
/**
* 根据ip地址定位工具类离线方式
* 参考地址:<a href="https://gitee.com/lionsoul/ip2region/tree/master/binding/java">集成 ip2region 实现离线IP地址定位库</a>
* IP地址行政区域工具类
* 参考地址:<a href="https://gitee.com/lionsoul/ip2region/tree/master/binding/java">ip2region xdb java 查询客户端实现</a>
* xdb数据库文件下载<a href="https://gitee.com/lionsoul/ip2region/tree/master/data">ip2region data</a>
*
* @author lishuyan
* @author 秋辞未寒
*/
@Slf4j
public class RegionUtils {
// IP地址库文件名称
public static final String IP_XDB_FILENAME = "ip2region.xdb";
// 默认IPv4地址库文件路径
// 下载地址https://gitee.com/lionsoul/ip2region/blob/master/data/ip2region_v4.xdb
public static final String DEFAULT_IPV4_XDB_PATH = "ip2region_v4.xdb";
private static final Searcher SEARCHER;
// 默认IPv6地址库文件路径
// 下载地址https://gitee.com/lionsoul/ip2region/blob/master/data/ip2region_v6.xdb
public static final String DEFAULT_IPV6_XDB_PATH = "ip2region_v6.xdb";
// 默认缓存切片大小为15MB仅针对BufferCache全量读取有效如果你的xdb数据库很大合理设置该值可以有效提升BufferCache模式下的查询效率具体可以查看Ip2Region的README
// 注意设置过大的值可能会申请内存时因内存不足而导致OOM请合理设置该值。
// READMEhttps://gitee.com/lionsoul/ip2region/tree/master/binding/java
public static final int DEFAULT_CACHE_SLICE_BYTES = 1024 * 1024 * 15;
// 未知地址
public static final String UNKNOWN_ADDRESS = "未知";
// Ip2Region服务实例
private static Ip2Region ip2Region;
// 初始化Ip2Region服务实例
static {
try {
// 1、将 ip2region 数据库文件 xdb 从 ClassPath 加载到内存。
// 2、基于加载到内存的 xdb 数据创建一个 Searcher 查询对象
SEARCHER = Searcher.newWithBuffer(ResourceUtil.readBytes(IP_XDB_FILENAME));
log.info("RegionUtils初始化成功加载IP地址库数据成功");
} catch (NoResourceException e) {
throw new ServiceException("RegionUtils初始化失败原因IP地址库数据不存在");
// 注意Ip2Region 的xdb文件加载策略 CachePolicy 有三种分别是BufferCache全量读取xdb到内存中、VIndexCache默认策略按需读取并缓存、NoCache实时读取
// 本项目工具使用的 CachePolicy 为 BufferCacheBufferCache会加载整个xdb文件到内存中setXdbInputStream 仅支持 BufferCache 策略
// 因为加载整个xdb文件会耗费非常大的内存如果你不希望加载整个xdb到内存中更推荐使用 VIndexCache 或 NoCache即实时读取文件策略和 setXdbPath/setXdbFile 加载方法需要注意的一点setXdbPath 和 setXdbFile 不支持读取ClassPath即源码和resource目录中的文件
// 一般而言更建议把xdb数据库放到一个指定的文件目录中即不打包进jar包中然后使用 VIndexCache + 配合SearcherPool的并发池读取数据更方便随时更新xdb数据库
InputStream v4InputStream = ResourceUtil.getStream(DEFAULT_IPV4_XDB_PATH);
// IPv4配置
Config v4Config = Config.custom()
.setCachePolicy(Config.BufferCache)
//.setXdbFile(v4TempXdb)
.setXdbInputStream(v4InputStream)
//
.setCacheSliceBytes(DEFAULT_CACHE_SLICE_BYTES)
.asV4();
// IPv6配置
Config v6Config = null;
InputStream v6XdbInputStream = ResourceUtil.getStreamSafe(DEFAULT_IPV6_XDB_PATH);
if (v6XdbInputStream == null) {
log.warn("未加载 IPv6 地址库:未在类路径下找到文件 {}。当前仅启用 IPv4 查询。如需启用 IPv6请将 ip2region_v6.xdb 放置到 resources 目录", DEFAULT_IPV6_XDB_PATH);
} else {
v6Config = Config.custom()
.setCachePolicy(Config.BufferCache)
//.setXdbFile(v6TempXdb)
.setXdbInputStream(v6XdbInputStream)
.setCacheSliceBytes(DEFAULT_CACHE_SLICE_BYTES)
.asV6();
}
// 初始化Ip2Region实例
RegionUtils.ip2Region = Ip2Region.create(v4Config, v6Config);
log.debug("IP工具初始化成功加载IP地址库数据成功");
} catch (Exception e) {
throw new ServiceException("RegionUtils初始化失败原因" + e.getMessage());
throw new ServiceException("RegionUtils初始化失败原因{}", e.getMessage());
}
}
/**
* 根据IP地址离线获取城市
*
* @param ipString ip地址字符串
*/
public static String getCityInfo(String ip) {
public static String getRegion(String ipString) {
try {
// 3、执行查询
String region = SEARCHER.search(StringUtils.trim(ip));
return region.replace("0|", "").replace("|0", "");
String region = ip2Region.search(ipString);
if (StringUtils.isBlank(region)) {
region = UNKNOWN_ADDRESS;
}
return region;
} catch (Exception e) {
log.error("IP地址离线获取城市异常 {}", ip);
return "未知";
log.error("IP地址离线获取城市异常 {}", ipString);
return UNKNOWN_ADDRESS;
}
}
/**
* 根据IP地址离线获取城市
*
* @param ipBytes ip地址字节数组
*/
public static String getRegion(byte[] ipBytes) {
try {
String region = ip2Region.search(ipBytes);
if (StringUtils.isBlank(region)) {
region = UNKNOWN_ADDRESS;
}
return region;
} catch (Exception e) {
log.error("IP地址离线获取城市异常 {}", Util.ipToString(ipBytes));
return UNKNOWN_ADDRESS;
}
}
/**
* 关闭Ip2Region服务
*/
public static void close() {
if (ip2Region == null) {
return;
}
try {
ip2Region.close(10000);
} catch (Exception e) {
log.error("Ip2Region服务关闭异常", e);
}
}
/**
* 关闭Ip2Region服务
*
* @param timeout 关闭超时时间
*/
public static void close(final Duration timeout) {
if (ip2Region == null) {
return;
}
if (timeout == null) {
close();
return;
}
try {
ip2Region.close(timeout.toMillis());
} catch (Exception e) {
log.error("Ip2Region服务关闭异常", e);
}
}

View File

@@ -29,7 +29,10 @@ public class CellMergeHandler {
// 行合并开始下标
this.rowIndex = hasTitle ? 1 : 0;
}
private CellMergeHandler(final boolean hasTitle, final int rowIndex) {
this.hasTitle = hasTitle;
this.rowIndex = hasTitle ? rowIndex : 0;
}
@SneakyThrows
public List<CellRangeAddress> handle(List<?> rows) {
// 如果入参为空集合则返回空集
@@ -103,6 +106,10 @@ public class CellMergeHandler {
}
if (isAddResult && i > current) {
//如果是同一行,则跳过合并
if (current + rowIndex == lastRow) {
continue;
}
result.add(new CellRangeAddress(current + rowIndex, lastRow, colNum, colNum));
}
}
@@ -177,6 +184,16 @@ public class CellMergeHandler {
return new FieldColumnIndex(colIndex, cellMerge);
}
}
/**
* 创建一个单元格合并处理器实例
*
* @param hasTitle 是否合并标题
* @param rowIndex 行索引
* @return 单元格合并处理器
*/
public static CellMergeHandler of(final boolean hasTitle, final int rowIndex) {
return new CellMergeHandler(hasTitle, rowIndex);
}
/**
* 创建一个单元格合并处理器实例

View File

@@ -31,6 +31,10 @@ public class CellMergeStrategy extends AbstractMergeStrategy implements SheetWri
this.cellList = CellMergeHandler.of(hasTitle).handle(list);
}
public CellMergeStrategy(List<?> list, boolean hasTitle, int rowIndex) {
this.cellList = CellMergeHandler.of(hasTitle, rowIndex).handle(list);
}
@Override
protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
if (CollUtil.isEmpty(cellList)) {

View File

@@ -192,7 +192,7 @@ public class MailUtils {
/**
* 发送邮件给多人
*
* @param mailAccount 邮件户信息
* @param mailAccount 邮件户信息
* @param tos 收件人列表
* @param subject 标题
* @param content 正文
@@ -207,7 +207,7 @@ public class MailUtils {
/**
* 发送邮件给多人
*
* @param mailAccount 邮件户信息
* @param mailAccount 邮件户信息
* @param tos 收件人列表
* @param ccs 抄送人列表可以为null或空
* @param bccs 密送人列表可以为null或空
@@ -343,7 +343,7 @@ public class MailUtils {
/**
* 发送邮件给多人
*
* @param mailAccount 邮件户信息
* @param mailAccount 邮件户信息
* @param tos 收件人列表
* @param subject 标题
* @param content 正文
@@ -360,7 +360,7 @@ public class MailUtils {
/**
* 发送邮件给多人
*
* @param mailAccount 邮件户信息
* @param mailAccount 邮件户信息
* @param tos 收件人列表
* @param ccs 抄送人列表可以为null或空
* @param bccs 密送人列表可以为null或空
@@ -400,7 +400,7 @@ public class MailUtils {
/**
* 发送邮件给多人
*
* @param mailAccount 邮件户信息
* @param mailAccount 邮件户信息
* @param useGlobalSession 是否全局共享Session
* @param tos 收件人列表
* @param ccs 抄送人列表可以为null或空

View File

@@ -141,7 +141,8 @@ public class OssClient {
try {
// 构建上传请求对象
FileUpload fileUpload = transferManager.uploadFile(
x -> x.putObjectRequest(
x -> {
x.source(filePath).putObjectRequest(
y -> y.bucket(properties.getBucketName())
.key(key)
.contentMD5(StringUtils.isNotEmpty(md5Digest) ? md5Digest : null)
@@ -149,10 +150,13 @@ public class OssClient {
// 用于设置对象的访问控制列表ACL。不同云厂商对ACL的支持和实现方式有所不同
// 因此根据具体的云服务提供商你可能需要进行不同的配置自行开启阿里云有acl权限配置腾讯云没有acl权限配置
//.acl(getAccessPolicy().getObjectCannedACL())
.build())
.addTransferListener(LoggingTransferListener.create())
.source(filePath).build());
.build()
);
if (log.isDebugEnabled()) {
x.addTransferListener(LoggingTransferListener.create());
}
}
);
// 等待上传完成并获取上传结果
CompletedFileUpload uploadResult = fileUpload.completionFuture().join();
String eTag = uploadResult.response().eTag();
@@ -192,16 +196,21 @@ public class OssClient {
// 使用 transferManager 进行上传
Upload upload = transferManager.upload(
x -> x.requestBody(body).addTransferListener(LoggingTransferListener.create())
.putObjectRequest(
x -> {
x.requestBody(body).putObjectRequest(
y -> y.bucket(properties.getBucketName())
.key(key)
.contentType(contentType)
// 用于设置对象的访问控制列表ACL。不同云厂商对ACL的支持和实现方式有所不同
// 因此根据具体的云服务提供商你可能需要进行不同的配置自行开启阿里云有acl权限配置腾讯云没有acl权限配置
//.acl(getAccessPolicy().getObjectCannedACL())
.build())
.build());
.build()
);
if (log.isDebugEnabled()) {
x.addTransferListener(LoggingTransferListener.create());
}
}
);
// 将输入流写入请求体
body.writeInputStream(inputStream);
@@ -229,13 +238,17 @@ public class OssClient {
Path tempFilePath = FileUtils.createTempFile().toPath();
// 使用 S3TransferManager 下载文件
FileDownload downloadFile = transferManager.downloadFile(
x -> x.getObjectRequest(
x -> {
x.destination(tempFilePath).getObjectRequest(
y -> y.bucket(properties.getBucketName())
.key(removeBaseUrl(path))
.build())
.addTransferListener(LoggingTransferListener.create())
.destination(tempFilePath)
.build());
.build()
);
if (log.isDebugEnabled()) {
x.addTransferListener(LoggingTransferListener.create());
}
}
);
// 等待文件下载操作完成
downloadFile.completionFuture().join();
return tempFilePath;
@@ -244,8 +257,8 @@ public class OssClient {
/**
* 下载文件从 Amazon S3 到 输出流
*
* @param key 文件在 Amazon S3 中的对象键
* @param out 输出流
* @param key 文件在 Amazon S3 中的对象键
* @param out 输出流
* @param consumer 自定义处理逻辑
* @throws OssException 如果下载失败,抛出自定义异常
*/
@@ -260,26 +273,24 @@ public class OssClient {
/**
* 下载文件从 Amazon S3 到 输出流
*
* @param key 文件在 Amazon S3 中的对象键
* @param key 文件在 Amazon S3 中的对象键
* @param contentLengthConsumer 文件大小消费者函数
* @return 写出订阅器
* @throws OssException 如果下载失败,抛出自定义异常
*/
public WriteOutSubscriber<OutputStream> download(String key, Consumer<Long> contentLengthConsumer) {
try {
// 构建下载请求
DownloadRequest<ResponsePublisher<GetObjectResponse>> publisherDownloadRequest = DownloadRequest.builder()
// 文件对象
.getObjectRequest(y -> y.bucket(properties.getBucketName())
.key(key)
.build())
.addTransferListener(LoggingTransferListener.create())
DownloadRequest.TypedBuilder<ResponsePublisher<GetObjectResponse>> typedBuilder = DownloadRequest.builder()
// 使用发布订阅转换器
.responseTransformer(AsyncResponseTransformer.toPublisher())
.build();
// 文件对象
.getObjectRequest(y -> y.bucket(properties.getBucketName()).key(key).build());
if (log.isDebugEnabled()) {
typedBuilder.addTransferListener(LoggingTransferListener.create());
}
// 使用 S3TransferManager 下载文件
Download<ResponsePublisher<GetObjectResponse>> publisherDownload = transferManager.download(publisherDownloadRequest);
Download<ResponsePublisher<GetObjectResponse>> publisherDownload = transferManager.download(typedBuilder.build());
// 获取下载发布订阅转换器
ResponsePublisher<GetObjectResponse> publisher = publisherDownload.completionFuture().join().result();
// 执行文件大小消费者函数
@@ -289,7 +300,7 @@ public class OssClient {
// 构建写出订阅器对象
return out -> {
// 创建可写入的字节通道
try(WritableByteChannel channel = Channels.newChannel(out)){
try (WritableByteChannel channel = Channels.newChannel(out)) {
// 订阅数据
publisher.subscribe(byteBuffer -> {
while (byteBuffer.hasRemaining()) {
@@ -347,7 +358,7 @@ public class OssClient {
*
* @param objectKey 对象KEY
* @param expiredTime 链接授权到期时间
* @param metadata 元数据
* @param metadata 元数据
*/
public String createPresignedPutUrl(String objectKey, Duration expiredTime, Map<String, String> metadata) {
// 使用 AWS S3 预签名 URL 的生成器 获取上传文件对象的预签名 URL

View File

@@ -88,6 +88,11 @@ public enum SensitiveStrategy {
*/
STRING_MASK(s -> DesensitizedUtils.mask(s, 4, 4, 4)),
/**
* 高安全级别脱敏Token / 私钥前2位可见后2位可见中间全部掩码
*/
MASK_HIGH_SECURITY(s -> DesensitizedUtils.maskHighSecurity(s, 2, 2)),
/**
* 清空为""
*/

View File

@@ -28,9 +28,14 @@ public class SocialLoginConfigProperties {
private String redirectUri;
/**
* 是否获取unionId
* 是否需要申请unionid目前只针对qq登录
*/
private boolean unionId;
private Boolean unionId;
/**
* Microsoft Entra ID原微软 AAD中的租户 ID
*/
private String tenantId;
/**
* Coding 企业名称

View File

@@ -57,7 +57,7 @@ public class SocialUtils {
case "taobao" -> new AuthTaobaoRequest(builder.build(), STATE_CACHE);
case "douyin" -> new AuthDouyinRequest(builder.build(), STATE_CACHE);
case "linkedin" -> new AuthLinkedinRequest(builder.build(), STATE_CACHE);
case "microsoft" -> new AuthMicrosoftRequest(builder.build(), STATE_CACHE);
case "microsoft" -> new AuthMicrosoftRequest(builder.tenantId(obj.getTenantId()).build(), STATE_CACHE);
case "renren" -> new AuthRenrenRequest(builder.build(), STATE_CACHE);
case "stack_overflow" -> new AuthStackOverflowRequest(builder.stackOverflowKey(obj.getStackOverflowKey()).build(), STATE_CACHE);
case "huawei" -> new AuthHuaweiV3Request(builder.build(), STATE_CACHE);

View File

@@ -20,7 +20,7 @@ public class NicknameTranslationImpl implements TranslationInterface<String> {
@Override
public String translation(Object key, String other) {
if (key instanceof Long id) {
return userService.selectNicknameByIds(id.toString());
return userService.selectNicknameById(id);
} else if (key instanceof String ids) {
return userService.selectNicknameByIds(ids);
}

View File

@@ -1,5 +1,6 @@
package org.dromara.common.translation.core.impl;
import cn.hutool.core.convert.Convert;
import org.dromara.common.core.service.UserService;
import org.dromara.common.translation.annotation.TranslationType;
import org.dromara.common.translation.constant.TransConstant;
@@ -19,9 +20,6 @@ public class UserNameTranslationImpl implements TranslationInterface<String> {
@Override
public String translation(Object key, String other) {
if (key instanceof Long id) {
return userService.selectUserNameById(id);
}
return null;
return userService.selectUserNameById(Convert.toLong(key));
}
}

View File

@@ -14,6 +14,7 @@ import org.dromara.common.core.exception.SseException;
import org.dromara.common.core.exception.base.BaseException;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.json.utils.JsonUtils;
import org.springframework.context.MessageSourceResolvable;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.expression.ExpressionException;
import org.springframework.http.converter.HttpMessageNotReadableException;
@@ -25,6 +26,7 @@ import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
import org.springframework.web.method.annotation.HandlerMethodValidationException;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;
@@ -191,6 +193,16 @@ public class GlobalExceptionHandler {
return R.fail(message);
}
/**
* 方法参数校验异常 用于处理 @Validated 注解
*/
@ExceptionHandler(HandlerMethodValidationException.class)
public R<Void> handlerMethodValidationException(HandlerMethodValidationException e) {
log.error(e.getMessage());
String message = StreamUtils.join(e.getAllErrors(), MessageSourceResolvable::getDefaultMessage, ", ");
return R.fail(message);
}
/**
* JSON 解析异常Jackson 在处理 JSON 格式出错时抛出)
* 可能是请求体格式非法,也可能是服务端反序列化失败

View File

@@ -8,6 +8,7 @@ import org.dromara.common.websocket.holder.WebSocketSessionHolder;
import org.dromara.common.websocket.utils.WebSocketUtils;
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
import org.springframework.web.socket.handler.ConcurrentWebSocketSessionDecorator;
import java.io.IOException;
import java.util.List;
@@ -33,7 +34,7 @@ public class PlusWebSocketHandler extends AbstractWebSocketHandler {
log.info("[connect] invalid token received. sessionId: {}", session.getId());
return;
}
WebSocketSessionHolder.addSession(loginUser.getUserId(), session);
WebSocketSessionHolder.addSession(loginUser.getUserId(), new ConcurrentWebSocketSessionDecorator(session, 10 * 1000, 64000));
log.info("[connect] sessionId: {},userId:{},userType:{}", session.getId(), loginUser.getUserId(), loginUser.getUserType());
}

View File

@@ -113,7 +113,7 @@ public class WebSocketUtils {
* @param session WebSocket会话
* @param message 要发送的WebSocket消息对象
*/
private synchronized static void sendMessage(WebSocketSession session, WebSocketMessage<?> message) {
private static void sendMessage(WebSocketSession session, WebSocketMessage<?> message) {
if (session == null || !session.isOpen()) {
log.warn("[send] session会话已经关闭");
} else {

View File

@@ -1,146 +0,0 @@
package com.aizuda.snailjob.server.common.register;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.aizuda.snailjob.common.core.enums.NodeTypeEnum;
import com.aizuda.snailjob.common.core.util.JsonUtil;
import com.aizuda.snailjob.common.core.util.NetUtil;
import com.aizuda.snailjob.common.core.util.SnailJobVersion;
import com.aizuda.snailjob.common.core.util.StreamUtils;
import com.aizuda.snailjob.common.log.SnailJobLog;
import com.aizuda.snailjob.server.common.cache.CacheConsumerGroup;
import com.aizuda.snailjob.server.common.config.SystemProperties;
import com.aizuda.snailjob.server.common.convert.RegisterNodeInfoConverter;
import com.aizuda.snailjob.server.common.dto.ServerNodeExtAttrs;
import com.aizuda.snailjob.server.common.handler.InstanceManager;
import com.aizuda.snailjob.template.datasource.persistence.po.ServerNode;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* 服务端注册
*
* @author opensnail
* @date 2023-06-07
* @since 1.6.0
*/
@Component(ServerRegister.BEAN_NAME)
@RequiredArgsConstructor
public class ServerRegister extends AbstractRegister {
public static final String BEAN_NAME = "serverRegister";
private final ScheduledExecutorService serverRegisterNode = Executors.newSingleThreadScheduledExecutor(r -> new Thread(r, "server-register-node"));
public static final int DELAY_TIME = 30;
public static final String CURRENT_CID;
public static final String GROUP_NAME = "DEFAULT_SERVER";
public static final String NAMESPACE_ID = "DEFAULT_SERVER_NAMESPACE_ID";
private final InstanceManager instanceManager;
private final SystemProperties systemProperties;
private final ServerProperties serverProperties;
static {
CURRENT_CID = IdUtil.getSnowflakeNextIdStr();
}
@Override
public boolean supports(int type) {
return getNodeType().equals(type);
}
@Override
protected void beforeProcessor(RegisterContext context) {
// 新增扩展参数
ServerNodeExtAttrs serverNodeExtAttrs = new ServerNodeExtAttrs();
serverNodeExtAttrs.setWebPort(serverProperties.getPort());
serverNodeExtAttrs.setSystemVersion(SnailJobVersion.getVersion());
context.setGroupName(GROUP_NAME);
context.setHostId(CURRENT_CID);
String serverHost = systemProperties.getServerHost();
if (StrUtil.isEmptyIfStr(serverHost)) {
serverHost = NetUtil.getLocalIpStr();
}
context.setHostIp(serverHost);
context.setHostPort(systemProperties.getServerPort());
context.setContextPath(Optional.ofNullable(serverProperties.getServlet().getContextPath()).orElse(StrUtil.EMPTY));
context.setNamespaceId(NAMESPACE_ID);
context.setExtAttrs(JsonUtil.toJsonString(serverNodeExtAttrs));
}
@Override
protected LocalDateTime getExpireAt() {
return LocalDateTime.now().plusSeconds(DELAY_TIME);
}
@Override
protected boolean doRegister(RegisterContext context, ServerNode serverNode) {
refreshExpireAt(Lists.newArrayList(serverNode));
return Boolean.TRUE;
}
@Override
protected void afterProcessor(final ServerNode serverNode) {
try {
// 同步当前POD消费的组的节点信息
// netty的client只会注册到一个服务端若组分配的和client连接的不是一个POD则会导致当前POD没有其他客户端的注册信息
ConcurrentMap<String /*groupName*/, Set<String>/*namespaceId*/> allConsumerGroupName = CacheConsumerGroup.getAllConsumerGroupName();
if (CollUtil.isNotEmpty(allConsumerGroupName)) {
Set<String> namespaceIdSets = StreamUtils.toSetByFlatMap(allConsumerGroupName.values(), Set::stream);
if (CollUtil.isEmpty(namespaceIdSets)) {
return;
}
List<ServerNode> serverNodes = serverNodeMapper.selectList(
new LambdaQueryWrapper<ServerNode>()
.eq(ServerNode::getNodeType, NodeTypeEnum.CLIENT.getType())
.in(ServerNode::getNamespaceId, namespaceIdSets)
.in(ServerNode::getGroupName, allConsumerGroupName.keySet()));
for (final ServerNode node : serverNodes) {
// 刷新全量本地缓存
instanceManager.registerOrUpdate(RegisterNodeInfoConverter.INSTANCE.toRegisterNodeInfo(node));
// 刷新过期时间
CacheConsumerGroup.addOrUpdate(node.getGroupName(), node.getNamespaceId());
}
}
} catch (Exception e) {
SnailJobLog.LOCAL.error("Client refresh failed", e);
}
}
@Override
protected Integer getNodeType() {
return NodeTypeEnum.SERVER.getType();
}
@Override
public void start() {
SnailJobLog.LOCAL.info("ServerRegister start");
serverRegisterNode.scheduleAtFixedRate(() -> {
try {
this.register(new RegisterContext());
} catch (Exception e) {
SnailJobLog.LOCAL.error("Server-side registration failed", e);
}
}, 0, DELAY_TIME * 2 / 3, TimeUnit.SECONDS);
}
@Override
public void close() {
SnailJobLog.LOCAL.info("ServerRegister close");
}
}

View File

@@ -62,6 +62,8 @@ public class TestDemoServiceImpl implements ITestDemoService {
private LambdaQueryWrapper<TestDemo> buildQueryWrapper(TestDemoBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<TestDemo> lqw = Wrappers.lambdaQuery();
lqw.eq(bo.getDeptId() != null, TestDemo::getDeptId, bo.getDeptId());
lqw.eq(bo.getUserId() != null, TestDemo::getUserId, bo.getUserId());
lqw.like(StringUtils.isNotBlank(bo.getTestKey()), TestDemo::getTestKey, bo.getTestKey());
lqw.eq(StringUtils.isNotBlank(bo.getValue()), TestDemo::getValue, bo.getValue());
lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,

View File

@@ -2,6 +2,7 @@ package org.dromara.demo.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.demo.domain.TestTree;
@@ -9,7 +10,6 @@ import org.dromara.demo.domain.bo.TestTreeBo;
import org.dromara.demo.domain.vo.TestTreeVo;
import org.dromara.demo.mapper.TestTreeMapper;
import org.dromara.demo.service.ITestTreeService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.Collection;
@@ -44,6 +44,8 @@ public class TestTreeServiceImpl implements ITestTreeService {
private LambdaQueryWrapper<TestTree> buildQueryWrapper(TestTreeBo bo) {
Map<String, Object> params = bo.getParams();
LambdaQueryWrapper<TestTree> lqw = Wrappers.lambdaQuery();
lqw.eq(bo.getDeptId() != null, TestTree::getDeptId, bo.getDeptId());
lqw.eq(bo.getUserId() != null, TestTree::getUserId, bo.getUserId());
lqw.like(StringUtils.isNotBlank(bo.getTreeName()), TestTree::getTreeName, bo.getTreeName());
lqw.between(params.get("beginCreateTime") != null && params.get("endCreateTime") != null,
TestTree::getCreateTime, params.get("beginCreateTime"), params.get("endCreateTime"));

View File

@@ -4,13 +4,12 @@ import com.baomidou.mybatisplus.annotation.FieldStrategy;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.ibatis.type.JdbcType;
import jakarta.validation.constraints.NotBlank;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.domain.BaseEntity;
/**
* 代码生成业务字段表 gen_table_column
@@ -115,6 +114,7 @@ public class GenTableColumn extends BaseEntity {
/**
* 字典类型
*/
@TableField(updateStrategy = FieldStrategy.ALWAYS, jdbcType = JdbcType.VARCHAR)
private String dictType;
/**

View File

@@ -137,6 +137,8 @@ public class SysMenuController extends BaseController {
return R.fail("新增菜单'" + menu.getMenuName() + "'失败,菜单名称已存在");
} else if (SystemConstants.YES_FRAME.equals(menu.getIsFrame()) && !StringUtils.ishttp(menu.getPath())) {
return R.fail("新增菜单'" + menu.getMenuName() + "'失败地址必须以http(s)://开头");
} else if (!menuService.checkRouteConfigUnique(menu)) {
return R.fail("新增菜单'" + menu.getMenuName() + "'失败,路由名称或地址已存在");
}
return toAjax(menuService.insertMenu(menu));
}
@@ -156,6 +158,8 @@ public class SysMenuController extends BaseController {
return R.fail("修改菜单'" + menu.getMenuName() + "'失败地址必须以http(s)://开头");
} else if (menu.getMenuId().equals(menu.getParentId())) {
return R.fail("修改菜单'" + menu.getMenuName() + "'失败,上级菜单不能选择自己");
} else if (!menuService.checkRouteConfigUnique(menu)) {
return R.fail("修改菜单'" + menu.getMenuName() + "'失败,路由名称或地址已存在");
}
return toAjax(menuService.updateMenu(menu));
}

View File

@@ -130,11 +130,11 @@ public class SysMenu extends BaseEntity {
public String getRouterPath() {
String routerPath = this.path;
// 内链打开外网方式
if (getParentId() != 0L && isInnerLink()) {
if (!Constants.TOP_PARENT_ID.equals(getParentId()) && isInnerLink()) {
routerPath = innerLinkReplaceEach(routerPath);
}
// 非外链并且是一级目录(类型为目录)
if (0L == getParentId() && SystemConstants.TYPE_DIR.equals(getMenuType())
if (Constants.TOP_PARENT_ID.equals(getParentId()) && SystemConstants.TYPE_DIR.equals(getMenuType())
&& SystemConstants.NO_FRAME.equals(getIsFrame())) {
routerPath = "/" + this.path;
}
@@ -152,7 +152,7 @@ public class SysMenu extends BaseEntity {
String component = SystemConstants.LAYOUT;
if (StringUtils.isNotEmpty(this.component) && !isMenuFrame()) {
component = this.component;
} else if (StringUtils.isEmpty(this.component) && getParentId() != 0L && isInnerLink()) {
} else if (StringUtils.isEmpty(this.component) && !Constants.TOP_PARENT_ID.equals(getParentId()) && isInnerLink()) {
component = SystemConstants.INNER_LINK;
} else if (StringUtils.isEmpty(this.component) && isParentView()) {
component = SystemConstants.PARENT_VIEW;
@@ -164,7 +164,7 @@ public class SysMenu extends BaseEntity {
* 是否为菜单内部跳转
*/
public boolean isMenuFrame() {
return getParentId() == 0L && SystemConstants.TYPE_MENU.equals(menuType) && isFrame.equals(SystemConstants.NO_FRAME);
return Constants.TOP_PARENT_ID.equals(getParentId()) && SystemConstants.TYPE_MENU.equals(menuType) && isFrame.equals(SystemConstants.NO_FRAME);
}
/**
@@ -178,7 +178,7 @@ public class SysMenu extends BaseEntity {
* 是否为parent_view组件
*/
public boolean isParentView() {
return getParentId() != 0L && SystemConstants.TYPE_DIR.equals(menuType);
return !Constants.TOP_PARENT_ID.equals(getParentId()) && SystemConstants.TYPE_DIR.equals(menuType);
}
/**

View File

@@ -78,7 +78,7 @@ public class SysUser extends TenantEntity {
private String password;
/**
* 号状态0正常 1停用
* 号状态0正常 1停用
*/
private String status;

View File

@@ -78,7 +78,7 @@ public class SysUserBo extends BaseEntity {
private String password;
/**
* 号状态0正常 1停用
* 号状态0正常 1停用
*/
private String status;

View File

@@ -63,9 +63,9 @@ public class SysUserExportVo implements Serializable {
private String sex;
/**
* 号状态0正常 1停用
* 号状态0正常 1停用
*/
@ExcelProperty(value = "号状态", converter = ExcelDictConvert.class)
@ExcelProperty(value = "号状态", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "sys_normal_disable")
private String status;

View File

@@ -67,9 +67,9 @@ public class SysUserImportVo implements Serializable {
private String sex;
/**
* 号状态0正常 1停用
* 号状态0正常 1停用
*/
@ExcelProperty(value = "号状态", converter = ExcelDictConvert.class)
@ExcelProperty(value = "号状态", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "sys_normal_disable")
private String status;

View File

@@ -89,7 +89,7 @@ public class SysUserVo implements Serializable {
private String password;
/**
* 号状态0正常 1停用
* 号状态0正常 1停用
*/
private String status;

View File

@@ -160,4 +160,13 @@ public interface ISysMenuService {
* @return 结果
*/
boolean checkMenuNameUnique(SysMenuBo menu);
/**
* 校验路由组合是否唯一
*
* @param menu 菜单信息
* @return 结果
*/
boolean checkRouteConfigUnique(SysMenuBo menu);
}

View File

@@ -174,7 +174,7 @@ public interface ISysUserService {
* 修改用户状态
*
* @param userId 用户ID
* @param status 号状态
* @param status 号状态
* @return 结果
*/
int updateUserStatus(Long userId, String status);

View File

@@ -6,6 +6,7 @@ import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.constant.Constants;
import org.dromara.common.core.constant.SystemConstants;
import org.dromara.common.core.utils.MapstructUtils;
@@ -29,13 +30,17 @@ import org.dromara.system.service.ISysMenuService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
/**
* 菜单 业务层处理
*
* @author Lion Li
*/
@Slf4j
@RequiredArgsConstructor
@Service
public class SysMenuServiceImpl implements ISysMenuService {
@@ -353,6 +358,43 @@ public class SysMenuServiceImpl implements ISysMenuService {
return !exist;
}
/**
* 校验路由名称是否唯一
*
* @param menuBo 菜单信息
* @return 结果
*/
@Override
public boolean checkRouteConfigUnique(SysMenuBo menuBo) {
SysMenu menu = MapstructUtils.convert(menuBo, SysMenu.class);
long menuId = ObjectUtil.isNull(menu.getMenuId()) ? -1L : menu.getMenuId();
Long parentId = menu.getParentId();
String path = menu.getPath();
String routeName = StringUtils.isEmpty(menu.getRouteName()) ? path : menu.getRouteName();
List<SysMenu> sysMenuList = baseMapper.selectList(
new LambdaQueryWrapper<SysMenu>()
.in(SysMenu::getMenuType, SystemConstants.TYPE_DIR, SystemConstants.TYPE_MENU)
.eq(SysMenu::getPath, path).or().eq(SysMenu::getPath, routeName));
for (SysMenu sysMenu : sysMenuList) {
if (sysMenu.getMenuId() != menuId) {
Long dbParentId = sysMenu.getParentId();
String dbPath = sysMenu.getPath();
String dbRouteName = StringUtils.isEmpty(sysMenu.getRouteName()) ? dbPath : sysMenu.getRouteName();
if (StringUtils.equalsAnyIgnoreCase(path, dbPath) && parentId.longValue() == dbParentId.longValue()) {
log.warn("[同级路由冲突] 同级下已存在相同路由路径 '{}',冲突菜单:{}", dbPath, sysMenu.getMenuName());
return false;
} else if (StringUtils.equalsAnyIgnoreCase(path, dbPath) && parentId.longValue() == Constants.TOP_PARENT_ID) {
log.warn("[根目录路由冲突] 根目录下路由 '{}' 必须唯一,已被菜单 '{}' 占用", path, sysMenu.getMenuName());
return false;
} else if (StringUtils.equalsAnyIgnoreCase(routeName, dbRouteName)) {
log.warn("[路由名称冲突] 路由名称 '{}' 需全局唯一,已被菜单 '{}' 使用", routeName, sysMenu.getMenuName());
return false;
}
}
}
return true;
}
/**
* 根据父节点的ID获取所有子节点
*

View File

@@ -375,7 +375,7 @@ public class SysUserServiceImpl implements ISysUserService, UserService {
* 修改用户状态
*
* @param userId 用户ID
* @param status 号状态
* @param status 号状态
* @return 结果
*/
@Override

View File

@@ -92,4 +92,20 @@ public interface FlowConstant {
* 业务编码
*/
String BUSINESS_CODE = "businessCode";
/**
* 忽略-办理权限校验true忽略false不忽略
*/
String VAR_IGNORE = "ignore";
/**
* 忽略-委派处理true忽略false不忽略
*/
String VAR_IGNORE_DEPUTE = "ignoreDepute";
/**
* 忽略-会签票签处理true忽略false不忽略
*/
String VAR_IGNORE_COOPERATE = "ignoreCooperate";
}

View File

@@ -253,4 +253,8 @@ public class FlowHisTaskVo implements Serializable {
this.cooperateTypeName = CooperateType.getValueByKey(cooperateType);
}
public String getCreateTime() {
return DateUtils.formatFriendlyTime(createTime);
}
}

View File

@@ -1,6 +1,7 @@
package org.dromara.workflow.domain.vo;
import lombok.Data;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.warm.flow.core.entity.User;
@@ -8,7 +9,6 @@ import org.dromara.workflow.common.constant.FlowConstant;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
@@ -212,4 +212,8 @@ public class FlowTaskVo implements Serializable {
private String businessTitle;
//业务扩展信息结束
public String getCreateTime() {
return DateUtils.formatFriendlyTime(createTime);
}
}

View File

@@ -74,6 +74,9 @@ public class WorkflowGlobalListener implements GlobalListener {
String ext = listenerVariable.getNode().getExt();
if (StringUtils.isNotBlank(ext)) {
Map<String, Object> variable = listenerVariable.getVariable();
if (CollUtil.isEmpty(variable)) {
variable = new HashMap<>();
}
NodeExtVo nodeExt = nodeExtService.parseNodeExt(ext, variable);
Set<String> copyList = nodeExt.getCopySettings();
if (CollUtil.isNotEmpty(copyList)) {

View File

@@ -206,6 +206,9 @@ public class FlwCategoryServiceImpl implements IFlwCategoryService, CategoryServ
if (ObjectUtil.isNull(oldCategory)) {
throw new ServiceException("流程分类不存在,无法修改");
}
if (oldCategory.getParentId() == 0L && category.getParentId() != 0L) {
throw new ServiceException("不允许修改顶级分类的父级节点");
}
if (!oldCategory.getParentId().equals(category.getParentId())) {
FlowCategory newParentCategory = baseMapper.selectById(category.getParentId());
if (ObjectUtil.isNotNull(newParentCategory)) {

View File

@@ -208,10 +208,6 @@ public class FlwDefinitionServiceImpl implements IFlwDefinitionService {
@Override
@Transactional(rollbackFor = Exception.class)
public void syncDef(String tenantId) {
List<FlowDefinition> flowDefinitions = flowDefinitionMapper.selectList(new LambdaQueryWrapper<FlowDefinition>().eq(FlowDefinition::getTenantId, DEFAULT_TENANT_ID));
if (CollUtil.isEmpty(flowDefinitions)) {
return;
}
FlowCategory flowCategory = flwCategoryMapper.selectOne(new LambdaQueryWrapper<FlowCategory>()
.eq(FlowCategory::getTenantId, DEFAULT_TENANT_ID)
.eq(FlowCategory::getCategoryId, FlowConstant.FLOW_CATEGORY_ID));
@@ -223,6 +219,11 @@ public class FlwDefinitionServiceImpl implements IFlwDefinitionService {
flowCategory.setUpdateBy(null);
flowCategory.setUpdateTime(null);
flwCategoryMapper.insert(flowCategory);
List<FlowDefinition> flowDefinitions = flowDefinitionMapper.selectList(new LambdaQueryWrapper<FlowDefinition>().eq(FlowDefinition::getTenantId, DEFAULT_TENANT_ID));
if (CollUtil.isEmpty(flowDefinitions)) {
return;
}
List<Long> defIds = StreamUtils.toList(flowDefinitions, FlowDefinition::getId);
List<FlowNode> flowNodes = flowNodeMapper.selectList(new LambdaQueryWrapper<FlowNode>().in(FlowNode::getDefinitionId, defIds));
List<FlowSkip> flowSkips = flowSkipMapper.selectList(new LambdaQueryWrapper<FlowSkip>().in(FlowSkip::getDefinitionId, defIds));

View File

@@ -232,6 +232,9 @@ public class FlwTaskServiceImpl implements IFlwTaskService {
FlowParams flowParams = FlowParams.build()
.handler(completeTaskBo.getHandler())
.variable(variables)
.ignore(Convert.toBool(variables.getOrDefault(VAR_IGNORE, false)))
.ignoreDepute(Convert.toBool(variables.getOrDefault(VAR_IGNORE_DEPUTE, false)))
.ignoreCooperate(Convert.toBool(variables.getOrDefault(VAR_IGNORE_COOPERATE, false)))
.skipType(SkipType.PASS.getKey())
.message(completeTaskBo.getMessage())
.flowStatus(BusinessStatusEnum.WAITING.getStatus())

View File

@@ -27,7 +27,10 @@
d.flow_code,
d.form_custom,
d.category,
COALESCE(t.form_path, d.form_path) as form_path,
COALESCE(
NULLIF(TRIM(t.form_path), ''),
NULLIF(TRIM(d.form_path), '')
) AS form_path,
d.version,
uu.processed_by,
uu.type,

View File

@@ -99,7 +99,7 @@ services:
network_mode: "host"
ruoyi-server1:
image: ruoyi/ruoyi-server:5.5.1
image: ruoyi/ruoyi-server:5.5.2
container_name: ruoyi-server1
environment:
# 时区上海
@@ -115,7 +115,7 @@ services:
network_mode: "host"
ruoyi-server2:
image: ruoyi/ruoyi-server:5.5.1
image: ruoyi/ruoyi-server:5.5.2
container_name: ruoyi-server2
environment:
# 时区上海
@@ -131,7 +131,7 @@ services:
network_mode: "host"
ruoyi-monitor-admin:
image: ruoyi/ruoyi-monitor-admin:5.5.1
image: ruoyi/ruoyi-monitor-admin:5.5.2
container_name: ruoyi-monitor-admin
environment:
# 时区上海
@@ -143,7 +143,7 @@ services:
network_mode: "host"
ruoyi-snailjob-server:
image: ruoyi/ruoyi-snailjob-server:5.5.1
image: ruoyi/ruoyi-snailjob-server:5.5.2
container_name: ruoyi-snailjob-server
environment:
# 时区上海

View File

@@ -106,11 +106,6 @@ http {
proxy_buffering off;
# 禁用代理缓存
proxy_cache off;
# 按 IP 限制连接数(防 CC 攻击) 小型站10~20 就够 中型站50~100
limit_conn perip 20;
# 按 Server 限制总并发连接数 根据服务器的最大并发处理能力来定 太小会限制合法用户访问,太大会占满服务器资源
limit_conn perserver 500;
proxy_pass http://server/;
}

View File

@@ -738,7 +738,7 @@ CREATE TABLE sj_retry_summary
id number GENERATED ALWAYS AS IDENTITY,
namespace_id varchar2(64) DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' NULL,
group_name varchar2(64) DEFAULT '' NULL,
scene_name varchar2(50) DEFAULT '' NULL,
scene_name varchar2(64) DEFAULT '' NULL,
trigger_at date DEFAULT CURRENT_TIMESTAMP NOT NULL,
running_num number DEFAULT 0 NOT NULL,
finish_num number DEFAULT 0 NOT NULL,

View File

@@ -257,7 +257,7 @@ comment on column sys_user.phonenumber is '手机号码';
comment on column sys_user.sex is '用户性别0男 1女 2未知';
comment on column sys_user.avatar is '头像路径';
comment on column sys_user.password is '密码';
comment on column sys_user.status is '号状态0正常 1停用';
comment on column sys_user.status is '号状态0正常 1停用';
comment on column sys_user.del_flag is '删除标志0代表存在 1代表删除';
comment on column sys_user.login_ip is '最后登录IP';
comment on column sys_user.login_date is '最后登录时间';

View File

@@ -684,7 +684,7 @@ CREATE TABLE sj_retry_summary
id bigserial PRIMARY KEY,
namespace_id varchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
group_name varchar(64) NOT NULL DEFAULT '',
scene_name varchar(50) NOT NULL DEFAULT '',
scene_name varchar(64) NOT NULL DEFAULT '',
trigger_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
running_num int NOT NULL DEFAULT 0,
finish_num int NOT NULL DEFAULT 0,

View File

@@ -258,7 +258,7 @@ comment on column sys_user.phonenumber is '手机号码';
comment on column sys_user.sex is '用户性别0男 1女 2未知';
comment on column sys_user.avatar is '头像地址';
comment on column sys_user.password is '密码';
comment on column sys_user.status is '号状态0正常 1停用';
comment on column sys_user.status is '号状态0正常 1停用';
comment on column sys_user.del_flag is '删除标志0代表存在 1代表删除';
comment on column sys_user.login_ip is '最后登陆IP';
comment on column sys_user.login_date is '最后登陆时间';

View File

@@ -423,7 +423,7 @@ CREATE TABLE `sj_retry_summary`
`id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
`namespace_id` VARCHAR(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a' COMMENT '命名空间id',
`group_name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '组名称',
`scene_name` VARCHAR(50) NOT NULL DEFAULT '' COMMENT '场景名称',
`scene_name` VARCHAR(64) NOT NULL DEFAULT '' COMMENT '场景名称',
`trigger_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '统计时间',
`running_num` int NOT NULL DEFAULT 0 COMMENT '重试中-日志数量',
`finish_num` int NOT NULL DEFAULT 0 COMMENT '重试完成-日志数量',

View File

@@ -148,7 +148,7 @@ create table sys_user (
sex char(1) default '0' comment '用户性别0男 1女 2未知',
avatar bigint(20) comment '头像地址',
password varchar(100) default '' comment '密码',
status char(1) default '0' comment '号状态0正常 1停用',
status char(1) default '0' comment '号状态0正常 1停用',
del_flag char(1) default '0' comment '删除标志0代表存在 1代表删除',
login_ip varchar(128) default '' comment '最后登录IP',
login_date datetime comment '最后登录时间',

View File

@@ -2248,7 +2248,7 @@ CREATE TABLE sj_retry_summary
id bigint NOT NULL PRIMARY KEY IDENTITY,
namespace_id nvarchar(64) NOT NULL DEFAULT '764d604ec6fc45f68cd92514c40e9e1a',
group_name nvarchar(64) NOT NULL DEFAULT '',
scene_name nvarchar(50) NOT NULL DEFAULT '',
scene_name nvarchar(64) NOT NULL DEFAULT '',
trigger_at datetime2 NOT NULL DEFAULT CURRENT_TIMESTAMP,
running_num int NOT NULL DEFAULT 0,
finish_num int NOT NULL DEFAULT 0,

View File

@@ -2802,7 +2802,7 @@ EXEC sys.sp_addextendedproperty
'COLUMN', N'password'
GO
EXEC sys.sp_addextendedproperty
'MS_Description', N'号状态0正常 1停用' ,
'MS_Description', N'号状态0正常 1停用' ,
'SCHEMA', N'dbo',
'TABLE', N'sys_user',
'COLUMN', N'status'