Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
吴泽威
2025-01-17 09:15:54 +08:00
29 changed files with 204 additions and 655 deletions

View File

@@ -1,3 +1,48 @@
## [v2.8.3](https://github.com/continew-org/continew-starter/compare/v2.8.2...v2.8.3) (2025-01-16)
### 💎 功能优化
- 【log】调整所属模块、日志描述默认提示不再直接抛出异常 ([326dd76](https://github.com/continew-org/continew-starter/commit/326dd76c34476141c39add5348da052bdb8c27cd))
- 【extension/crud】移除 BaseDO、BaseCreateDO、BaseUpdateDO 等(已移动到 Admin 项目内) ([6e621bc](https://github.com/continew-org/continew-starter/commit/6e621bc4597996c8f1f65c542f5faa922b95a900))
### 🐛 问题修复
- 【extension/crud】修复查询条件校验无效的问题 ([d771e12](https://github.com/continew-org/continew-starter/commit/d771e128399851fa78f1041fa4ffcd6af3332fcd))
## [v2.8.2](https://github.com/continew-org/continew-starter/compare/v2.8.1...v2.8.2) (2025-01-09)
### ✨ 新特性
- 【core】SpringUtils 新增获取代理对象方法 ([5f68227](https://github.com/continew-org/continew-starter/commit/5f6822742fd0f032bcc351155f0b966d24b05346))
### 💎 功能优化
- 【extension/crud】移除 CommonUserService、ContainerPool已移动到 Admin 项目内) ([0b342d5](https://github.com/continew-org/continew-starter/commit/0b342d5c73e95b809337b939b4e1e957374bad85))
### 🐛 问题修复
- 【log】修复日志记录时所属模块和描述取值优先级失效的问题 ([4fe067a](https://github.com/continew-org/continew-starter/commit/4fe067a889f00617f03caf7ae3598466560dce33))
### 📦 依赖升级
- graceful-response 5.0.4-boot3 => 5.0.5-boot3修复父类参数校验异常 ([aa463df](https://github.com/continew-org/continew-starter/commit/aa463dff37b658d1cb2a69e68f54790e03c4103d))
## [v2.8.1](https://github.com/continew-org/continew-starter/compare/v2.8.0...v2.8.1) (2025-01-06)
### ✨ 新特性
- 【core】BaseEnum 新增 getByValue、getByDescription、isValidValue 方法 ([279d72b](https://github.com/continew-org/continew-starter/commit/279d72b7242bf996f9b88d38ed0ea7aa0a0d1c46))
### 💎 功能优化
- 【extension/crud】移除 BaseResp、BaseDetailResp已移动到 Admin 项目内) ([eb2cac5](https://github.com/continew-org/continew-starter/commit/eb2cac54f75b2850f2957b32190d12e63377c185))
- 【log】优化日志处理器解析 description、module 方法 ([a6c9d33](https://github.com/continew-org/continew-starter/commit/a6c9d33024ea70bb3dbe11981cbc9a3f9027bcd2))
- 解决 Sonar 问题,替换部分过期 API ([80c0700](https://github.com/continew-org/continew-starter/commit/80c070093498abb8dff5529d177e1e2519577bf0))
### 🐛 问题修复
- 【file/excel】优化 BaseEnum 转换器 (GitHub#10@Solution-Lin) ([b9779e8](https://github.com/continew-org/continew-starter/commit/b9779e894464ec534bebdd230a7239b6d1964ddb))
## [v2.8.0](https://github.com/continew-org/continew-starter/compare/v2.7.5...v2.8.0) (2024-12-25)
### ✨ 新特性

View File

@@ -36,7 +36,6 @@ import org.springdoc.core.service.SecurityService;
import org.springdoc.core.utils.PropertyResolverUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.web.method.HandlerMethod;
import java.io.StringReader;
@@ -152,7 +151,7 @@ public class OpenApiHandler extends OpenAPIService {
if (this.openAPI.getPaths() == null) {
this.openAPI.setPaths(new Paths());
}
if (!CollectionUtils.isEmpty(this.openAPI.getServers())) {
if (CollUtil.isNotEmpty(this.openAPI.getServers())) {
this.isServersPresent = true;
}
}
@@ -176,7 +175,7 @@ public class OpenApiHandler extends OpenAPIService {
buildTagsFromMethod(handlerMethod.getMethod(), tags, tagsStr, locale);
buildTagsFromClass(handlerMethod.getBeanType(), tags, tagsStr, locale);
if (!CollectionUtils.isEmpty(tagsStr)) {
if (CollUtil.isNotEmpty(tagsStr)) {
tagsStr = tagsStr.stream()
.map(str -> propertyResolverUtils.resolve(str, locale))
.collect(Collectors.toSet());
@@ -190,8 +189,8 @@ public class OpenApiHandler extends OpenAPIService {
}
}
if (!CollectionUtils.isEmpty(tagsStr)) {
if (CollectionUtils.isEmpty(operation.getTags())) {
if (CollUtil.isNotEmpty(tagsStr)) {
if (CollUtil.isEmpty(operation.getTags())) {
operation.setTags(new ArrayList<>(tagsStr));
} else {
Set<String> operationTagsSet = new HashSet<>(operation.getTags());
@@ -225,10 +224,10 @@ public class OpenApiHandler extends OpenAPIService {
}
}
if (!CollectionUtils.isEmpty(tags)) {
if (CollUtil.isNotEmpty(tags)) {
// Existing tags
List<Tag> openApiTags = openAPI.getTags();
if (!CollectionUtils.isEmpty(openApiTags)) {
if (CollUtil.isNotEmpty(openApiTags)) {
tags.addAll(openApiTags);
}
openAPI.setTags(new ArrayList<>(tags));
@@ -256,7 +255,7 @@ public class OpenApiHandler extends OpenAPIService {
.collect(Collectors.toSet());
methodTags.addAll(AnnotatedElementUtils
.findAllMergedAnnotations(method, io.swagger.v3.oas.annotations.tags.Tag.class));
if (!CollectionUtils.isEmpty(methodTags)) {
if (CollUtil.isNotEmpty(methodTags)) {
tagsStr.addAll(toSet(methodTags, tag -> propertyResolverUtils.resolve(tag.name(), locale)));
List<io.swagger.v3.oas.annotations.tags.Tag> allTags = new ArrayList<>(methodTags);
addTags(allTags, tags, locale);

View File

@@ -19,6 +19,7 @@ package top.continew.starter.cache.redisson.util;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.extra.spring.SpringUtil;
import org.redisson.api.*;
import org.redisson.api.options.KeysScanOptions;
import top.continew.starter.core.constant.StringConstants;
import java.time.Duration;
@@ -191,7 +192,9 @@ public class RedisUtils {
* @return 缓存列表
*/
public static Collection<String> keys(String pattern) {
return CLIENT.getKeys().getKeysStreamByPattern(pattern).toList();
KeysScanOptions options = KeysScanOptions.defaults();
options.pattern(pattern);
return CLIENT.getKeys().getKeysStream(options).toList();
}
/**
@@ -365,8 +368,21 @@ public class RedisUtils {
* @return true成功false失败
*/
public static boolean rateLimit(String key, RateType rateType, int rate, int rateInterval) {
return rateLimit(key, rateType, rate, Duration.ofSeconds(rateInterval));
}
/**
* 限流
*
* @param key 键
* @param rateType 限流类型OVERALL全局限流PER_CLIENT单机限流
* @param rate 速率(指定时间间隔产生的令牌数)
* @param rateInterval 速率间隔(时间间隔)
* @return true成功false失败
*/
public static boolean rateLimit(String key, RateType rateType, int rate, Duration rateInterval) {
RRateLimiter rateLimiter = CLIENT.getRateLimiter(key);
rateLimiter.trySetRate(rateType, rate, rateInterval, RateIntervalUnit.SECONDS);
rateLimiter.trySetRate(rateType, rate, rateInterval);
return rateLimiter.tryAcquire(1);
}

View File

@@ -67,7 +67,6 @@ public class AsyncAutoConfiguration implements AsyncConfigurer {
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return (throwable, method, objects) -> {
throwable.printStackTrace();
StringBuilder sb = new StringBuilder();
sb.append("Exception message: ")
.append(throwable.getMessage())

View File

@@ -17,6 +17,7 @@
package top.continew.starter.core.enums;
import java.io.Serializable;
import java.util.Objects;
/**
* 枚举接口
@@ -49,4 +50,50 @@ public interface BaseEnum<T extends Serializable> {
default String getColor() {
return null;
}
/**
* 根据枚举值获取
*
* @param value 枚举值
* @param clazz 枚举类
* @return 枚举对象
* @since 2.8.1
*/
static <E extends Enum<E> & BaseEnum, T> E getByValue(T value, Class<E> clazz) {
for (E e : clazz.getEnumConstants()) {
if (Objects.equals(e.getValue(), value)) {
return e;
}
}
return null;
}
/**
* 根据枚举描述获取
*
* @param description 枚举描述
* @param clazz 枚举类
* @return 枚举对象
* @since 2.8.1
*/
static <E extends Enum<E> & BaseEnum> E getByDescription(String description, Class<?> clazz) {
for (Object e : clazz.getEnumConstants()) {
if (e instanceof BaseEnum<?> baseEnum && Objects.equals(baseEnum.getDescription(), description)) {
return (E)baseEnum;
}
}
return null;
}
/**
* 判断枚举值是否有效
*
* @param value 枚举值
* @param clazz 枚举类
* @return 是否有效
* @since 2.8.1
*/
static <E extends Enum<E> & BaseEnum, T> boolean isValidValue(T value, Class<E> clazz) {
return getByValue(value, clazz) != null;
}
}

View File

@@ -14,21 +14,30 @@
* limitations under the License.
*/
package top.continew.starter.extension.crud.constant;
package top.continew.starter.core.util;
import cn.hutool.extra.spring.SpringUtil;
/**
* 数据源容器相关常量Crane4j 数据填充组件使用
* Spring 工具类
*
* @author Charles7c
* @since 1.2.0
* @since 2.8.2
*/
public class ContainerPool {
public class SpringUtils {
private SpringUtils() {
}
/**
* 用户昵称
* 获取代理对象
*
* @param target 目标对象
* @param <T> 目标对象类型
* @return 代理对象
* @since 2.8.2
*/
public static final String USER_NICKNAME = "UserNickname";
protected ContainerPool() {
public static <T> T getProxy(T target) {
return (T)SpringUtil.getBean(target.getClass());
}
}
}

View File

@@ -183,12 +183,11 @@ public class QueryWrapperHelper {
* @param queryType 查询类型
* @param columnName 列名
* @param fieldValue 字段值
* @param <R> 查询数据类型
*/
private static <R> void parse(QueryType queryType,
String columnName,
Object fieldValue,
List<Consumer<QueryWrapper>> consumers) {
private static void parse(QueryType queryType,
String columnName,
Object fieldValue,
List<Consumer<QueryWrapper>> consumers) {
switch (queryType) {
case EQ -> consumers.add(q -> q.eq(columnName, fieldValue));
case NE -> consumers.add(q -> q.ne(columnName, fieldValue));

View File

@@ -43,7 +43,7 @@
<properties>
<!-- 项目版本号 -->
<revision>2.8.0</revision>
<revision>2.8.3</revision>
<snail-job.version>1.2.0</snail-job.version>
<sa-token.version>1.39.0</sa-token.version>
<just-auth.version>1.16.7</just-auth.version>
@@ -61,7 +61,7 @@
<nashorn.version>15.5</nashorn.version>
<x-file-storage.version>2.2.1</x-file-storage.version>
<aws-s3.version>1.12.780</aws-s3.version>
<graceful-response.version>5.0.4-boot3</graceful-response.version>
<graceful-response.version>5.0.5-boot3</graceful-response.version>
<crane4j.version>2.9.0</crane4j.version>
<knife4j.version>4.5.0</knife4j.version>
<tlog.version>1.5.2</tlog.version>

View File

@@ -30,7 +30,6 @@ 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.BasePageResp;
import top.continew.starter.extension.crud.service.BaseService;
@@ -49,7 +48,7 @@ import java.util.List;
* @author Charles7c
* @since 1.0.0
*/
public abstract class AbstractBaseController<S extends BaseService<L, D, Q, C>, L, D, Q, C extends BaseReq> implements CrudApiHandler {
public abstract class AbstractBaseController<S extends BaseService<L, D, Q, C>, L, D, Q, C> implements CrudApiHandler {
@Autowired
protected S baseService;
@@ -65,7 +64,7 @@ public abstract class AbstractBaseController<S extends BaseService<L, D, Q, C>,
@Operation(summary = "分页查询列表", description = "分页查询列表")
@ResponseBody
@GetMapping
public BasePageResp<L> page(Q query, @Validated PageQuery pageQuery) {
public BasePageResp<L> page(@Validated Q query, @Validated PageQuery pageQuery) {
return baseService.page(query, pageQuery);
}
@@ -80,7 +79,7 @@ public abstract class AbstractBaseController<S extends BaseService<L, D, Q, C>,
@Operation(summary = "查询列表", description = "查询列表")
@ResponseBody
@GetMapping("/list")
public List<L> list(Q query, SortQuery sortQuery) {
public List<L> list(@Validated Q query, @Validated SortQuery sortQuery) {
return baseService.list(query, sortQuery);
}
@@ -95,7 +94,7 @@ public abstract class AbstractBaseController<S extends BaseService<L, D, Q, C>,
@Operation(summary = "查询树列表", description = "查询树列表")
@ResponseBody
@GetMapping("/tree")
public List<Tree<Long>> tree(Q query, SortQuery sortQuery) {
public List<Tree<Long>> tree(@Validated Q query, @Validated SortQuery sortQuery) {
return baseService.tree(query, sortQuery, false);
}
@@ -168,7 +167,7 @@ public abstract class AbstractBaseController<S extends BaseService<L, D, Q, C>,
@ExcludeFromGracefulResponse
@Operation(summary = "导出数据", description = "导出数据")
@GetMapping("/export")
public void export(Q query, SortQuery sortQuery, HttpServletResponse response) {
public void export(@Validated Q query, @Validated SortQuery sortQuery, HttpServletResponse response) {
baseService.export(query, sortQuery, response);
}
}

View File

@@ -1,32 +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.model.req;
import java.io.Serial;
import java.io.Serializable;
/**
* 请求参数基类
*
* @author Charles7c
* @since 1.0.0
*/
public class BaseReq implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
}

View File

@@ -1,39 +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.crane4j.annotation.ContainerMethod;
import cn.crane4j.annotation.MappingType;
import top.continew.starter.extension.crud.constant.ContainerPool;
/**
* 公共用户业务接口
*
* @author Charles7c
* @since 1.0.0
*/
public interface CommonUserService {
/**
* 根据 ID 查询昵称
*
* @param id ID
* @return 昵称
*/
@ContainerMethod(namespace = ContainerPool.USER_NICKNAME, type = MappingType.ORDER_OF_KEYS)
String getNicknameById(Long id);
}

View File

@@ -1,62 +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.model.entity;
import java.io.Serial;
import java.time.LocalDateTime;
/**
* 实体类基类
*
* <p>
* 通用字段:创建人、创建时间
* </p>
*
* @author Charles7c
* @since 2.0.1
*/
public class BaseCreateDO extends BaseIdDO {
@Serial
private static final long serialVersionUID = 1L;
/**
* 创建人
*/
private Long createUser;
/**
* 创建时间
*/
private LocalDateTime createTime;
public Long getCreateUser() {
return createUser;
}
public void setCreateUser(Long createUser) {
this.createUser = createUser;
}
public LocalDateTime getCreateTime() {
return createTime;
}
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
}

View File

@@ -1,84 +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.model.entity;
import java.io.Serial;
import java.time.LocalDateTime;
/**
* 实体类基类
*
* @author Charles7c
* @since 1.0.0
*/
public class BaseDO extends BaseIdDO {
@Serial
private static final long serialVersionUID = 1L;
/**
* 创建人
*/
private Long createUser;
/**
* 创建时间
*/
private LocalDateTime createTime;
/**
* 修改人
*/
private Long updateUser;
/**
* 修改时间
*/
private LocalDateTime updateTime;
public Long getCreateUser() {
return createUser;
}
public void setCreateUser(Long createUser) {
this.createUser = createUser;
}
public LocalDateTime getCreateTime() {
return createTime;
}
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
public Long getUpdateUser() {
return updateUser;
}
public void setUpdateUser(Long updateUser) {
this.updateUser = updateUser;
}
public LocalDateTime getUpdateTime() {
return updateTime;
}
public void setUpdateTime(LocalDateTime updateTime) {
this.updateTime = updateTime;
}
}

View File

@@ -1,62 +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.model.entity;
import java.io.Serial;
import java.time.LocalDateTime;
/**
* 实体类基类
*
* <p>
* 通用字段:创建人、创建时间
* </p>
*
* @author Charles7c
* @since 2.0.1
*/
public class BaseUpdateDO extends BaseIdDO {
@Serial
private static final long serialVersionUID = 1L;
/**
* 修改人
*/
private Long updateUser;
/**
* 修改时间
*/
private LocalDateTime updateTime;
public Long getUpdateUser() {
return updateUser;
}
public void setUpdateUser(Long updateUser) {
this.updateUser = updateUser;
}
public LocalDateTime getUpdateTime() {
return updateTime;
}
public void setUpdateTime(LocalDateTime updateTime) {
this.updateTime = updateTime;
}
}

View File

@@ -1,67 +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.model.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import java.io.Serial;
import java.time.LocalDateTime;
/**
* 实体类基类
*
* <p>
* 通用字段:创建人、创建时间
* </p>
*
* @author Charles7c
* @since 2.0.1
*/
public class BaseCreateDO extends BaseIdDO {
@Serial
private static final long serialVersionUID = 1L;
/**
* 创建人
*/
@TableField(fill = FieldFill.INSERT)
private Long createUser;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
public Long getCreateUser() {
return createUser;
}
public void setCreateUser(Long createUser) {
this.createUser = createUser;
}
public LocalDateTime getCreateTime() {
return createTime;
}
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
}

View File

@@ -1,91 +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.model.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import java.io.Serial;
import java.time.LocalDateTime;
/**
* 实体类基类
*
* @author Charles7c
* @since 1.0.0
*/
public class BaseDO extends BaseIdDO {
@Serial
private static final long serialVersionUID = 1L;
/**
* 创建人
*/
@TableField(fill = FieldFill.INSERT)
private Long createUser;
/**
* 创建时间
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;
/**
* 修改人
*/
@TableField(fill = FieldFill.UPDATE)
private Long updateUser;
/**
* 修改时间
*/
@TableField(fill = FieldFill.UPDATE)
private LocalDateTime updateTime;
public Long getCreateUser() {
return createUser;
}
public void setCreateUser(Long createUser) {
this.createUser = createUser;
}
public LocalDateTime getCreateTime() {
return createTime;
}
public void setCreateTime(LocalDateTime createTime) {
this.createTime = createTime;
}
public Long getUpdateUser() {
return updateUser;
}
public void setUpdateUser(Long updateUser) {
this.updateUser = updateUser;
}
public LocalDateTime getUpdateTime() {
return updateTime;
}
public void setUpdateTime(LocalDateTime updateTime) {
this.updateTime = updateTime;
}
}

View File

@@ -1,67 +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.model.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import java.io.Serial;
import java.time.LocalDateTime;
/**
* 实体类基类
*
* <p>
* 通用字段:创建人、创建时间
* </p>
*
* @author Charles7c
* @since 2.0.1
*/
public class BaseUpdateDO extends BaseIdDO {
@Serial
private static final long serialVersionUID = 1L;
/**
* 修改人
*/
@TableField(fill = FieldFill.UPDATE)
private Long updateUser;
/**
* 修改时间
*/
@TableField(fill = FieldFill.UPDATE)
private LocalDateTime updateTime;
public Long getUpdateUser() {
return updateUser;
}
public void setUpdateUser(Long updateUser) {
this.updateUser = updateUser;
}
public LocalDateTime getUpdateTime() {
return updateTime;
}
public void setUpdateTime(LocalDateTime updateTime) {
this.updateTime = updateTime;
}
}

View File

@@ -16,8 +16,6 @@
package top.continew.starter.file.excel.converter;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ClassUtil;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
@@ -34,7 +32,7 @@ import top.continew.starter.core.enums.BaseEnum;
* @see BaseEnum
* @since 1.2.0
*/
public class ExcelBaseEnumConverter implements Converter<BaseEnum<Integer>> {
public class ExcelBaseEnumConverter implements Converter<BaseEnum<?>> {
@Override
public Class<BaseEnum> supportJavaTypeKey() {
@@ -50,17 +48,17 @@ public class ExcelBaseEnumConverter implements Converter<BaseEnum<Integer>> {
* 转换为 Java 数据(读取 Excel
*/
@Override
public BaseEnum convertToJavaData(ReadCellData<?> cellData,
ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
return this.getEnum(BaseEnum.class, Convert.toStr(cellData.getData()));
public BaseEnum<?> convertToJavaData(ReadCellData<?> cellData,
ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
return BaseEnum.getByDescription(cellData.getStringValue(), contentProperty.getField().getType());
}
/**
* 转换为 Excel 数据(写入 Excel
*/
@Override
public WriteCellData<String> convertToExcelData(BaseEnum<Integer> value,
public WriteCellData<String> convertToExcelData(BaseEnum<?> value,
ExcelContentProperty contentProperty,
GlobalConfiguration globalConfiguration) {
if (null == value) {
@@ -68,24 +66,4 @@ public class ExcelBaseEnumConverter implements Converter<BaseEnum<Integer>> {
}
return new WriteCellData<>(value.getDescription());
}
/**
* 通过 value 获取枚举对象,获取不到时为 {@code null}
*
* @param enumType 枚举类型
* @param description 描述
* @return 对应枚举 ,获取不到时为 {@code null}
*/
private BaseEnum<Integer> getEnum(Class<?> enumType, String description) {
Object[] enumConstants = enumType.getEnumConstants();
for (Object enumConstant : enumConstants) {
if (ClassUtil.isAssignable(BaseEnum.class, enumType)) {
BaseEnum<Integer> baseEnum = (BaseEnum<Integer>)enumConstant;
if (baseEnum.getDescription().equals(description)) {
return baseEnum;
}
}
}
return null;
}
}

View File

@@ -16,11 +16,7 @@
package top.continew.starter.log.handler;
import cn.hutool.core.text.CharSequenceUtil;
import top.continew.starter.log.AbstractLogHandler;
import top.continew.starter.log.model.LogRecord;
import java.lang.reflect.Method;
/**
* 日志处理器-AOP 版实现
@@ -29,20 +25,4 @@ import java.lang.reflect.Method;
* @since 2.8.0
*/
public class AopLogHandler extends AbstractLogHandler {
@Override
public void logDescription(LogRecord logRecord, Method targetMethod) {
super.logDescription(logRecord, targetMethod);
if (CharSequenceUtil.isBlank(logRecord.getDescription())) {
logRecord.setDescription("请在该接口方法上指定日志描述");
}
}
@Override
public void logModule(LogRecord logRecord, Method targetMethod, Class<?> targetClass) {
super.logModule(logRecord, targetMethod, targetClass);
if (CharSequenceUtil.isBlank(logRecord.getModule())) {
logRecord.setModule("请在该接口类上指定所属模块");
}
}
}

View File

@@ -11,4 +11,12 @@
<artifactId>continew-starter-log-core</artifactId>
<description>ContiNew Starter 日志模块 - 核心模块</description>
<dependencies>
<!-- Swagger 注解 -->
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations-jakarta</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -18,6 +18,8 @@ package top.continew.starter.log;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.text.CharSequenceUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import top.continew.starter.log.annotation.Log;
@@ -80,10 +82,17 @@ public abstract class AbstractLogHandler implements LogHandler {
*/
@Override
public void logDescription(LogRecord logRecord, Method targetMethod) {
logRecord.setDescription("请在该接口方法上添加 @top.continew.starter.log.annotation.Log(value) 来指定日志描述");
Log methodLog = AnnotationUtil.getAnnotation(targetMethod, Log.class);
// 例如:@Log("新增部门") -> 新增部门
if (null != methodLog && CharSequenceUtil.isNotBlank(methodLog.value())) {
logRecord.setDescription(methodLog.value());
return;
}
// 例如:@Operation(summary="新增部门") -> 新增部门
Operation methodOperation = AnnotationUtil.getAnnotation(targetMethod, Operation.class);
if (null != methodOperation && CharSequenceUtil.isNotBlank(methodOperation.summary())) {
logRecord.setDescription(methodOperation.summary());
}
}
@@ -96,6 +105,7 @@ public abstract class AbstractLogHandler implements LogHandler {
*/
@Override
public void logModule(LogRecord logRecord, Method targetMethod, Class<?> targetClass) {
logRecord.setModule("请在该接口方法或类上添加 @top.continew.starter.log.annotation.Log(module) 来指定所属模块");
Log methodLog = AnnotationUtil.getAnnotation(targetMethod, Log.class);
// 例如:@Log(module = "部门管理") -> 部门管理
// 方法级注解优先级高于类级注解
@@ -106,6 +116,12 @@ public abstract class AbstractLogHandler implements LogHandler {
Log classLog = AnnotationUtil.getAnnotation(targetClass, Log.class);
if (null != classLog && CharSequenceUtil.isNotBlank(classLog.module())) {
logRecord.setModule(classLog.module());
return;
}
// 例如:@Tag(name = "部门管理") -> 部门管理
Tag classTag = AnnotationUtil.getAnnotation(targetClass, Tag.class);
if (null != classTag && CharSequenceUtil.isNotBlank(classTag.name())) {
logRecord.setModule(classTag.name());
}
}

View File

@@ -13,12 +13,6 @@
<description>ContiNew Starter 日志模块 - 基于拦截器实现Spring Boot Actuator HttpTrace 增强版)</description>
<dependencies>
<!-- Swagger 注解 -->
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations-jakarta</artifactId>
</dependency>
<!-- TTL线程间传递 ThreadLocal异步执行时上下文传递的解决方案 -->
<dependency>
<groupId>com.alibaba</groupId>

View File

@@ -16,14 +16,7 @@
package top.continew.starter.log.handler;
import cn.hutool.core.annotation.AnnotationUtil;
import cn.hutool.core.text.CharSequenceUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import top.continew.starter.log.AbstractLogHandler;
import top.continew.starter.log.model.LogRecord;
import java.lang.reflect.Method;
/**
* 日志处理器-拦截器版实现
@@ -32,31 +25,4 @@ import java.lang.reflect.Method;
* @since 2.8.0
*/
public class InterceptorLogHandler extends AbstractLogHandler {
@Override
public void logDescription(LogRecord logRecord, Method targetMethod) {
super.logDescription(logRecord, targetMethod);
if (CharSequenceUtil.isNotBlank(logRecord.getDescription())) {
return;
}
// 例如:@Operation(summary="新增部门") -> 新增部门
Operation methodOperation = AnnotationUtil.getAnnotation(targetMethod, Operation.class);
if (null != methodOperation) {
logRecord.setDescription(CharSequenceUtil.blankToDefault(methodOperation.summary(), "请在该接口方法上指定日志描述"));
}
}
@Override
public void logModule(LogRecord logRecord, Method targetMethod, Class<?> targetClass) {
super.logModule(logRecord, targetMethod, targetClass);
if (CharSequenceUtil.isNotBlank(logRecord.getModule())) {
return;
}
// 例如:@Tag(name = "部门管理") -> 部门管理
Tag classTag = AnnotationUtil.getAnnotation(targetClass, Tag.class);
if (null != classTag) {
String name = classTag.name();
logRecord.setModule(CharSequenceUtil.blankToDefault(name, "请在该接口类上指定所属模块"));
}
}
}

View File

@@ -37,9 +37,9 @@ public enum Algorithm {
DES(DesEncryptor.class),
/**
* PBEWithMD5AndDES
* PBE With MD5 And DES
*/
PBEWithMD5AndDES(PbeWithMd5AndDesEncryptor.class),
PBE_WITH_MD5_AND_DES(PbeWithMd5AndDesEncryptor.class),
/**
* RSA

View File

@@ -16,10 +16,10 @@
package top.continew.starter.security.limiter.annotation;
import org.redisson.api.RateIntervalUnit;
import top.continew.starter.security.limiter.enums.LimitType;
import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
/**
* 限流注解
@@ -60,7 +60,7 @@ public @interface RateLimiter {
/**
* 速率间隔时间单位(默认:毫秒)
*/
RateIntervalUnit unit() default RateIntervalUnit.MILLISECONDS;
TimeUnit unit() default TimeUnit.MILLISECONDS;
/**
* 提示信息

View File

@@ -39,6 +39,7 @@ import top.continew.starter.security.limiter.exception.RateLimiterException;
import top.continew.starter.web.util.SpringWebUtils;
import java.lang.reflect.Method;
import java.time.Duration;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
@@ -129,12 +130,11 @@ public class RateLimiterAspect {
// 限流器配置
RateType rateType = rateLimiter.type() == LimitType.CLUSTER ? RateType.PER_CLIENT : RateType.OVERALL;
int rate = rateLimiter.rate();
int rateInterval = rateLimiter.interval();
RateIntervalUnit rateIntervalUnit = rateLimiter.unit();
Duration rateInterval = Duration.ofMillis(rateLimiter.unit().toMillis(rateLimiter.interval()));
// 判断是否需要更新限流器
if (this.isConfigurationUpdateNeeded(rRateLimiter, rateType, rate, rateInterval, rateIntervalUnit)) {
if (this.isConfigurationUpdateNeeded(rRateLimiter, rateType, rate, rateInterval)) {
// 更新限流器
rRateLimiter.setRate(rateType, rate, rateInterval, rateIntervalUnit);
rRateLimiter.setRate(rateType, rate, rateInterval);
}
// 尝试获取令牌
return !rRateLimiter.tryAcquire();
@@ -181,20 +181,18 @@ public class RateLimiterAspect {
/**
* 判断是否需要更新限流器配置
*
* @param rRateLimiter 限流器
* @param rateType 限流类型OVERALL全局限流PER_CLIENT单机限流
* @param rate 速率(指定时间间隔产生的令牌数)
* @param rateInterval 速率间隔
* @param rateIntervalUnit 时间单位
* @param rRateLimiter 限流器
* @param rateType 限流类型OVERALL全局限流PER_CLIENT单机限流
* @param rate 速率(指定时间间隔产生的令牌数)
* @param rateInterval 速率间隔
* @return 是否需要更新配置
*/
private boolean isConfigurationUpdateNeeded(RRateLimiter rRateLimiter,
RateType rateType,
long rate,
long rateInterval,
RateIntervalUnit rateIntervalUnit) {
Duration rateInterval) {
RateLimiterConfig config = rRateLimiter.getConfig();
return !Objects.equals(config.getRateType(), rateType) || !Objects.equals(config.getRate(), rate) || !Objects
.equals(config.getRateInterval(), rateIntervalUnit.toMillis(rateInterval));
.equals(config.getRateInterval(), rateInterval.toMillis());
}
}

View File

@@ -34,7 +34,7 @@ public enum MaskType implements IMaskStrategy {
CUSTOM {
@Override
public String mask(String str, char character, int left, int right) {
return CharSequenceUtil.replace(str, left, str.length() - right, character);
return CharSequenceUtil.replaceByCodePoint(str, left, str.length() - right, character);
}
},
@@ -45,7 +45,7 @@ public enum MaskType implements IMaskStrategy {
MOBILE_PHONE {
@Override
public String mask(String str, char character, int left, int right) {
return CharSequenceUtil.replace(str, 3, str.length() - 4, character);
return CharSequenceUtil.replaceByCodePoint(str, 3, str.length() - 4, character);
}
},
@@ -58,7 +58,7 @@ public enum MaskType implements IMaskStrategy {
FIXED_PHONE {
@Override
public String mask(String str, char character, int left, int right) {
return CharSequenceUtil.replace(str, 4, str.length() - 2, character);
return CharSequenceUtil.replaceByCodePoint(str, 4, str.length() - 2, character);
}
},
@@ -76,7 +76,7 @@ public enum MaskType implements IMaskStrategy {
if (index <= 1) {
return str;
}
return CharSequenceUtil.replace(str, 1, index, character);
return CharSequenceUtil.replaceByCodePoint(str, 1, index, character);
}
},
@@ -89,7 +89,7 @@ public enum MaskType implements IMaskStrategy {
ID_CARD {
@Override
public String mask(String str, char character, int left, int right) {
return CharSequenceUtil.replace(str, 1, str.length() - 2, character);
return CharSequenceUtil.replaceByCodePoint(str, 1, str.length() - 2, character);
}
},
@@ -140,11 +140,11 @@ public enum MaskType implements IMaskStrategy {
// 普通车牌
int length = str.length();
if (length == 7) {
return CharSequenceUtil.replace(str, 3, 6, character);
return CharSequenceUtil.replaceByCodePoint(str, 3, 6, character);
}
// 新能源车牌
if (length == 8) {
return CharSequenceUtil.replace(str, 3, 7, character);
return CharSequenceUtil.replaceByCodePoint(str, 3, 7, character);
}
return str;
}
@@ -159,7 +159,7 @@ public enum MaskType implements IMaskStrategy {
CHINESE_NAME {
@Override
public String mask(String str, char character, int left, int right) {
return CharSequenceUtil.replace(str, 1, str.length(), character);
return CharSequenceUtil.replaceByCodePoint(str, 1, str.length(), character);
}
},
@@ -186,7 +186,7 @@ public enum MaskType implements IMaskStrategy {
@Override
public String mask(String str, char character, int left, int right) {
int length = str.length();
return CharSequenceUtil.replace(str, length - 8, length, character);
return CharSequenceUtil.replaceByCodePoint(str, length - 8, length, character);
}
},

View File

@@ -16,7 +16,7 @@
package top.continew.starter.web.autoconfigure.xss;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.CollUtil;
import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
@@ -55,14 +55,14 @@ public class XssFilter implements Filter {
if (servletRequest instanceof HttpServletRequest request && xssProperties.isEnabled()) {
// 放行路由:忽略 XSS 过滤
List<String> excludePatterns = xssProperties.getExcludePatterns();
if (CollectionUtil.isNotEmpty(excludePatterns) && SpringWebUtils.isMatch(request
if (CollUtil.isNotEmpty(excludePatterns) && SpringWebUtils.isMatch(request
.getServletPath(), excludePatterns)) {
filterChain.doFilter(request, servletResponse);
return;
}
// 拦截路由:执行 XSS 过滤
List<String> includePatterns = xssProperties.getIncludePatterns();
if (CollectionUtil.isNotEmpty(includePatterns)) {
if (CollUtil.isNotEmpty(includePatterns)) {
if (SpringWebUtils.isMatch(request.getServletPath(), includePatterns)) {
filterChain.doFilter(new XssServletRequestWrapper(request, xssProperties), servletResponse);
} else {

View File

@@ -16,7 +16,7 @@
package top.continew.starter.web.autoconfigure.xss;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ArrayUtil;
@@ -110,7 +110,7 @@ public class XssServletRequestWrapper extends HttpServletRequestWrapper {
// 转义
if (XssMode.ESCAPE.equals(mode)) {
List<String> reStr = ReUtil.findAllGroup0(HtmlUtil.RE_HTML_MARK, content);
if (CollectionUtil.isEmpty(reStr)) {
if (CollUtil.isEmpty(reStr)) {
return content;
}
for (String s : reStr) {