mirror of
https://github.com/continew-org/continew-starter.git
synced 2025-11-12 03:01:27 +08:00
Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 99c9071eae | |||
| 73b066a88d | |||
| 2a5ace0033 | |||
| 93ab4e50cc | |||
| 6a6c559d2f | |||
| 16da470008 | |||
| f2a30e8b74 | |||
| 15f87068c6 | |||
| 3a0c3e02b0 | |||
| 3edf79cf3b | |||
| 6b3bc832de | |||
| 1d53eaa3c3 | |||
| 4e2096e37d | |||
| 8dcde6b84d | |||
| fd9d2bb370 | |||
| 464b87c9c7 | |||
| 27ce092b79 | |||
| b11013ee80 | |||
| 232624aace | |||
| 2e9079a909 | |||
| c7bee0033e | |||
| efb84c936f | |||
| 4b77d5cb3f | |||
| e3433bed01 | |||
| 56edceec7e |
45
CHANGELOG.md
45
CHANGELOG.md
@@ -1,3 +1,48 @@
|
||||
## [v2.7.5](https://github.com/continew-org/continew-starter/compare/v2.7.4...v2.7.5) (2024-12-06)
|
||||
|
||||
### 💎 功能优化
|
||||
|
||||
- 【extension/crud】重构 BaseController 内权限校验,BaseController => AbstractBaseController ([3edf79c](https://github.com/continew-org/continew-starter/commit/3edf79cf3bdba91010776c95902414fa7481c39e)) ([15f8706](https://github.com/continew-org/continew-starter/commit/15f87068c65e4afd83ee7020bde2707870012fb9)) ([16da470](https://github.com/continew-org/continew-starter/commit/16da470008d44ebf37cbe9e292ac8db8fc04ceb5)) ([2a5ace0](https://github.com/continew-org/continew-starter/commit/2a5ace003329422589af834431ac2bd8fa30ac28))
|
||||
- 【extension/crud】调整 BaseController、BaseService 到 crud-core 模块 ([3a0c3e0](https://github.com/continew-org/continew-starter/commit/3a0c3e02b03837789bfc433a335b062ec5dab511))
|
||||
- 【extension/crud】优化部分代码,ValidateGroup => CrudValidationGroup ([f2a30e8](https://github.com/continew-org/continew-starter/commit/f2a30e8b74b828644970be0b05920a32d6eb514a)) ([6a6c559](https://github.com/continew-org/continew-starter/commit/6a6c559d2f53f25888259a7e5d020281408aaa9f))
|
||||
- 【data】使用 Hutool 工具方法替换反射 API,以解决扫描问题 ([93ab4e5](https://github.com/continew-org/continew-starter/commit/93ab4e50cc1957ab772b31c9c433f9fc3d29da33))
|
||||
|
||||
### 🐛 问题修复
|
||||
|
||||
- 【data/mp】修复 Query 范围查询数组类型数据解析错误 ([6b3bc83](https://github.com/continew-org/continew-starter/commit/6b3bc832de25acdb2be418ad7626e9b6e234adde))
|
||||
|
||||
## [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)
|
||||
|
||||
### ✨ 新特性
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
// 自定义枚举描述并封装参数配置
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 限流
|
||||
*
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
}
|
||||
@@ -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 " " -> " "}
|
||||
*/
|
||||
public static final String HTML_NBSP = XmlUtil.NBSP;
|
||||
|
||||
/**
|
||||
* 字符串常量:HTML And 符转义 {@code "&" -> "&"}
|
||||
*/
|
||||
public static final String HTML_AMP = XmlUtil.AMP;
|
||||
|
||||
/**
|
||||
* 字符串常量:HTML 双引号转义 {@code """ -> "\""}
|
||||
*/
|
||||
public static final String HTML_QUOTE = XmlUtil.QUOTE;
|
||||
|
||||
/**
|
||||
* 字符串常量:HTML 单引号转义 {@code "&apos" -> "'"}
|
||||
*/
|
||||
public static final String HTML_APOS = XmlUtil.APOS;
|
||||
|
||||
/**
|
||||
* 字符串常量:HTML 小于号转义 {@code "<" -> "<"}
|
||||
*/
|
||||
public static final String HTML_LT = XmlUtil.LT;
|
||||
|
||||
/**
|
||||
* 字符串常量:HTML 大于号转义 {@code ">" -> ">"}
|
||||
*/
|
||||
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 " " -> " "}
|
||||
*/
|
||||
public static final String HTML_NBSP = " ";
|
||||
|
||||
/**
|
||||
* HTML And 符转义 {@code "&" -> "&"}
|
||||
*/
|
||||
public static final String HTML_AMP = "&";
|
||||
|
||||
/**
|
||||
* HTML 双引号转义 {@code """ -> "\""}
|
||||
*/
|
||||
public static final String HTML_QUOTE = """;
|
||||
|
||||
/**
|
||||
* HTML 单引号转义 {@code "&apos" -> "'"}
|
||||
*/
|
||||
public static final String HTML_APOS = "'";
|
||||
|
||||
/**
|
||||
* HTML 小于号转义 {@code "<" -> "<"}
|
||||
*/
|
||||
public static final String HTML_LT = "<";
|
||||
|
||||
/**
|
||||
* HTML 大于号转义 {@code ">" -> ">"}
|
||||
*/
|
||||
public static final String HTML_GT = ">";
|
||||
|
||||
private StringConstants() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 {};
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -68,8 +68,8 @@ public class DataPermissionDialect extends CommonsDialectImpl {
|
||||
* 构建自定义数据权限表达式
|
||||
*
|
||||
* <p>
|
||||
* 处理完后的 SQL 示例:<br /> select t1.* from table as t1 where t1.dept_id in (select dept_id from sys_role_dept
|
||||
* where role_id = xxx);
|
||||
* 处理完后的 SQL 示例:<br /> select t1.* from table as t1 where t1.dept_id in (select dept_id from sys_role_dept where
|
||||
* role_id = xxx);
|
||||
* </p>
|
||||
*
|
||||
* @param dataPermission 数据权限
|
||||
@@ -124,8 +124,8 @@ public class DataPermissionDialect extends CommonsDialectImpl {
|
||||
* 构建本部门及以下数据权限表达式
|
||||
*
|
||||
* <p>
|
||||
* 处理完后的 SQL 示例:<br /> select t1.* from table as t1 where t1.dept_id in (select id from sys_dept where id =
|
||||
* xxx or find_in_set(xxx, ancestors));
|
||||
* 处理完后的 SQL 示例:<br /> select t1.* from table as t1 where t1.dept_id in (select id from sys_dept where id = xxx or
|
||||
* find_in_set(xxx, ancestors));
|
||||
* </p>
|
||||
*
|
||||
* @param dataPermission 数据权限
|
||||
@@ -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;
|
||||
|
||||
@@ -16,17 +16,19 @@
|
||||
|
||||
package top.continew.starter.data.mf.util;
|
||||
|
||||
import cn.hutool.core.annotation.AnnotationUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import com.mybatisflex.core.query.QueryWrapper;
|
||||
import org.slf4j.Logger;
|
||||
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;
|
||||
@@ -133,23 +135,21 @@ public class QueryWrapperHelper {
|
||||
* @return QueryWrapper Consumer
|
||||
*/
|
||||
private static <Q, R> List<Consumer<QueryWrapper>> buildWrapperConsumer(Q query, Field field) {
|
||||
boolean accessible = field.canAccess(query);
|
||||
try {
|
||||
field.setAccessible(true);
|
||||
// 如果字段值为空,直接返回
|
||||
Object fieldValue = field.get(query);
|
||||
Object fieldValue = ReflectUtil.getFieldValue(query, field);
|
||||
if (ObjectUtil.isEmpty(fieldValue)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 设置了 @QueryIgnore 注解,直接忽略
|
||||
QueryIgnore queryIgnoreAnnotation = field.getAnnotation(QueryIgnore.class);
|
||||
QueryIgnore queryIgnoreAnnotation = AnnotationUtil.getAnnotation(field, QueryIgnore.class);
|
||||
if (null != queryIgnoreAnnotation) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 建议:数据库表列建议采用下划线连接法命名,程序变量建议采用驼峰法命名
|
||||
String fieldName = field.getName();
|
||||
String fieldName = ReflectUtil.getFieldName(field);
|
||||
// 没有 @Query 注解,默认等值查询
|
||||
Query queryAnnotation = field.getAnnotation(Query.class);
|
||||
Query queryAnnotation = AnnotationUtil.getAnnotation(field, Query.class);
|
||||
if (null == queryAnnotation) {
|
||||
return Collections.singletonList(q -> q.eq(CharSequenceUtil.toUnderlineCase(fieldName), fieldValue));
|
||||
}
|
||||
@@ -173,8 +173,6 @@ public class QueryWrapperHelper {
|
||||
} catch (Exception e) {
|
||||
log.error("Build query wrapper occurred an error: {}. Query: {}, Field: {}.", e
|
||||
.getMessage(), query, field, e);
|
||||
} finally {
|
||||
field.setAccessible(accessible);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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> {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -16,17 +16,19 @@
|
||||
|
||||
package top.continew.starter.data.mp.util;
|
||||
|
||||
import cn.hutool.core.annotation.AnnotationUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import org.slf4j.Logger;
|
||||
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;
|
||||
@@ -127,23 +129,21 @@ public class QueryWrapperHelper {
|
||||
* @return QueryWrapper Consumer
|
||||
*/
|
||||
private static <Q, R> List<Consumer<QueryWrapper<R>>> buildWrapperConsumer(Q query, Field field) {
|
||||
boolean accessible = field.canAccess(query);
|
||||
try {
|
||||
field.setAccessible(true);
|
||||
// 如果字段值为空,直接返回
|
||||
Object fieldValue = field.get(query);
|
||||
Object fieldValue = ReflectUtil.getFieldValue(query, field);
|
||||
if (ObjectUtil.isEmpty(fieldValue)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 设置了 @QueryIgnore 注解,直接忽略
|
||||
QueryIgnore queryIgnoreAnnotation = field.getAnnotation(QueryIgnore.class);
|
||||
QueryIgnore queryIgnoreAnnotation = AnnotationUtil.getAnnotation(field, QueryIgnore.class);
|
||||
if (null != queryIgnoreAnnotation) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 建议:数据库表列建议采用下划线连接法命名,程序变量建议采用驼峰法命名
|
||||
String fieldName = field.getName();
|
||||
String fieldName = ReflectUtil.getFieldName(field);
|
||||
// 没有 @Query 注解,默认等值查询
|
||||
Query queryAnnotation = field.getAnnotation(Query.class);
|
||||
Query queryAnnotation = AnnotationUtil.getAnnotation(field, Query.class);
|
||||
if (null == queryAnnotation) {
|
||||
return Collections.singletonList(q -> q.eq(CharSequenceUtil.toUnderlineCase(fieldName), fieldValue));
|
||||
}
|
||||
@@ -167,8 +167,6 @@ public class QueryWrapperHelper {
|
||||
} catch (Exception e) {
|
||||
log.error("Build query wrapper occurred an error: {}. Query: {}, Field: {}.", e
|
||||
.getMessage(), query, field, e);
|
||||
} finally {
|
||||
field.setAccessible(accessible);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@@ -193,8 +191,9 @@ public class QueryWrapperHelper {
|
||||
case LT -> consumers.add(q -> q.lt(columnName, fieldValue));
|
||||
case LE -> consumers.add(q -> q.le(columnName, fieldValue));
|
||||
case BETWEEN -> {
|
||||
// 数组转集合
|
||||
List<Object> between = new ArrayList<>(ArrayUtil.isArray(fieldValue)
|
||||
? CollUtil.toList(fieldValue)
|
||||
? List.of((Object[])fieldValue)
|
||||
: (List<Object>)fieldValue);
|
||||
ValidationUtils.throwIf(between.size() != 2, "[{}] 必须是一个范围", columnName);
|
||||
consumers.add(q -> q.between(columnName, between.get(0), between.get(1)));
|
||||
@@ -205,13 +204,13 @@ public class QueryWrapperHelper {
|
||||
case IN -> {
|
||||
ValidationUtils.throwIfEmpty(fieldValue, "[{}] 不能为空", columnName);
|
||||
consumers.add(q -> q.in(columnName, ArrayUtil.isArray(fieldValue)
|
||||
? CollUtil.toList(fieldValue)
|
||||
? List.of((Object[])fieldValue)
|
||||
: (Collection<Object>)fieldValue));
|
||||
}
|
||||
case NOT_IN -> {
|
||||
ValidationUtils.throwIfEmpty(fieldValue, "[{}] 不能为空", columnName);
|
||||
consumers.add(q -> q.notIn(columnName, ArrayUtil.isArray(fieldValue)
|
||||
? CollUtil.toList(fieldValue)
|
||||
? List.of((Object[])fieldValue)
|
||||
: (Collection<Object>)fieldValue));
|
||||
}
|
||||
case IS_NULL -> consumers.add(q -> q.isNull(columnName));
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
|
||||
<properties>
|
||||
<!-- 项目版本号 -->
|
||||
<revision>2.7.2</revision>
|
||||
<revision>2.7.5</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>
|
||||
|
||||
@@ -30,18 +30,6 @@
|
||||
<artifactId>continew-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 认证模块 - SaToken -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-auth-satoken</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- 数据访问模块 - 核心模块 -->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.extension.crud.annotation;
|
||||
|
||||
import top.continew.starter.extension.crud.enums.Api;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* CRUD(增删改查)API
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2.7.5
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface CrudApi {
|
||||
|
||||
/**
|
||||
* API 类型
|
||||
*/
|
||||
Api value() default Api.LIST;
|
||||
}
|
||||
@@ -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};
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package top.continew.starter.extension.crud.annotation;
|
||||
|
||||
import org.springframework.context.annotation.Import;
|
||||
import top.continew.starter.extension.crud.autoconfigure.CrudRequestMappingAutoConfiguration;
|
||||
import top.continew.starter.extension.crud.autoconfigure.CrudRestControllerAutoConfiguration;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
@@ -30,5 +31,5 @@ import java.lang.annotation.*;
|
||||
@Target({ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Import({CrudRestControllerAutoConfiguration.class})
|
||||
@Import({CrudRequestMappingAutoConfiguration.class, CrudRestControllerAutoConfiguration.class})
|
||||
public @interface EnableCrudRestController {}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.extension.crud.aop;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
import org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.support.AbstractPointcutAdvisor;
|
||||
import org.springframework.aop.support.ComposablePointcut;
|
||||
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
|
||||
/**
|
||||
* CRUD API 注解通知
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2.7.5
|
||||
*/
|
||||
public class CrudApiAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
|
||||
|
||||
private final Advice advice;
|
||||
private final Pointcut pointcut;
|
||||
|
||||
public CrudApiAnnotationAdvisor(CrudApiAnnotationInterceptor advice, Class<? extends Annotation> annotation) {
|
||||
this.advice = advice;
|
||||
this.pointcut = new ComposablePointcut(AnnotationMatchingPointcut.forMethodAnnotation(annotation));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pointcut getPointcut() {
|
||||
return this.pointcut;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Advice getAdvice() {
|
||||
return this.advice;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
if (this.advice instanceof BeanFactoryAware beanFactoryAware) {
|
||||
beanFactoryAware.setBeanFactory(beanFactory);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.extension.crud.aop;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.core.BridgeMethodResolver;
|
||||
import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import top.continew.starter.extension.crud.annotation.CrudApi;
|
||||
import top.continew.starter.extension.crud.controller.AbstractBaseController;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* CRUD API 注解拦截器
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2.7.5
|
||||
*/
|
||||
public class CrudApiAnnotationInterceptor implements MethodInterceptor {
|
||||
|
||||
@Override
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
// 获取目标类
|
||||
Class<?> targetClass = AopUtils.getTargetClass(Objects.requireNonNull(invocation.getThis()));
|
||||
// 获取目标方法
|
||||
Method specificMethod = ClassUtils.getMostSpecificMethod(invocation.getMethod(), targetClass);
|
||||
Method targetMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
|
||||
// 获取 @CrudApi 注解
|
||||
CrudApi crudApi = AnnotatedElementUtils.findMergedAnnotation(targetMethod, CrudApi.class);
|
||||
// 执行处理
|
||||
AbstractBaseController controller = (AbstractBaseController)invocation.getThis();
|
||||
controller.preHandle(crudApi, invocation.getArguments(), targetMethod, targetClass);
|
||||
return invocation.proceed();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.extension.crud.autoconfigure;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
import org.springframework.web.accept.ContentNegotiationManager;
|
||||
import org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||
import org.springframework.web.servlet.resource.ResourceUrlProvider;
|
||||
|
||||
/**
|
||||
* CRUD Request Mapping 自动配置
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(CrudProperties.class)
|
||||
public class CrudRequestMappingAutoConfiguration extends DelegatingWebMvcConfiguration {
|
||||
|
||||
/**
|
||||
* CRUD 请求映射器处理器映射器(覆盖默认 RequestMappingHandlerMapping)
|
||||
*/
|
||||
@Override
|
||||
public RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
|
||||
return new CrudRequestMappingHandlerMapping();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
@Override
|
||||
public RequestMappingHandlerMapping requestMappingHandlerMapping(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
|
||||
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
|
||||
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
|
||||
return super.requestMappingHandlerMapping(contentNegotiationManager, conversionService, resourceUrlProvider);
|
||||
}
|
||||
}
|
||||
@@ -53,7 +53,7 @@ public class CrudRequestMappingHandlerMapping extends RequestMappingHandlerMappi
|
||||
// 过滤 API,如果非本类中定义,且 API 列表中不包含,则忽略
|
||||
Api[] apiArr = crudRequestMapping.api();
|
||||
Api api = ExceptionUtils.exToNull(() -> Api.valueOf(method.getName().toUpperCase()));
|
||||
if (method.getDeclaringClass() != handlerType && !ArrayUtil.containsAny(apiArr, Api.ALL, api)) {
|
||||
if (method.getDeclaringClass() != handlerType && !ArrayUtil.contains(apiArr, api)) {
|
||||
return null;
|
||||
}
|
||||
// 拼接路径(合并了 @RequestMapping 的部分能力)
|
||||
|
||||
@@ -19,44 +19,42 @@ package top.continew.starter.extension.crud.autoconfigure;
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.autoconfigure.AutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.format.support.FormattingConversionService;
|
||||
import org.springframework.web.accept.ContentNegotiationManager;
|
||||
import org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration;
|
||||
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
|
||||
import org.springframework.web.servlet.resource.ResourceUrlProvider;
|
||||
import top.continew.starter.extension.crud.annotation.CrudApi;
|
||||
import top.continew.starter.extension.crud.aop.CrudApiAnnotationAdvisor;
|
||||
import top.continew.starter.extension.crud.aop.CrudApiAnnotationInterceptor;
|
||||
|
||||
/**
|
||||
* CRUD REST Controller 自动配置
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.0.0
|
||||
* @since 2.7.5
|
||||
*/
|
||||
@Configuration
|
||||
@AutoConfiguration
|
||||
@EnableConfigurationProperties(CrudProperties.class)
|
||||
public class CrudRestControllerAutoConfiguration extends DelegatingWebMvcConfiguration {
|
||||
public class CrudRestControllerAutoConfiguration {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(CrudRestControllerAutoConfiguration.class);
|
||||
|
||||
/**
|
||||
* CRUD 请求映射器处理器映射器(覆盖默认 RequestMappingHandlerMapping)
|
||||
* CRUD API 注解通知
|
||||
*/
|
||||
@Override
|
||||
public RequestMappingHandlerMapping createRequestMappingHandlerMapping() {
|
||||
return new CrudRequestMappingHandlerMapping();
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public CrudApiAnnotationAdvisor crudApiAnnotationAdvisor(CrudApiAnnotationInterceptor crudApiAnnotationInterceptor) {
|
||||
return new CrudApiAnnotationAdvisor(crudApiAnnotationInterceptor, CrudApi.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* CRUD API 注解拦截器
|
||||
*/
|
||||
@Bean
|
||||
@Primary
|
||||
@Override
|
||||
public RequestMappingHandlerMapping requestMappingHandlerMapping(@Qualifier("mvcContentNegotiationManager") ContentNegotiationManager contentNegotiationManager,
|
||||
@Qualifier("mvcConversionService") FormattingConversionService conversionService,
|
||||
@Qualifier("mvcResourceUrlProvider") ResourceUrlProvider resourceUrlProvider) {
|
||||
return super.requestMappingHandlerMapping(contentNegotiationManager, conversionService, resourceUrlProvider);
|
||||
@ConditionalOnMissingBean
|
||||
public CrudApiAnnotationInterceptor crudApiAnnotationInterceptor() {
|
||||
return new CrudApiAnnotationInterceptor();
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
@@ -35,25 +35,21 @@ public class CrudTreeProperties {
|
||||
|
||||
/**
|
||||
* 父 ID 字段名
|
||||
*
|
||||
*/
|
||||
private String parentIdKey = "parentId";
|
||||
|
||||
/**
|
||||
* 名称字段名
|
||||
*
|
||||
*/
|
||||
private String nameKey = "name";
|
||||
|
||||
/**
|
||||
* 排序字段名
|
||||
*
|
||||
*/
|
||||
private String weightKey = "weight";
|
||||
|
||||
/**
|
||||
* 子列表字段名
|
||||
*
|
||||
*/
|
||||
private String childrenKey = "children";
|
||||
|
||||
|
||||
@@ -16,9 +16,7 @@
|
||||
|
||||
package top.continew.starter.extension.crud.controller;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.lang.tree.Tree;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import com.feiniaojin.gracefulresponse.api.ExcludeFromGracefulResponse;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
@@ -27,31 +25,31 @@ import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import top.continew.starter.core.constant.StringConstants;
|
||||
import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
|
||||
import top.continew.starter.extension.crud.annotation.CrudApi;
|
||||
import top.continew.starter.extension.crud.enums.Api;
|
||||
import top.continew.starter.extension.crud.handler.CrudApiHandler;
|
||||
import top.continew.starter.extension.crud.model.query.PageQuery;
|
||||
import top.continew.starter.extension.crud.model.query.SortQuery;
|
||||
import top.continew.starter.extension.crud.model.req.BaseReq;
|
||||
import top.continew.starter.extension.crud.model.resp.BaseIdResp;
|
||||
import top.continew.starter.extension.crud.model.resp.PageResp;
|
||||
import top.continew.starter.extension.crud.model.resp.BasePageResp;
|
||||
import top.continew.starter.extension.crud.service.BaseService;
|
||||
import top.continew.starter.extension.crud.util.ValidateGroup;
|
||||
import top.continew.starter.extension.crud.validation.CrudValidationGroup;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 控制器基类
|
||||
* 控制器抽象基类
|
||||
*
|
||||
* @param <S> 业务接口
|
||||
* @param <L> 列表类型
|
||||
* @param <D> 详情类型
|
||||
* @param <Q> 查询条件
|
||||
* @param <C> 创建或修改类型
|
||||
* @param <C> 创建或修改参数类型
|
||||
* @author Charles7c
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q, C extends BaseReq> {
|
||||
public abstract class AbstractBaseController<S extends BaseService<L, D, Q, C>, L, D, Q, C extends BaseReq> implements CrudApiHandler {
|
||||
|
||||
@Autowired
|
||||
protected S baseService;
|
||||
@@ -63,11 +61,11 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q,
|
||||
* @param pageQuery 分页查询条件
|
||||
* @return 分页信息
|
||||
*/
|
||||
@CrudApi(Api.PAGE)
|
||||
@Operation(summary = "分页查询列表", description = "分页查询列表")
|
||||
@ResponseBody
|
||||
@GetMapping
|
||||
public PageResp<L> page(Q query, @Validated PageQuery pageQuery) {
|
||||
this.checkPermission(Api.LIST);
|
||||
public BasePageResp<L> page(Q query, @Validated PageQuery pageQuery) {
|
||||
return baseService.page(query, pageQuery);
|
||||
}
|
||||
|
||||
@@ -78,11 +76,11 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q,
|
||||
* @param sortQuery 排序查询条件
|
||||
* @return 列表信息
|
||||
*/
|
||||
@CrudApi(Api.LIST)
|
||||
@Operation(summary = "查询列表", description = "查询列表")
|
||||
@ResponseBody
|
||||
@GetMapping("/list")
|
||||
public List<L> list(Q query, SortQuery sortQuery) {
|
||||
this.checkPermission(Api.LIST);
|
||||
return baseService.list(query, sortQuery);
|
||||
}
|
||||
|
||||
@@ -93,11 +91,11 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q,
|
||||
* @param sortQuery 排序查询条件
|
||||
* @return 树列表信息
|
||||
*/
|
||||
@CrudApi(Api.TREE)
|
||||
@Operation(summary = "查询树列表", description = "查询树列表")
|
||||
@ResponseBody
|
||||
@GetMapping("/tree")
|
||||
public List<Tree<Long>> tree(Q query, SortQuery sortQuery) {
|
||||
this.checkPermission(Api.LIST);
|
||||
return baseService.tree(query, sortQuery, false);
|
||||
}
|
||||
|
||||
@@ -107,41 +105,41 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q,
|
||||
* @param id ID
|
||||
* @return 详情信息
|
||||
*/
|
||||
@CrudApi(Api.DETAIL)
|
||||
@Operation(summary = "查询详情", description = "查询详情")
|
||||
@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) {
|
||||
return baseService.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增
|
||||
*
|
||||
* @param req 创建信息
|
||||
* @return 自增 ID
|
||||
* @param req 创建参数
|
||||
* @return ID
|
||||
*/
|
||||
@CrudApi(Api.ADD)
|
||||
@Operation(summary = "新增数据", description = "新增数据")
|
||||
@ResponseBody
|
||||
@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();
|
||||
public BaseIdResp<Long> add(@Validated(CrudValidationGroup.Add.class) @RequestBody C req) {
|
||||
return new BaseIdResp<>(baseService.add(req));
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改
|
||||
*
|
||||
* @param req 修改信息
|
||||
* @param req 修改参数
|
||||
* @param id ID
|
||||
*/
|
||||
@CrudApi(Api.UPDATE)
|
||||
@Operation(summary = "修改数据", description = "修改数据")
|
||||
@Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
|
||||
@ResponseBody
|
||||
@PutMapping("/{id}")
|
||||
public void update(@Validated(ValidateGroup.Crud.Update.class) @RequestBody C req, @PathVariable("id") Long id) {
|
||||
this.checkPermission(Api.UPDATE);
|
||||
public void update(@Validated(CrudValidationGroup.Update.class) @RequestBody C req, @PathVariable("id") Long id) {
|
||||
baseService.update(req, id);
|
||||
}
|
||||
|
||||
@@ -150,12 +148,12 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q,
|
||||
*
|
||||
* @param ids ID 列表
|
||||
*/
|
||||
@CrudApi(Api.DELETE)
|
||||
@Operation(summary = "删除数据", description = "删除数据")
|
||||
@Parameter(name = "ids", description = "ID 列表", example = "1,2", in = ParameterIn.PATH)
|
||||
@ResponseBody
|
||||
@DeleteMapping("/{ids}")
|
||||
public void delete(@PathVariable("ids") List<Long> ids) {
|
||||
this.checkPermission(Api.DELETE);
|
||||
baseService.delete(ids);
|
||||
}
|
||||
|
||||
@@ -166,24 +164,11 @@ public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q,
|
||||
* @param sortQuery 排序查询条件
|
||||
* @param response 响应对象
|
||||
*/
|
||||
@CrudApi(Api.EXPORT)
|
||||
@ExcludeFromGracefulResponse
|
||||
@Operation(summary = "导出数据", description = "导出数据")
|
||||
@GetMapping("/export")
|
||||
public void export(Q query, SortQuery sortQuery, HttpServletResponse response) {
|
||||
this.checkPermission(Api.EXPORT);
|
||||
baseService.export(query, sortQuery, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 API 类型进行权限验证
|
||||
*
|
||||
* @param api API 类型
|
||||
*/
|
||||
protected void checkPermission(Api api) {
|
||||
CrudRequestMapping crudRequestMapping = this.getClass().getDeclaredAnnotation(CrudRequestMapping.class);
|
||||
String path = crudRequestMapping.value();
|
||||
String permissionPrefix = String.join(StringConstants.COLON, CharSequenceUtil
|
||||
.splitTrim(path, StringConstants.SLASH));
|
||||
StpUtil.checkPermission("%s:%s".formatted(permissionPrefix, api.name().toLowerCase()));
|
||||
}
|
||||
}
|
||||
@@ -24,11 +24,6 @@ package top.continew.starter.extension.crud.enums;
|
||||
*/
|
||||
public enum Api {
|
||||
|
||||
/**
|
||||
* 所有 API
|
||||
*/
|
||||
ALL,
|
||||
|
||||
/**
|
||||
* 分页
|
||||
*/
|
||||
@@ -47,7 +42,7 @@ public enum Api {
|
||||
/**
|
||||
* 详情
|
||||
*/
|
||||
GET,
|
||||
DETAIL,
|
||||
|
||||
/**
|
||||
* 新增
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.extension.crud.handler;
|
||||
|
||||
import top.continew.starter.extension.crud.annotation.CrudApi;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* CRUD API 处理器
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2.7.5
|
||||
*/
|
||||
public interface CrudApiHandler {
|
||||
|
||||
/**
|
||||
* 前置处理
|
||||
*
|
||||
* @param crudApi CRUD API 注解
|
||||
* @param args 方法参数
|
||||
* @param targetMethod 目标方法
|
||||
* @param targetClass 目标类
|
||||
* @throws Exception 处理异常
|
||||
*/
|
||||
void preHandle(CrudApi crudApi, Object[] args, Method targetMethod, Class<?> targetClass) throws Exception;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -20,7 +20,7 @@ import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* 请求基类
|
||||
* 请求参数基类
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.0.0
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import java.io.Serializable;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 响应基类
|
||||
* 响应参数基类
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.0.0
|
||||
|
||||
@@ -20,8 +20,8 @@ import cn.hutool.core.lang.tree.Tree;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import top.continew.starter.extension.crud.model.query.PageQuery;
|
||||
import top.continew.starter.extension.crud.model.query.SortQuery;
|
||||
import top.continew.starter.extension.crud.model.resp.BasePageResp;
|
||||
import top.continew.starter.extension.crud.model.resp.LabelValueResp;
|
||||
import top.continew.starter.extension.crud.model.resp.PageResp;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -31,7 +31,7 @@ import java.util.List;
|
||||
* @param <L> 列表类型
|
||||
* @param <D> 详情类型
|
||||
* @param <Q> 查询条件
|
||||
* @param <C> 创建或修改类型
|
||||
* @param <C> 创建或修改参数类型
|
||||
* @author Charles7c
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@@ -44,7 +44,7 @@ public interface BaseService<L, D, Q, C> {
|
||||
* @param pageQuery 分页查询条件
|
||||
* @return 分页列表信息
|
||||
*/
|
||||
PageResp<L> page(Q query, PageQuery pageQuery);
|
||||
BasePageResp<L> page(Q query, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询列表
|
||||
@@ -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);
|
||||
@@ -14,30 +14,25 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.extension.crud.util;
|
||||
package top.continew.starter.extension.crud.validation;
|
||||
|
||||
import jakarta.validation.groups.Default;
|
||||
|
||||
/**
|
||||
* 分组校验
|
||||
* CRUD 分组校验
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface ValidateGroup extends Default {
|
||||
public interface CrudValidationGroup extends Default {
|
||||
|
||||
/**
|
||||
* 分组校验-增删改查
|
||||
* CRUD 分组校验-新增
|
||||
*/
|
||||
interface Crud extends ValidateGroup {
|
||||
/**
|
||||
* 分组校验-创建
|
||||
*/
|
||||
interface Add extends Crud {}
|
||||
interface Add extends CrudValidationGroup {}
|
||||
|
||||
/**
|
||||
* 分组校验-修改
|
||||
*/
|
||||
interface Update extends Crud {}
|
||||
}
|
||||
/**
|
||||
* CRUD 分组校验-修改
|
||||
*/
|
||||
interface Update extends CrudValidationGroup {}
|
||||
}
|
||||
@@ -1,189 +0,0 @@
|
||||
/*
|
||||
* 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.extension.crud.controller;
|
||||
|
||||
import cn.dev33.satoken.stp.StpUtil;
|
||||
import cn.hutool.core.lang.tree.Tree;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import com.feiniaojin.gracefulresponse.api.ExcludeFromGracefulResponse;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.enums.ParameterIn;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import top.continew.starter.core.constant.StringConstants;
|
||||
import top.continew.starter.extension.crud.annotation.CrudRequestMapping;
|
||||
import top.continew.starter.extension.crud.enums.Api;
|
||||
import top.continew.starter.extension.crud.model.query.PageQuery;
|
||||
import top.continew.starter.extension.crud.model.query.SortQuery;
|
||||
import top.continew.starter.extension.crud.model.req.BaseReq;
|
||||
import top.continew.starter.extension.crud.model.resp.BaseIdResp;
|
||||
import top.continew.starter.extension.crud.model.resp.PageResp;
|
||||
import top.continew.starter.extension.crud.service.BaseService;
|
||||
import top.continew.starter.extension.crud.util.ValidateGroup;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 控制器基类
|
||||
*
|
||||
* @param <S> 业务接口
|
||||
* @param <L> 列表类型
|
||||
* @param <D> 详情类型
|
||||
* @param <Q> 查询条件
|
||||
* @param <C> 创建或修改类型
|
||||
* @author Charles7c
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public abstract class BaseController<S extends BaseService<L, D, Q, C>, L, D, Q, C extends BaseReq> {
|
||||
|
||||
@Autowired
|
||||
protected S baseService;
|
||||
|
||||
/**
|
||||
* 分页查询列表
|
||||
*
|
||||
* @param query 查询条件
|
||||
* @param pageQuery 分页查询条件
|
||||
* @return 分页信息
|
||||
*/
|
||||
@Operation(summary = "分页查询列表", description = "分页查询列表")
|
||||
@ResponseBody
|
||||
@GetMapping
|
||||
public PageResp<L> page(Q query, @Validated PageQuery pageQuery) {
|
||||
this.checkPermission(Api.LIST);
|
||||
return baseService.page(query, pageQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询列表
|
||||
*
|
||||
* @param query 查询条件
|
||||
* @param sortQuery 排序查询条件
|
||||
* @return 列表信息
|
||||
*/
|
||||
@Operation(summary = "查询列表", description = "查询列表")
|
||||
@ResponseBody
|
||||
@GetMapping("/list")
|
||||
public List<L> list(Q query, SortQuery sortQuery) {
|
||||
this.checkPermission(Api.LIST);
|
||||
return baseService.list(query, sortQuery);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询树列表
|
||||
*
|
||||
* @param query 查询条件
|
||||
* @param sortQuery 排序查询条件
|
||||
* @return 树列表信息
|
||||
*/
|
||||
@Operation(summary = "查询树列表", description = "查询树列表")
|
||||
@ResponseBody
|
||||
@GetMapping("/tree")
|
||||
public List<Tree<Long>> tree(Q query, SortQuery sortQuery) {
|
||||
this.checkPermission(Api.LIST);
|
||||
return baseService.tree(query, sortQuery, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询详情
|
||||
*
|
||||
* @param id ID
|
||||
* @return 详情信息
|
||||
*/
|
||||
@Operation(summary = "查询详情", description = "查询详情")
|
||||
@Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
|
||||
@ResponseBody
|
||||
@GetMapping("/{id}")
|
||||
public D get(@PathVariable("id") Long id) {
|
||||
this.checkPermission(Api.LIST);
|
||||
return baseService.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增
|
||||
*
|
||||
* @param req 创建信息
|
||||
* @return 自增 ID
|
||||
*/
|
||||
@Operation(summary = "新增数据", description = "新增数据")
|
||||
@ResponseBody
|
||||
@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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改
|
||||
*
|
||||
* @param req 修改信息
|
||||
* @param id ID
|
||||
*/
|
||||
@Operation(summary = "修改数据", description = "修改数据")
|
||||
@Parameter(name = "id", description = "ID", example = "1", in = ParameterIn.PATH)
|
||||
@ResponseBody
|
||||
@PutMapping("/{id}")
|
||||
public void update(@Validated(ValidateGroup.Crud.Update.class) @RequestBody C req, @PathVariable("id") Long id) {
|
||||
this.checkPermission(Api.UPDATE);
|
||||
baseService.update(req, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*
|
||||
* @param ids ID 列表
|
||||
*/
|
||||
@Operation(summary = "删除数据", description = "删除数据")
|
||||
@Parameter(name = "ids", description = "ID 列表", example = "1,2", in = ParameterIn.PATH)
|
||||
@ResponseBody
|
||||
@DeleteMapping("/{ids}")
|
||||
public void delete(@PathVariable("ids") List<Long> ids) {
|
||||
this.checkPermission(Api.DELETE);
|
||||
baseService.delete(ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出
|
||||
*
|
||||
* @param query 查询条件
|
||||
* @param sortQuery 排序查询条件
|
||||
* @param response 响应对象
|
||||
*/
|
||||
@ExcludeFromGracefulResponse
|
||||
@Operation(summary = "导出数据", description = "导出数据")
|
||||
@GetMapping("/export")
|
||||
public void export(Q query, SortQuery sortQuery, HttpServletResponse response) {
|
||||
this.checkPermission(Api.EXPORT);
|
||||
baseService.export(query, sortQuery, response);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 API 类型进行权限验证
|
||||
*
|
||||
* @param api API 类型
|
||||
*/
|
||||
protected void checkPermission(Api api) {
|
||||
CrudRequestMapping crudRequestMapping = this.getClass().getDeclaredAnnotation(CrudRequestMapping.class);
|
||||
String path = crudRequestMapping.value();
|
||||
String permissionPrefix = String.join(StringConstants.COLON, CharSequenceUtil
|
||||
.splitTrim(path, StringConstants.SLASH));
|
||||
StpUtil.checkPermission("%s:%s".formatted(permissionPrefix, api.name().toLowerCase()));
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* 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.extension.crud.service;
|
||||
|
||||
import cn.hutool.core.lang.tree.Tree;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import top.continew.starter.extension.crud.model.query.PageQuery;
|
||||
import top.continew.starter.extension.crud.model.query.SortQuery;
|
||||
import top.continew.starter.extension.crud.model.resp.LabelValueResp;
|
||||
import top.continew.starter.extension.crud.model.resp.PageResp;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 业务接口基类
|
||||
*
|
||||
* @param <L> 列表类型
|
||||
* @param <D> 详情类型
|
||||
* @param <Q> 查询条件
|
||||
* @param <C> 创建或修改类型
|
||||
* @author Charles7c
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public interface BaseService<L, D, Q, C> {
|
||||
|
||||
/**
|
||||
* 分页查询列表
|
||||
*
|
||||
* @param query 查询条件
|
||||
* @param pageQuery 分页查询条件
|
||||
* @return 分页列表信息
|
||||
*/
|
||||
PageResp<L> page(Q query, PageQuery pageQuery);
|
||||
|
||||
/**
|
||||
* 查询列表
|
||||
*
|
||||
* @param query 查询条件
|
||||
* @param sortQuery 排序查询条件
|
||||
* @return 列表信息
|
||||
*/
|
||||
List<L> list(Q query, SortQuery sortQuery);
|
||||
|
||||
/**
|
||||
* 查询树列表
|
||||
* <p>
|
||||
* 虽然提供了查询条件,但不建议使用,容易因缺失根节点导致树节点丢失。
|
||||
* 建议在前端进行查询过滤,如需使用建议重写方法。
|
||||
* </p>
|
||||
*
|
||||
* @param query 查询条件
|
||||
* @param sortQuery 排序查询条件
|
||||
* @param isSimple 是否为简单树结构(不包含基本树结构之外的扩展字段,简单树(下拉列表)使用全局配置结构,复杂树(表格)使用 @DictField 局部配置)
|
||||
* @return 树列表信息
|
||||
*/
|
||||
List<Tree<Long>> tree(Q query, SortQuery sortQuery, boolean isSimple);
|
||||
|
||||
/**
|
||||
* 查看详情
|
||||
*
|
||||
* @param id ID
|
||||
* @return 详情信息
|
||||
*/
|
||||
D get(Long id);
|
||||
|
||||
/**
|
||||
* 查询字典列表
|
||||
*
|
||||
* @param query 查询条件
|
||||
* @param sortQuery 排序查询条件
|
||||
* @return 字典列表信息
|
||||
*/
|
||||
List<LabelValueResp> listDict(Q query, SortQuery sortQuery);
|
||||
|
||||
/**
|
||||
* 新增
|
||||
*
|
||||
* @param req 创建信息
|
||||
* @return 自增 ID
|
||||
*/
|
||||
Long add(C req);
|
||||
|
||||
/**
|
||||
* 修改
|
||||
*
|
||||
* @param req 修改信息
|
||||
* @param id ID
|
||||
*/
|
||||
void update(C req, Long id);
|
||||
|
||||
/**
|
||||
* 删除
|
||||
*
|
||||
* @param ids ID 列表
|
||||
*/
|
||||
void delete(List<Long> ids);
|
||||
|
||||
/**
|
||||
* 导出
|
||||
*
|
||||
* @param query 查询条件
|
||||
* @param sortQuery 排序查询条件
|
||||
* @param response 响应对象
|
||||
*/
|
||||
void export(Q query, SortQuery sortQuery, HttpServletResponse response);
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.extension.crud.service.impl;
|
||||
package top.continew.starter.extension.crud.service;
|
||||
|
||||
import cn.crane4j.core.support.OperateTemplate;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
@@ -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;
|
||||
@@ -45,7 +44,6 @@ import top.continew.starter.extension.crud.model.entity.BaseIdDO;
|
||||
import top.continew.starter.extension.crud.model.query.PageQuery;
|
||||
import top.continew.starter.extension.crud.model.query.SortQuery;
|
||||
import top.continew.starter.extension.crud.model.resp.PageResp;
|
||||
import top.continew.starter.extension.crud.service.BaseService;
|
||||
import top.continew.starter.file.excel.util.ExcelUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
@@ -61,7 +59,7 @@ import java.util.Optional;
|
||||
* @param <L> 列表类型
|
||||
* @param <D> 详情类型
|
||||
* @param <Q> 查询条件
|
||||
* @param <C> 创建或修改类型
|
||||
* @param <C> 创建或修改参数类型
|
||||
* @author Charles7c
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.extension.crud.service.impl;
|
||||
package top.continew.starter.extension.crud.service;
|
||||
|
||||
import cn.crane4j.core.support.OperateTemplate;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
@@ -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;
|
||||
@@ -51,7 +51,6 @@ import top.continew.starter.extension.crud.model.query.PageQuery;
|
||||
import top.continew.starter.extension.crud.model.query.SortQuery;
|
||||
import top.continew.starter.extension.crud.model.resp.LabelValueResp;
|
||||
import top.continew.starter.extension.crud.model.resp.PageResp;
|
||||
import top.continew.starter.extension.crud.service.BaseService;
|
||||
import top.continew.starter.file.excel.util.ExcelUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
@@ -65,7 +64,7 @@ import java.util.*;
|
||||
* @param <L> 列表类型
|
||||
* @param <D> 详情类型
|
||||
* @param <Q> 查询条件
|
||||
* @param <C> 创建或修改类型
|
||||
* @param <C> 创建或修改参数类型
|
||||
* @author Charles7c
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@@ -117,8 +117,8 @@ public class DefaultDataPermissionHandler implements DataPermissionHandler {
|
||||
* 构建本部门及以下数据权限表达式
|
||||
*
|
||||
* <p>
|
||||
* 处理完后的 SQL 示例:<br /> select t1.* from table as t1 where t1.dept_id in (select id from sys_dept where id =
|
||||
* xxx or find_in_set(xxx, ancestors));
|
||||
* 处理完后的 SQL 示例:<br /> select t1.* from table as t1 where t1.dept_id in (select id from sys_dept where id = xxx or
|
||||
* find_in_set(xxx, ancestors));
|
||||
* </p>
|
||||
*
|
||||
* @param dataPermission 数据权限
|
||||
@@ -194,8 +194,8 @@ public class DefaultDataPermissionHandler implements DataPermissionHandler {
|
||||
* 构建自定义数据权限表达式
|
||||
*
|
||||
* <p>
|
||||
* 处理完后的 SQL 示例:<br /> select t1.* from table as t1 where t1.dept_id in (select dept_id from sys_role_dept
|
||||
* where role_id = xxx);
|
||||
* 处理完后的 SQL 示例:<br /> select t1.* from table as t1 where t1.dept_id in (select dept_id from sys_role_dept where
|
||||
* role_id = xxx);
|
||||
* </p>
|
||||
*
|
||||
* @param dataPermission 数据权限
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package top.continew.starter.file.excel.converter;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
import com.alibaba.excel.converters.Converter;
|
||||
import com.alibaba.excel.enums.CellDataTypeEnum;
|
||||
import com.alibaba.excel.metadata.GlobalConfiguration;
|
||||
@@ -57,7 +57,7 @@ public class ExcelListConverter implements Converter<List> {
|
||||
ExcelContentProperty contentProperty,
|
||||
GlobalConfiguration globalConfiguration) {
|
||||
String stringValue = cellData.getStringValue();
|
||||
return StrUtil.split(stringValue, StringConstants.COMMA);
|
||||
return CharSequenceUtil.split(stringValue, StringConstants.COMMA);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
* 邮件配置
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user