mirror of
https://gitee.com/zhijiantianya/yudao-cloud.git
synced 2025-12-30 01:12:26 +00:00
Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/yudao-cloud
# Conflicts: # yudao-dependencies/pom.xml # yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/api/event/CrmContractStatusListener.java # yudao-module-bpm/yudao-module-bpm-server/src/main/java/cn/iocoder/yudao/module/bpm/api/event/CrmReceivableStatusListener.java
This commit is contained in:
@@ -1,12 +1,16 @@
|
||||
package cn.iocoder.yudao.framework.common.util.json.databind;
|
||||
|
||||
import cn.hutool.core.util.ReflectUtil;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* 基于时间戳的 LocalDateTime 序列化器
|
||||
@@ -19,7 +23,19 @@ public class TimestampLocalDateTimeSerializer extends JsonSerializer<LocalDateTi
|
||||
|
||||
@Override
|
||||
public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||
// 将 LocalDateTime 对象,转换为 Long 时间戳
|
||||
String fieldName = gen.getOutputContext().getCurrentName();
|
||||
Class<?> clazz = gen.getOutputContext().getCurrentValue().getClass();
|
||||
Field field = ReflectUtil.getField(clazz, fieldName);
|
||||
// 情况一:有 JsonFormat 自定义注解,则使用它。https://github.com/YunaiV/ruoyi-vue-pro/pull/1019
|
||||
JsonFormat[] jsonFormats = field.getAnnotationsByType(JsonFormat.class);
|
||||
if (jsonFormats.length > 0) {
|
||||
String pattern = jsonFormats[0].pattern();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
|
||||
gen.writeString(formatter.format(value));
|
||||
return;
|
||||
}
|
||||
|
||||
// 情况二:默认将 LocalDateTime 对象,转换为 Long 时间戳
|
||||
gen.writeNumber(value.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli());
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
|
||||
import cn.iocoder.yudao.framework.datapermission.core.aop.DataPermissionContextHolder;
|
||||
import com.fhs.trans.service.impl.SimpleTransService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
import java.util.Collections;
|
||||
@@ -31,32 +32,53 @@ public class DataPermissionRuleFactoryImpl implements DataPermissionRuleFactory
|
||||
|
||||
@Override // mappedStatementId 参数,暂时没有用。以后,可以基于 mappedStatementId + DataPermission 进行缓存
|
||||
public List<DataPermissionRule> getDataPermissionRule(String mappedStatementId) {
|
||||
// 1. 无数据权限
|
||||
// 1.1 无数据权限
|
||||
if (CollUtil.isEmpty(rules)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 2. 未配置,则默认开启
|
||||
// 1.2 未配置,则默认开启
|
||||
DataPermission dataPermission = DataPermissionContextHolder.get();
|
||||
if (dataPermission == null) {
|
||||
return rules;
|
||||
}
|
||||
// 3. 已配置,但禁用
|
||||
// 1.3 已配置,但禁用
|
||||
if (!dataPermission.enable()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 1.4 特殊:数据翻译时,强制忽略数据权限 https://github.com/YunaiV/ruoyi-vue-pro/issues/1007
|
||||
if (isTranslateCall()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
// 4. 已配置,只选择部分规则
|
||||
// 2.1 情况一:已配置,只选择部分规则
|
||||
if (ArrayUtil.isNotEmpty(dataPermission.includeRules())) {
|
||||
return rules.stream().filter(rule -> ArrayUtil.contains(dataPermission.includeRules(), rule.getClass()))
|
||||
.collect(Collectors.toList()); // 一般规则不会太多,所以不采用 HashSet 查询
|
||||
}
|
||||
// 5. 已配置,只排除部分规则
|
||||
// 2.2 已配置,只排除部分规则
|
||||
if (ArrayUtil.isNotEmpty(dataPermission.excludeRules())) {
|
||||
return rules.stream().filter(rule -> !ArrayUtil.contains(dataPermission.excludeRules(), rule.getClass()))
|
||||
.collect(Collectors.toList()); // 一般规则不会太多,所以不采用 HashSet 查询
|
||||
}
|
||||
// 6. 已配置,全部规则
|
||||
// 2.3 已配置,全部规则
|
||||
return rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为数据翻译 {@link com.fhs.core.trans.anno.Trans} 的调用
|
||||
*
|
||||
* 目前暂时只有这个办法,已经和 easy-trans 做过沟通
|
||||
*
|
||||
* @return 是否
|
||||
*/
|
||||
private boolean isTranslateCall() {
|
||||
StackTraceElement[] stack = Thread.currentThread().getStackTrace();
|
||||
for (StackTraceElement e : stack) {
|
||||
if (SimpleTransService.class.getName().equals(e.getClassName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package cn.iocoder.yudao.framework.excel.core.util;
|
||||
|
||||
import cn.idev.excel.FastExcelFactory;
|
||||
import cn.idev.excel.converters.longconverter.LongStringConverter;
|
||||
import cn.idev.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
|
||||
import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
|
||||
import cn.iocoder.yudao.framework.excel.core.handler.ColumnWidthMatchStyleStrategy;
|
||||
import cn.iocoder.yudao.framework.excel.core.handler.SelectSheetWriteHandler;
|
||||
@@ -10,6 +9,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -45,9 +45,12 @@ public class ExcelUtils {
|
||||
}
|
||||
|
||||
public static <T> List<T> read(MultipartFile file, Class<T> head) throws IOException {
|
||||
return FastExcelFactory.read(file.getInputStream(), head, null)
|
||||
.autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理
|
||||
.doReadAllSync();
|
||||
// 参考 https://t.zsxq.com/zM77F 帖子,增加 try 处理,兼容 windows 场景
|
||||
try (InputStream inputStream = file.getInputStream()) {
|
||||
return FastExcelFactory.read(inputStream, head, null)
|
||||
.autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理
|
||||
.doReadAllSync();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,7 +9,10 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.env.EnvironmentPostProcessor;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.MapPropertySource;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -56,11 +59,19 @@ public class IdTypeEnvironmentPostProcessor implements EnvironmentPostProcessor
|
||||
}
|
||||
|
||||
public IdType getIdType(ConfigurableEnvironment environment) {
|
||||
return environment.getProperty(ID_TYPE_KEY, IdType.class);
|
||||
String value = environment.getProperty(ID_TYPE_KEY);
|
||||
try {
|
||||
return StrUtil.isNotBlank(value) ? IdType.valueOf(value) : IdType.NONE;
|
||||
} catch (IllegalArgumentException ex) {
|
||||
log.error("[getIdType][无法解析 id-type 配置值({})]", value, ex);
|
||||
return IdType.NONE;
|
||||
}
|
||||
}
|
||||
|
||||
public void setIdType(ConfigurableEnvironment environment, IdType idType) {
|
||||
environment.getSystemProperties().put(ID_TYPE_KEY, idType);
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put(ID_TYPE_KEY, idType);
|
||||
environment.getPropertySources().addFirst(new MapPropertySource("mybatisPlusIdType", map));
|
||||
log.info("[setIdType][修改 MyBatis Plus 的 idType 为({})]", idType);
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,29 @@ public interface BaseMapperX<T> extends MPJBaseMapper<T> {
|
||||
return new PageResult<>(mpPage.getRecords(), mpPage.getTotal());
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行分页查询并返回结果。
|
||||
*
|
||||
* @param pageParam 分页参数,包含页码、每页条数和排序字段信息。如果 pageSize 为 {@link PageParam#PAGE_SIZE_NONE},则不分页,直接查询所有数据。
|
||||
* @param clazz 结果集的类类型
|
||||
* @param lambdaWrapper MyBatis Plus Join 查询条件包装器
|
||||
* @param <D> 结果集的泛型类型
|
||||
* @return 返回分页查询的结果,包括总记录数和当前页的数据列表
|
||||
*/
|
||||
default <D> PageResult<D> selectJoinPage(SortablePageParam pageParam, Class<D> clazz, MPJLambdaWrapper<T> lambdaWrapper) {
|
||||
// 特殊:不分页,直接查询全部
|
||||
if (PageParam.PAGE_SIZE_NONE.equals(pageParam.getPageSize())) {
|
||||
List<D> list = selectJoinList(clazz, lambdaWrapper);
|
||||
return new PageResult<>(list, (long) list.size());
|
||||
}
|
||||
|
||||
// MyBatis Plus Join 查询
|
||||
IPage<D> mpPage = MyBatisUtils.buildPage(pageParam, pageParam.getSortingFields());
|
||||
mpPage = selectJoinPage(mpPage, clazz, lambdaWrapper);
|
||||
// 转换返回
|
||||
return new PageResult<>(mpPage.getRecords(), mpPage.getTotal());
|
||||
}
|
||||
|
||||
default <DTO> PageResult<DTO> selectJoinPage(PageParam pageParam, Class<DTO> resultTypeClass, MPJBaseJoin<T> joinQueryWrapper) {
|
||||
IPage<DTO> mpPage = MyBatisUtils.buildPage(pageParam);
|
||||
selectJoinPage(mpPage, resultTypeClass, joinQueryWrapper);
|
||||
|
||||
@@ -42,15 +42,16 @@ public class ApiEncryptResponseWrapper extends HttpServletResponseWrapper {
|
||||
this.flushBuffer();
|
||||
byte[] body = byteArrayOutputStream.toByteArray();
|
||||
|
||||
// 2. 加密 body
|
||||
String encryptedBody = symmetricEncryptor != null ? symmetricEncryptor.encryptBase64(body)
|
||||
: asymmetricEncryptor.encryptBase64(body, KeyType.PublicKey);
|
||||
response.getWriter().write(encryptedBody);
|
||||
|
||||
// 3. 添加加密 header 标识
|
||||
// 2. 添加加密 header 标识
|
||||
this.addHeader(properties.getHeader(), "true");
|
||||
// 特殊:特殊:https://juejin.cn/post/6867327674675625992
|
||||
this.addHeader("Access-Control-Expose-Headers", properties.getHeader());
|
||||
|
||||
// 3.1 加密 body
|
||||
String encryptedBody = symmetricEncryptor != null ? symmetricEncryptor.encryptBase64(body)
|
||||
: asymmetricEncryptor.encryptBase64(body, KeyType.PublicKey);
|
||||
// 3.2 输出加密后的 body:(设置 header 要放在 response 的 write 之前)
|
||||
response.getWriter().write(encryptedBody);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -20,6 +20,8 @@ import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.util.AntPathMatcher;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
@@ -81,6 +83,7 @@ public class YudaoWebAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
|
||||
public GlobalExceptionHandler globalExceptionHandler(ApiErrorLogCommonApi apiErrorLogApi) {
|
||||
return new GlobalExceptionHandler(applicationName, apiErrorLogApi);
|
||||
}
|
||||
@@ -103,6 +106,7 @@ public class YudaoWebAutoConfiguration {
|
||||
* 创建 CorsFilter Bean,解决跨域问题
|
||||
*/
|
||||
@Bean
|
||||
@Order(value = WebFilterOrderEnum.CORS_FILTER) // 特殊:修复因执行顺序影响到跨域配置不生效问题
|
||||
public FilterRegistrationBean<CorsFilter> corsFilterBean() {
|
||||
// 创建 CorsConfiguration 对象
|
||||
CorsConfiguration config = new CorsConfiguration();
|
||||
@@ -146,9 +150,20 @@ public class YudaoWebAutoConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@LoadBalanced
|
||||
@Primary
|
||||
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
|
||||
return restTemplateBuilder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建 RestTemplate 实例(支持负载均衡)
|
||||
*
|
||||
* @param restTemplateBuilder {@link RestTemplateAutoConfiguration#restTemplateBuilder}
|
||||
*/
|
||||
@Bean
|
||||
@LoadBalanced
|
||||
public RestTemplate loadBalancedRestTemplate(RestTemplateBuilder restTemplateBuilder) {
|
||||
return restTemplateBuilder.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user