Compare commits

...

14 Commits

36 changed files with 863 additions and 292 deletions

View File

@@ -1,3 +1,35 @@
## [v2.7.4](https://github.com/continew-org/continew-starter/compare/v2.7.3...v2.7.4) (2024-11-18)
### 💎 功能优化
- 【core】增加 + 号字符串/字符常量 ([464b87c](https://github.com/continew-org/continew-starter/commit/464b87c9c7789bc142538bc146ecfe4358c12a50))
- 【core】移除多余的校验工具类 ([fd9d2bb](https://github.com/continew-org/continew-starter/commit/fd9d2bb370caef4e9f9e3874e113c381ab4e5eb9))
### 🐛 问题修复
- 【extension/crud】修复新增响应结构 BaseIdResp 无法被继承问题 ([27ce092](https://github.com/continew-org/continew-starter/commit/27ce092b796a66f1c59d8715cf5648edab9efa65))
- 【json/jackson】修复 BaseEnum 反序列化数据类型仅支持数值的问题 ([b11013e](https://github.com/continew-org/continew-starter/commit/b11013ee80cb00022890d950ff7f666909de2082))
## [v2.7.3](https://github.com/continew-org/continew-starter/compare/v2.7.2...v2.7.3) (2024-11-15)
### ✨ 新特性
- 【cache/redisson】RedisUtils 新增 ZSet 相关方法 ([56edcee](https://github.com/continew-org/continew-starter/commit/56edceec7e7c61bbd06a1995c2351441302ac969))
- 【core】新增枚举校验器 EnumValue ([c7bee00](https://github.com/continew-org/continew-starter/commit/c7bee0033ef794784b9c9fd39f61a245abff0c62))
### 💎 功能优化
- 【extension/crud】查询详情命名调整GET -> DETAIL增加详情权限校验 ([4b77d5c](https://github.com/continew-org/continew-starter/commit/4b77d5cb3ff93e7d8d207196948352294c6cdcc6))
- 【core】拆分字符串常量和字符常量 ([2e9079a](https://github.com/continew-org/continew-starter/commit/2e9079a909db8df57ed7de49c95d9daeb9616f4a))
### 🐛 问题修复
- 【data/mp】修复普通枚举类型处理错误 ([efb84c9](https://github.com/continew-org/continew-starter/commit/efb84c936f1012c3ac4b6264599f6fb1f5ae5f97))
### 📦 依赖升级
- CosID 2.9.8 => 2.9.9 ([e3433be](https://github.com/continew-org/continew-starter/commit/e3433bed01e9bccc1179c04acc82df843434d9af))
## [v2.7.2](https://github.com/continew-org/continew-starter/compare/v2.7.1...v2.7.2) (2024-11-12)
### ✨ 新特性

View File

@@ -16,8 +16,8 @@
package top.continew.starter.apidoc.handler;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.type.CollectionType;
import com.fasterxml.jackson.databind.type.SimpleType;
import io.swagger.v3.core.converter.AnnotatedType;
@@ -52,7 +52,7 @@ public class BaseEnumParameterHandler implements ParameterCustomizer, PropertyCu
return parameterModel;
}
String description = parameterModel.getDescription();
if (StrUtil.contains(description, "color:red")) {
if (CharSequenceUtil.contains(description, "color:red")) {
return parameterModel;
}
// 自定义枚举描述并封装参数配置

View File

@@ -194,6 +194,167 @@ public class RedisUtils {
return CLIENT.getKeys().getKeysStreamByPattern(pattern).toList();
}
/**
* 添加元素到 ZSet 中
*
* @param key 键
* @param value 值
* @param score 分数
* @return true添加成功false添加失败
* @since 2.7.3
*/
public static <T> boolean zAdd(String key, T value, double score) {
RScoredSortedSet<T> zSet = CLIENT.getScoredSortedSet(key);
return zSet.add(score, value);
}
/**
* 查询 ZSet 中指定元素的分数
*
* @param key 键
* @param value 值
* @return 分数null 表示元素不存在)
* @since 2.7.3
*/
public static <T> Double zScore(String key, T value) {
RScoredSortedSet<T> zSet = CLIENT.getScoredSortedSet(key);
return zSet.getScore(value);
}
/**
* 查询 ZSet 中指定元素的排名
*
* @param key 键
* @param value 值
* @return 排名(从 0 开始null 表示元素不存在)
* @since 2.7.3
*/
public static <T> Integer zRank(String key, T value) {
RScoredSortedSet<T> zSet = CLIENT.getScoredSortedSet(key);
return zSet.rank(value);
}
/**
* 查询 ZSet 中的元素个数
*
* @param key 键
* @return 元素个数
* @since 2.7.3
*/
public static <T> int zSize(String key) {
RScoredSortedSet<T> zSet = CLIENT.getScoredSortedSet(key);
return zSet.size();
}
/**
* 从 ZSet 中删除指定元素
*
* @param key 键
* @param value 值
* @return true删除成功false删除失败
* @since 2.7.3
*/
public static <T> boolean zRemove(String key, T value) {
RScoredSortedSet<T> zSet = CLIENT.getScoredSortedSet(key);
return zSet.remove(value);
}
/**
* 删除 ZSet 中指定分数范围内的元素
*
* @param key 键
* @param min 最小分数(包含)
* @param max 最大分数(包含)
* @return 删除的元素个数
* @since 2.7.3
*/
public static <T> int zRemoveRangeByScore(String key, double min, double max) {
RScoredSortedSet<T> zSet = CLIENT.getScoredSortedSet(key);
return zSet.removeRangeByScore(min, true, max, true);
}
/**
* 删除 ZSet 中指定排名范围内的元素
*
* <p>
* 索引从 0 开始。<code>-1<code> 表示最高分,<code>-2<code> 表示第二高分。
* </p>
*
* @param key 键
* @param startIndex 起始索引
* @param endIndex 结束索引
* @return 删除的元素个数
* @since 2.7.3
*/
public static <T> int zRemoveRangeByRank(String key, int startIndex, int endIndex) {
RScoredSortedSet<T> zSet = CLIENT.getScoredSortedSet(key);
return zSet.removeRangeByRank(startIndex, endIndex);
}
/**
* 根据分数范围查询 ZSet 中的元素列表
*
* @param key 键
* @param min 最小分数(包含)
* @param max 最大分数(包含)
* @return 元素列表
* @since 2.7.3
*/
public static <T> Collection<T> zRangeByScore(String key, double min, double max) {
RScoredSortedSet<T> zSet = CLIENT.getScoredSortedSet(key);
return zSet.valueRange(min, true, max, true);
}
/**
* 根据分数范围查询 ZSet 中的元素列表
*
* @param key 键
* @param min 最小分数(包含)
* @param max 最大分数(包含)
* @param offset 偏移量
* @param count 数量
* @return 元素列表
* @since 2.7.3
*/
public static <T> Collection<T> zRangeByScore(String key, double min, double max, int offset, int count) {
RScoredSortedSet<T> zSet = CLIENT.getScoredSortedSet(key);
return zSet.valueRange(min, true, max, true, offset, count);
}
/**
* 根据分数范围查询 ZSet 中的元素个数
*
* @param key 键
* @param min 最小分数(包含)
* @param max 最大分数(包含)
* @return 元素个数
* @since 2.7.3
*/
public static <T> int zCountRangeByScore(String key, double min, double max) {
RScoredSortedSet<T> zSet = CLIENT.getScoredSortedSet(key);
return zSet.count(min, true, max, true);
}
/**
* 计算 ZSet 中多个元素的分数之和
*
* @param key 键
* @param values 值列表
* @return 分数之和
* @since 2.7.3
*/
public static <T> double zSum(String key, Collection<T> values) {
RScoredSortedSet<T> zSet = CLIENT.getScoredSortedSet(key);
double sum = 0;
for (T value : values) {
Double score = zSet.getScore(value);
if (score != null) {
sum += score;
}
}
return sum;
}
/**
* 限流
*

View File

@@ -0,0 +1,171 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.starter.core.constant;
/**
* 字符相关常量
*
* @author looly<a href="https://gitee.com/dromara/hutool">Hutool</a>
* @author Charles7c
* @see cn.hutool.core.text.CharPool
* @since 2.7.3
*/
public class CharConstants {
/**
* 空格符 {@code ' '}
*/
public static final char SPACE = ' ';
/**
* 制表符 {@code '\t'}
*/
public static final char TAB = ' ';
/**
* 点 {@code '.'}
*/
public static final char DOT = '.';
/**
* 逗号 {@code ','}
*/
public static final char COMMA = ',';
/**
* 中文逗号 {@code ''}
*/
public static final char CHINESE_COMMA = '';
/**
* 冒号 {@code ':'}
*/
public static final char COLON = ':';
/**
* 分号 {@code ';'}
*/
public static final char SEMICOLON = ';';
/**
* 问号 {@code '?'}
*/
public static final char QUESTION_MARK = '?';
/**
* 下划线 {@code '_'}
*/
public static final char UNDERLINE = '_';
/**
* 减号(连接符) {@code '-'}
*/
public static final char DASHED = '-';
/**
* 加号 {@code '+'}
*/
public static final char PLUS = '+';
/**
* 等号 {@code '='}
*/
public static final char EQUALS = '=';
/**
* 星号 {@code '*'}
*/
public static final char ASTERISK = '*';
/**
* 斜杠 {@code '/'}
*/
public static final char SLASH = '/';
/**
* 反斜杠 {@code '\\'}
*/
public static final char BACKSLASH = '\\';
/**
* 管道符 {@code '|'}
*/
public static final char PIPE = '|';
/**
* 艾特 {@code '@'}
*/
public static final char AT = '@';
/**
* 与符号 {@code '&'}
*/
public static final char AMP = '&';
/**
* 花括号(左) <code>'{'</code>
*/
public static final char DELIM_START = '{';
/**
* 花括号(右) <code>'}'</code>
*/
public static final char DELIM_END = '}';
/**
* 中括号(左) {@code '['}
*/
public static final char BRACKET_START = '[';
/**
* 中括号(右) {@code ']'}
*/
public static final char BRACKET_END = ']';
/**
* 圆括号(左) {@code '('}
*/
public static final char ROUND_BRACKET_START = '(';
/**
* 圆括号(右) {@code ')'}
*/
public static final char ROUND_BRACKET_END = ')';
/**
* 双引号 {@code '"'}
*/
public static final char DOUBLE_QUOTES = '"';
/**
* 单引号 {@code '\''}
*/
public static final char SINGLE_QUOTE = '\'';
/**
* 回车符 {@code '\r'}
*/
public static final char CR = '\r';
/**
* 换行符 {@code '\n'}
*/
public static final char LF = '\n';
private CharConstants() {
}
}

View File

@@ -16,253 +16,143 @@
package top.continew.starter.core.constant;
import cn.hutool.core.text.CharPool;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.XmlUtil;
/**
* 字符串相关常量
*
* @author looly
* @author looly<a href="https://gitee.com/dromara/hutool">Hutool</a>
* @author Charles7c
* @see cn.hutool.core.text.StrPool
* @since 1.0.0
*/
public class StringConstants {
/**
* 字符常量:空格符 {@code ' '}
*/
public static final char C_SPACE = CharPool.SPACE;
/**
* 字符常量:制表符 {@code '\t'}
*/
public static final char C_TAB = CharPool.TAB;
/**
* 字符常量:点 {@code '.'}
*/
public static final char C_DOT = CharPool.DOT;
/**
* 字符常量:斜杠 {@code '/'}
*/
public static final char C_SLASH = CharPool.SLASH;
/**
* 字符常量:反斜杠 {@code '\\'}
*/
public static final char C_BACKSLASH = CharPool.BACKSLASH;
/**
* 字符常量:回车符 {@code '\r'}
*/
public static final char C_CR = CharPool.CR;
/**
* 字符常量:换行符 {@code '\n'}
*/
public static final char C_LF = CharPool.LF;
/**
* 字符常量:下划线 {@code '_'}
*/
public static final char C_UNDERLINE = CharPool.UNDERLINE;
/**
* 字符常量:逗号 {@code ','}
*/
public static final char C_COMMA = CharPool.COMMA;
/**
* 字符常量:花括号(左) <code>'{'</code>
*/
public static final char C_DELIM_START = CharPool.DELIM_START;
/**
* 字符常量:花括号(右) <code>'}'</code>
*/
public static final char C_DELIM_END = CharPool.DELIM_END;
/**
* 字符常量:中括号(左) {@code '['}
*/
public static final char C_BRACKET_START = CharPool.BRACKET_START;
/**
* 字符常量:中括号(右) {@code ']'}
*/
public static final char C_BRACKET_END = CharPool.BRACKET_END;
/**
* 字符常量:冒号 {@code ':'}
*/
public static final char C_COLON = CharPool.COLON;
/**
* 字符常量:艾特 {@code '@'}
*/
public static final char C_AT = CharPool.AT;
/**
* 字符常量:星号 {@code '*'}
*/
public static final char C_ASTERISK = '*';
/**
* 字符串常量:制表符 {@code "\t"}
*/
public static final String TAB = StrPool.TAB;
/**
* 字符串常量:点 {@code "."}
*/
public static final String DOT = StrPool.DOT;
/**
* 字符串常量:双点 {@code ".."} <br> 用途:作为指向上级文件夹的路径,如:{@code "../path"}
*/
public static final String DOUBLE_DOT = StrPool.DOUBLE_DOT;
/**
* 字符串常量:斜杠 {@code "/"}
*/
public static final String SLASH = StrPool.SLASH;
/**
* 字符串常量:反斜杠 {@code "\\"}
*/
public static final String BACKSLASH = StrPool.BACKSLASH;
/**
* 字符串常量:回车符 {@code "\r"} <br> 解释:该字符常用于表示 Linux 系统和 MacOS 系统下的文本换行
*/
public static final String CR = StrPool.CR;
/**
* 字符串常量:换行符 {@code "\n"}
*/
public static final String LF = StrPool.LF;
/**
* 字符串常量Windows 换行 {@code "\r\n"} <br> 解释:该字符串常用于表示 Windows 系统下的文本换行
*/
public static final String CRLF = StrPool.CRLF;
/**
* 字符串常量:下划线 {@code "_"}
*/
public static final String UNDERLINE = StrPool.UNDERLINE;
/**
* 字符串常量:减号(连接符) {@code "-"}
*/
public static final String DASHED = StrPool.DASHED;
/**
* 字符串常量:逗号 {@code ","}
*/
public static final String COMMA = StrPool.COMMA;
/**
* 字符串常量:花括号(左) <code>"{"</code>
*/
public static final String DELIM_START = StrPool.DELIM_START;
/**
* 字符串常量:花括号(右) <code>"}"</code>
*/
public static final String DELIM_END = StrPool.DELIM_END;
/**
* 字符串常量:中括号(左) {@code "["}
*/
public static final String BRACKET_START = StrPool.BRACKET_START;
/**
* 字符串常量:中括号(右) {@code "]"}
*/
public static final String BRACKET_END = StrPool.BRACKET_END;
/**
* 字符串常量:冒号 {@code ":"}
*/
public static final String COLON = StrPool.COLON;
/**
* 字符串常量:艾特 {@code "@"}
*/
public static final String AT = StrPool.AT;
/**
* 字符串常量HTML 不间断空格转义 {@code "&nbsp;" -> " "}
*/
public static final String HTML_NBSP = XmlUtil.NBSP;
/**
* 字符串常量HTML And 符转义 {@code "&amp;" -> "&"}
*/
public static final String HTML_AMP = XmlUtil.AMP;
/**
* 字符串常量HTML 双引号转义 {@code "&quot;" -> "\""}
*/
public static final String HTML_QUOTE = XmlUtil.QUOTE;
/**
* 字符串常量HTML 单引号转义 {@code "&apos" -> "'"}
*/
public static final String HTML_APOS = XmlUtil.APOS;
/**
* 字符串常量HTML 小于号转义 {@code "&lt;" -> "<"}
*/
public static final String HTML_LT = XmlUtil.LT;
/**
* 字符串常量HTML 大于号转义 {@code "&gt;" -> ">"}
*/
public static final String HTML_GT = XmlUtil.GT;
/**
* 字符串常量:空 JSON {@code "{}"}
*/
public static final String EMPTY_JSON = StrPool.EMPTY_JSON;
/**
* 空字符串
* 字符 {@code ""}
*/
public static final String EMPTY = "";
/**
* 空格
* 空格符 {@code " "}
*/
public static final String SPACE = " ";
/**
* 分号
* 制表符 {@code "\t"}
*/
public static final String TAB = " ";
/**
* 空 JSON {@code "{}"}
*/
public static final String EMPTY_JSON = "{}";
/**
* 点 {@code "."}
*/
public static final String DOT = ".";
/**
* 双点 {@code ".."}
* <p>
* 作为指向上级文件夹的路径,如:{@code "../path"}
* </p>
*/
public static final String DOUBLE_DOT = "..";
/**
* 逗号 {@code ","}
*/
public static final String COMMA = ",";
/**
* 中文逗号 {@code ""}
*/
public static final String CHINESE_COMMA = "";
/**
* 冒号 {@code ":"}
*/
public static final String COLON = ":";
/**
* 分号 {@code ";"}
*/
public static final String SEMICOLON = ";";
/**
* 星号
*/
public static final String ASTERISK = "*";
/**
* 问号
* 问号 {@code "?"}
*/
public static final String QUESTION_MARK = "?";
/**
* 管道符
* 下划线 {@code "_"}
*/
public static final String UNDERLINE = "_";
/**
* 减号(连接符) {@code "-"}
*/
public static final String DASHED = "-";
/**
* 加号 {@code "+"}
*/
public static final String PLUS = "+";
/**
* 等号 {@code "="}
*/
public static final String EQUALS = "=";
/**
* 星号 {@code "*"}
*/
public static final String ASTERISK = "*";
/**
* 斜杠 {@code "/"}
*/
public static final String SLASH = "/";
/**
* 反斜杠 {@code "\\"}
*/
public static final String BACKSLASH = "\\";
/**
* 管道符 {@code "|"}
*/
public static final String PIPE = "|";
/**
* 中文逗号
* 艾特 {@code "@"}
*/
public static final String CHINESE_COMMA = "";
public static final String AT = "@";
/**
* 与符号 {@code "&"}
*/
public static final String AMP = "&";
/**
* 花括号(左) <code>"{"</code>
*/
public static final String DELIM_START = "{";
/**
* 花括号(右) <code>"}"</code>
*/
public static final String DELIM_END = "}";
/**
* 中括号(左) {@code "["}
*/
public static final String BRACKET_START = "[";
/**
* 中括号(右) {@code "]"}
*/
public static final String BRACKET_END = "]";
/**
* 圆括号(左) {@code "("}
@@ -275,20 +165,65 @@ public class StringConstants {
public static final String ROUND_BRACKET_END = ")";
/**
* 等号(=
* 双引号 {@code "\""}
*/
public static final String EQUALS = "=";
public static final String DOUBLE_QUOTES = "\"";
/**
* 路径模式
* 单引号 {@code "'"}
*/
public static final String SINGLE_QUOTE = "'";
/**
* 回车符 {@code "\r"}
*/
public static final String CR = "\r";
/**
* 换行符 {@code "\n"}
*/
public static final String LF = "\n";
/**
* 路径模式 {@code "/**"}
*/
public static final String PATH_PATTERN = "/**";
/**
* 路径模式(仅匹配当前目录)
* 路径模式(仅匹配当前目录) {@code "/*"}
*/
public static final String PATH_PATTERN_CURRENT_DIR = "/*";
/**
* HTML 不间断空格转义 {@code "&nbsp;" -> " "}
*/
public static final String HTML_NBSP = "&nbsp;";
/**
* HTML And 符转义 {@code "&amp;" -> "&"}
*/
public static final String HTML_AMP = "&amp;";
/**
* HTML 双引号转义 {@code "&quot;" -> "\""}
*/
public static final String HTML_QUOTE = "&quot;";
/**
* HTML 单引号转义 {@code "&apos" -> "'"}
*/
public static final String HTML_APOS = "&apos;";
/**
* HTML 小于号转义 {@code "&lt;" -> "<"}
*/
public static final String HTML_LT = "&lt;";
/**
* HTML 大于号转义 {@code "&gt;" -> ">"}
*/
public static final String HTML_GT = "&gt;";
private StringConstants() {
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package top.continew.starter.core.util.validate;
package top.continew.starter.core.validation;
import cn.hutool.core.text.CharSequenceUtil;
import top.continew.starter.core.constant.StringConstants;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package top.continew.starter.core.util.validate;
package top.continew.starter.core.validation;
import cn.hutool.core.text.CharSequenceUtil;
import top.continew.starter.core.exception.BadRequestException;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package top.continew.starter.core.util.validate;
package top.continew.starter.core.validation;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
@@ -22,8 +22,6 @@ import cn.hutool.core.util.ReflectUtil;
import cn.hutool.extra.spring.SpringUtil;
import jakarta.validation.ConstraintViolation;
import jakarta.validation.ConstraintViolationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Set;
import java.util.function.BooleanSupplier;
@@ -35,7 +33,7 @@ import java.util.function.BooleanSupplier;
* @since 1.0.0
*/
public class Validator {
private static final Logger log = LoggerFactory.getLogger(Validator.class);
private static final jakarta.validation.Validator VALIDATOR = SpringUtil
.getBean(jakarta.validation.Validator.class);
@@ -181,7 +179,6 @@ public class Validator {
*/
protected static void throwIf(boolean condition, String message, Class<? extends RuntimeException> exceptionType) {
if (condition) {
log.error(message);
throw ReflectUtil.newInstance(exceptionType, message);
}
}
@@ -197,7 +194,6 @@ public class Validator {
String message,
Class<? extends RuntimeException> exceptionType) {
if (null != conditionSupplier && conditionSupplier.getAsBoolean()) {
log.error(message);
throw ReflectUtil.newInstance(exceptionType, message);
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.starter.core.validation.constraints;
import jakarta.validation.Constraint;
import jakarta.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
/**
* 枚举校验注解
*
* <p>
* {@code @EnumValue(value = XxxEnum.class, message = "参数值非法")} <br />
* {@code @EnumValue(enumValues = {"F", "M"} ,message = "性别只允许为F或M")}
* </p>
*
* @author Jasmine
* @author Charles7c
* @since 2.7.3
*/
@Documented
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EnumValueValidator.class)
public @interface EnumValue {
/**
* 枚举类
*
* @return 枚举类
*/
Class<? extends Enum> value() default Enum.class;
/**
* 枚举值
*
* @return 枚举值
*/
String[] enumValues() default {};
/**
* 获取枚举值的方法名
*
* @return 获取枚举值的方法名
*/
String method() default "";
/**
* 提示消息
*
* @return 提示消息
*/
String message() default "参数值非法";
/**
* 分组
*
* @return 分组
*/
Class<?>[] groups() default {};
/**
* 负载
*
* @return 负载
*/
Class<? extends Payload>[] payload() default {};
}

View File

@@ -0,0 +1,97 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.starter.core.validation.constraints;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.text.CharSequenceUtil;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.function.Function;
/**
* 枚举校验注解校验器
*
* @author Charles7c
* @author Jasmine
* @since 2.7.3
*/
public class EnumValueValidator implements ConstraintValidator<EnumValue, Object> {
private static final Logger log = LoggerFactory.getLogger(EnumValueValidator.class);
private Class<? extends Enum> enumClass;
private String[] enumValues;
private String enumMethod;
@Override
public void initialize(EnumValue enumValue) {
this.enumClass = enumValue.value();
this.enumValues = enumValue.enumValues();
this.enumMethod = enumValue.method();
}
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
// 优先校验 enumValues
if (enumValues.length > 0) {
return Arrays.asList(enumValues).contains(Convert.toStr(value));
}
Enum[] enumConstants = enumClass.getEnumConstants();
if (enumConstants.length == 0) {
return false;
}
if (CharSequenceUtil.isBlank(enumMethod)) {
return findEnumValue(enumConstants, Enum::toString, Convert.toStr(value));
}
try {
// 枚举类指定了方法名,则调用指定方法获取枚举值
Method method = enumClass.getMethod(enumMethod);
for (Enum enumConstant : enumConstants) {
if (Convert.toStr(method.invoke(enumConstant)).equals(Convert.toStr(value))) {
return true;
}
}
} catch (Exception e) {
log.error("An error occurred while validating the enum value, please check the @EnumValue parameter configuration.", e);
}
return false;
}
/**
* 遍历枚举类,判断是否包含指定值
*
* @param enumConstants 枚举类数组
* @param function 获取枚举值的函数
* @param value 待校验的值
* @return 是否包含指定值
*/
private boolean findEnumValue(Enum[] enumConstants, Function<Enum, Object> function, Object value) {
for (Enum enumConstant : enumConstants) {
if (function.apply(enumConstant).equals(value)) {
return true;
}
}
return false;
}
}

View File

@@ -16,7 +16,7 @@
package top.continew.starter.data.mf.datapermission;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.text.CharSequenceUtil;
import com.mybatisflex.core.dialect.impl.CommonsDialectImpl;
import com.mybatisflex.core.query.QueryWrapper;
@@ -152,7 +152,7 @@ public class DataPermissionDialect extends CommonsDialectImpl {
* @return 带表别名字段
*/
private String buildColumn(String tableAlias, String columnName) {
if (StrUtil.isNotEmpty(tableAlias)) {
if (CharSequenceUtil.isNotEmpty(tableAlias)) {
return "%s.%s".formatted(tableAlias, columnName);
}
return columnName;

View File

@@ -26,7 +26,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Sort;
import top.continew.starter.core.exception.BadRequestException;
import top.continew.starter.core.util.ReflectUtils;
import top.continew.starter.core.util.validate.ValidationUtils;
import top.continew.starter.core.validation.ValidationUtils;
import top.continew.starter.data.core.annotation.Query;
import top.continew.starter.data.core.annotation.QueryIgnore;
import top.continew.starter.data.core.enums.QueryType;

View File

@@ -19,7 +19,10 @@ package top.continew.starter.data.mp.autoconfigure;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusPropertiesCustomizer;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.*;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import jakarta.annotation.PostConstruct;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
@@ -36,7 +39,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
import top.continew.starter.core.constant.PropertiesConstants;
import top.continew.starter.core.util.GeneralPropertySourceFactory;
import top.continew.starter.data.mp.autoconfigure.idgenerator.MyBatisPlusIdGeneratorConfiguration;
import top.continew.starter.data.mp.handler.MybatisBaseEnumTypeHandler;
import top.continew.starter.data.mp.handler.CompositeBaseEnumTypeHandler;
import java.util.Map;
@@ -63,7 +66,8 @@ public class MybatisPlusAutoConfiguration {
*/
@Bean
public MybatisPlusPropertiesCustomizer mybatisPlusPropertiesCustomizer() {
return properties -> properties.getConfiguration().setDefaultEnumTypeHandler(MybatisBaseEnumTypeHandler.class);
return properties -> properties.getConfiguration()
.setDefaultEnumTypeHandler(CompositeBaseEnumTypeHandler.class);
}
/**

View File

@@ -0,0 +1,105 @@
/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
* <p>
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* <p>
* http://www.gnu.org/licenses/lgpl.html
* <p>
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.continew.starter.data.mp.handler;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import org.apache.ibatis.type.EnumTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeException;
import org.apache.ibatis.type.TypeHandler;
import java.lang.reflect.Constructor;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 复合枚举类型处理器(扩展 BaseEnum 支持)
*
* @author miemie<a href="https://gitee.com/baomidou/mybatis-plus">MyBatis Plus</a>
* @author Charles7c
* @see com.baomidou.mybatisplus.core.handlers.CompositeEnumTypeHandler
* @since 2.7.3
*/
public class CompositeBaseEnumTypeHandler<E extends Enum<E>> implements TypeHandler<E> {
private static final Map<Class<?>, Boolean> MP_ENUM_CACHE = new ConcurrentHashMap<>();
private static Class<? extends TypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;
private final TypeHandler<E> delegate;
public CompositeBaseEnumTypeHandler(Class<E> enumClassType) {
if (enumClassType == null) {
throw new IllegalArgumentException("Type argument cannot be null");
}
if (Boolean.TRUE.equals(CollectionUtils
.computeIfAbsent(MP_ENUM_CACHE, enumClassType, MybatisBaseEnumTypeHandler::isMpEnums))) {
delegate = new MybatisBaseEnumTypeHandler<>(enumClassType);
} else {
delegate = getInstance(enumClassType, defaultEnumTypeHandler);
}
}
public static void setDefaultEnumTypeHandler(Class<? extends TypeHandler> defaultEnumTypeHandler) {
if (defaultEnumTypeHandler != null && !MybatisBaseEnumTypeHandler.class
.isAssignableFrom(defaultEnumTypeHandler)) {
CompositeBaseEnumTypeHandler.defaultEnumTypeHandler = defaultEnumTypeHandler;
}
}
@Override
public void setParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
delegate.setParameter(ps, i, parameter, jdbcType);
}
@Override
public E getResult(ResultSet rs, String columnName) throws SQLException {
return delegate.getResult(rs, columnName);
}
@Override
public E getResult(ResultSet rs, int columnIndex) throws SQLException {
return delegate.getResult(rs, columnIndex);
}
@Override
public E getResult(CallableStatement cs, int columnIndex) throws SQLException {
return delegate.getResult(cs, columnIndex);
}
@SuppressWarnings("unchecked")
public <T> TypeHandler<T> getInstance(Class<?> javaTypeClass, Class<?> typeHandlerClass) {
if (javaTypeClass != null) {
try {
Constructor<?> c = typeHandlerClass.getConstructor(Class.class);
return (TypeHandler<T>)c.newInstance(javaTypeClass);
} catch (NoSuchMethodException ignored) {
// ignored
} catch (Exception e) {
throw new TypeException("Failed invoking constructor for handler " + typeHandlerClass, e);
}
}
try {
Constructor<?> c = typeHandlerClass.getConstructor();
return (TypeHandler<T>)c.newInstance();
} catch (Exception e) {
throw new TypeException("Unable to find a usable constructor for " + typeHandlerClass, e);
}
}
}

View File

@@ -43,10 +43,11 @@ import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
/**
* 自定义枚举属性转换器
* 枚举类型处理器(扩展 BaseEnum 支持)
*
* @author hubin
* @author hubin<a href="https://gitee.com/baomidou/mybatis-plus">MyBatis Plus</a>
* @author Charles7c
* @see com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
* @since 2.4.0
*/
public class MybatisBaseEnumTypeHandler<E extends Enum<E>> extends BaseTypeHandler<E> {

View File

@@ -18,7 +18,7 @@ package top.continew.starter.data.mp.service.impl;
import cn.hutool.core.util.ClassUtil;
import top.continew.starter.core.util.ReflectUtils;
import top.continew.starter.core.util.validate.CheckUtils;
import top.continew.starter.core.validation.CheckUtils;
import top.continew.starter.data.mp.base.BaseMapper;
import top.continew.starter.data.mp.service.IService;

View File

@@ -26,7 +26,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Sort;
import top.continew.starter.core.exception.BadRequestException;
import top.continew.starter.core.util.ReflectUtils;
import top.continew.starter.core.util.validate.ValidationUtils;
import top.continew.starter.core.validation.ValidationUtils;
import top.continew.starter.data.core.annotation.Query;
import top.continew.starter.data.core.annotation.QueryIgnore;
import top.continew.starter.data.core.enums.QueryType;

View File

@@ -43,7 +43,7 @@
<properties>
<!-- 项目版本号 -->
<revision>2.7.2</revision>
<revision>2.7.4</revision>
<snail-job.version>1.1.2</snail-job.version>
<sa-token.version>1.39.0</sa-token.version>
<just-auth.version>1.16.6</just-auth.version>
@@ -53,7 +53,7 @@
<p6spy.version>3.9.1</p6spy.version>
<jetcache.version>2.7.6</jetcache.version>
<redisson.version>3.36.0</redisson.version>
<cosid.version>2.9.8</cosid.version>
<cosid.version>2.9.9</cosid.version>
<sms4j.version>3.3.3</sms4j.version>
<aj-captcha.version>1.3.0</aj-captcha.version>
<easy-captcha.version>1.6.2</easy-captcha.version>

View File

@@ -39,5 +39,5 @@ public @interface CrudRequestMapping {
/**
* API 列表
*/
Api[] api() default {Api.PAGE, Api.GET, Api.ADD, Api.UPDATE, Api.DELETE, Api.EXPORT};
Api[] api() default {Api.PAGE, Api.DETAIL, Api.ADD, Api.UPDATE, Api.DELETE, Api.EXPORT};
}

View File

@@ -17,7 +17,7 @@
package top.continew.starter.extension.crud.autoconfigure;
import cn.hutool.core.lang.tree.TreeNodeConfig;
import top.continew.starter.core.util.validate.CheckUtils;
import top.continew.starter.core.validation.CheckUtils;
import top.continew.starter.extension.crud.annotation.TreeField;
/**

View File

@@ -21,7 +21,7 @@ import cn.hutool.core.util.ArrayUtil;
import io.swagger.v3.oas.annotations.media.Schema;
import org.springframework.data.domain.Sort;
import top.continew.starter.core.constant.StringConstants;
import top.continew.starter.core.util.validate.ValidationUtils;
import top.continew.starter.core.validation.ValidationUtils;
import top.continew.starter.data.core.util.SqlInjectionUtils;
import java.io.Serial;

View File

@@ -20,7 +20,7 @@ import java.io.Serial;
import java.io.Serializable;
/**
* 请求基类
* 请求参数基类
*
* @author Charles7c
* @since 1.0.0

View File

@@ -42,27 +42,10 @@ public class BaseIdResp<T extends Serializable> implements Serializable {
this.id = id;
}
BaseIdResp(final T id) {
public BaseIdResp() {
}
public BaseIdResp(final T id) {
this.id = id;
}
public static <T extends Serializable> BaseIdRespBuilder<T> builder() {
return new BaseIdRespBuilder();
}
public static class BaseIdRespBuilder<T extends Serializable> {
private T id;
BaseIdRespBuilder() {
}
public BaseIdRespBuilder<T> id(final T id) {
this.id = id;
return this;
}
public BaseIdResp<T> build() {
return new BaseIdResp(this.id);
}
}
}

View File

@@ -29,7 +29,7 @@ import java.io.Serializable;
import java.time.LocalDateTime;
/**
* 响应基类
* 响应参数基类
*
* @author Charles7c
* @since 1.0.0

View File

@@ -111,8 +111,8 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q,
@Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
@ResponseBody
@GetMapping("/{id}")
public D get(@PathVariable("id") Long id) {
this.checkPermission(Api.LIST);
public D detail(@PathVariable("id") Long id) {
this.checkPermission(Api.DETAIL);
return baseService.get(id);
}
@@ -127,7 +127,7 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q,
@PostMapping
public BaseIdResp<Long> add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody C req) {
this.checkPermission(Api.ADD);
return BaseIdResp.<Long>builder().id(baseService.add(req)).build();
return new BaseIdResp<>(baseService.add(req));
}
/**

View File

@@ -29,12 +29,11 @@ import cn.hutool.extra.spring.SpringUtil;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.data.domain.Sort;
import org.springframework.transaction.annotation.Transactional;
import top.continew.starter.core.constant.StringConstants;
import top.continew.starter.core.util.ReflectUtils;
import top.continew.starter.core.util.validate.ValidationUtils;
import top.continew.starter.core.validation.ValidationUtils;
import top.continew.starter.data.mf.base.BaseMapper;
import top.continew.starter.data.mf.service.impl.ServiceImpl;
import top.continew.starter.data.mf.util.QueryWrapperHelper;

View File

@@ -111,8 +111,8 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q,
@Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
@ResponseBody
@GetMapping("/{id}")
public D get(@PathVariable("id") Long id) {
this.checkPermission(Api.LIST);
public D detail(@PathVariable("id") Long id) {
this.checkPermission(Api.DETAIL);
return baseService.get(id);
}
@@ -127,7 +127,7 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q,
@PostMapping
public BaseIdResp<Long> add(@Validated(ValidateGroup.Crud.Add.class) @RequestBody C req) {
this.checkPermission(Api.ADD);
return BaseIdResp.<Long>builder().id(baseService.add(req)).build();
return new BaseIdResp<>(baseService.add(req));
}
/**

View File

@@ -90,7 +90,7 @@ public interface BaseService<L, D, Q, C> {
/**
* 新增
*
* @param req 创建信息
* @param req 创建参数
* @return 自增 ID
*/
Long add(C req);
@@ -98,7 +98,7 @@ public interface BaseService<L, D, Q, C> {
/**
* 修改
*
* @param req 修改信息
* @param req 修改参数
* @param id ID
*/
void update(C req, Long id);

View File

@@ -37,8 +37,8 @@ import org.springframework.transaction.annotation.Transactional;
import top.continew.starter.core.constant.StringConstants;
import top.continew.starter.core.util.ClassUtils;
import top.continew.starter.core.util.ReflectUtils;
import top.continew.starter.core.util.validate.CheckUtils;
import top.continew.starter.core.util.validate.ValidationUtils;
import top.continew.starter.core.validation.CheckUtils;
import top.continew.starter.core.validation.ValidationUtils;
import top.continew.starter.data.mp.base.BaseMapper;
import top.continew.starter.data.mp.service.impl.ServiceImpl;
import top.continew.starter.data.mp.util.QueryWrapperHelper;

View File

@@ -16,6 +16,7 @@
package top.continew.starter.json.jackson.serializer;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ReflectUtil;
import com.fasterxml.jackson.core.JsonParser;
@@ -26,6 +27,7 @@ import top.continew.starter.core.enums.BaseEnum;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Objects;
/**
* 枚举接口 BaseEnum 反序列化器
@@ -66,7 +68,7 @@ public class BaseEnumDeserializer extends JsonDeserializer<BaseEnum> {
for (Object enumConstant : enumConstants) {
if (ClassUtil.isAssignable(BaseEnum.class, fieldTypeClass)) {
BaseEnum baseEnum = (BaseEnum)enumConstant;
if (baseEnum.getValue().equals(Integer.valueOf(value))) {
if (Objects.equals(Convert.toStr(baseEnum.getValue()), Convert.toStr(value))) {
return baseEnum;
}
}

View File

@@ -17,7 +17,7 @@
package top.continew.starter.messaging.mail.core;
import cn.hutool.core.map.MapUtil;
import top.continew.starter.core.util.validate.ValidationUtils;
import top.continew.starter.core.validation.ValidationUtils;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

View File

@@ -17,7 +17,7 @@
package top.continew.starter.messaging.mail.core;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import top.continew.starter.core.util.validate.ValidationUtils;
import top.continew.starter.core.validation.ValidationUtils;
/**
* 邮件配置

View File

@@ -18,7 +18,7 @@ package top.continew.starter.security.mask.annotation;
import com.fasterxml.jackson.annotation.JacksonAnnotationsInside;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import top.continew.starter.core.constant.StringConstants;
import top.continew.starter.core.constant.CharConstants;
import top.continew.starter.security.mask.core.JsonMaskSerializer;
import top.continew.starter.security.mask.enums.MaskType;
import top.continew.starter.security.mask.strategy.IMaskStrategy;
@@ -72,5 +72,5 @@ public @interface JsonMask {
/**
* 脱敏符号(默认:*
*/
char character() default StringConstants.C_ASTERISK;
char character() default CharConstants.ASTERISK;
}

View File

@@ -33,7 +33,7 @@ import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
import top.continew.starter.core.constant.PropertiesConstants;
import top.continew.starter.core.util.validate.CheckUtils;
import top.continew.starter.core.validation.CheckUtils;
import java.util.HashMap;
import java.util.List;

View File

@@ -18,7 +18,6 @@ package top.continew.starter.web.autoconfigure.mvc;
import org.springframework.core.convert.converter.Converter;
import top.continew.starter.core.enums.BaseEnum;
import top.continew.starter.core.util.validate.ValidationUtils;
import java.util.HashMap;
import java.util.Map;
@@ -42,8 +41,6 @@ public class BaseEnumConverter<T extends BaseEnum> implements Converter<String,
@Override
public T convert(String source) {
T t = enumMap.get(source);
ValidationUtils.throwIfNull(t, "枚举值非法:{}", source);
return t;
return enumMap.get(source);
}
}