Merge branch 'master-jdk17' of https://gitee.com/zhijiantianya/yudao-cloud
# Conflicts: # yudao-framework/yudao-spring-boot-starter-web/src/main/java/cn/iocoder/yudao/framework/apilog/core/interceptor/ApiAccessLogInterceptor.java # yudao-module-report/yudao-module-report-biz/src/main/java/cn/iocoder/yudao/module/report/framework/jmreport/core/service/JmReportTokenServiceImpl.javapull/126/MERGE
commit
0536fa3957
File diff suppressed because one or more lines are too long
|
@ -39,4 +39,11 @@ public class TenantProperties {
|
||||||
*/
|
*/
|
||||||
private Set<String> ignoreTables = Collections.emptySet();
|
private Set<String> ignoreTables = Collections.emptySet();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 需要忽略多租户的 Spring Cache 缓存
|
||||||
|
*
|
||||||
|
* 即默认所有缓存都开启多租户的功能,所以记得添加对应的 tenant_id 字段哟
|
||||||
|
*/
|
||||||
|
private Set<String> ignoreCaches = Collections.emptySet();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,12 +133,13 @@ public class YudaoTenantAutoConfiguration {
|
||||||
@Primary // 引入租户时,tenantRedisCacheManager 为主 Bean
|
@Primary // 引入租户时,tenantRedisCacheManager 为主 Bean
|
||||||
public RedisCacheManager tenantRedisCacheManager(RedisTemplate<String, Object> redisTemplate,
|
public RedisCacheManager tenantRedisCacheManager(RedisTemplate<String, Object> redisTemplate,
|
||||||
RedisCacheConfiguration redisCacheConfiguration,
|
RedisCacheConfiguration redisCacheConfiguration,
|
||||||
YudaoCacheProperties yudaoCacheProperties) {
|
YudaoCacheProperties yudaoCacheProperties,
|
||||||
|
TenantProperties tenantProperties) {
|
||||||
// 创建 RedisCacheWriter 对象
|
// 创建 RedisCacheWriter 对象
|
||||||
RedisConnectionFactory connectionFactory = Objects.requireNonNull(redisTemplate.getConnectionFactory());
|
RedisConnectionFactory connectionFactory = Objects.requireNonNull(redisTemplate.getConnectionFactory());
|
||||||
RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory,
|
RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory,
|
||||||
BatchStrategies.scan(yudaoCacheProperties.getRedisScanBatchSize()));
|
BatchStrategies.scan(yudaoCacheProperties.getRedisScanBatchSize()));
|
||||||
// 创建 TenantRedisCacheManager 对象
|
// 创建 TenantRedisCacheManager 对象
|
||||||
return new TenantRedisCacheManager(cacheWriter, redisCacheConfiguration);
|
return new TenantRedisCacheManager(cacheWriter, redisCacheConfiguration, tenantProperties.getIgnoreCaches());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package cn.iocoder.yudao.framework.tenant.core.redis;
|
package cn.iocoder.yudao.framework.tenant.core.redis;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.iocoder.yudao.framework.redis.core.TimeoutRedisCacheManager;
|
import cn.iocoder.yudao.framework.redis.core.TimeoutRedisCacheManager;
|
||||||
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
@ -8,6 +9,8 @@ import org.springframework.data.redis.cache.RedisCacheConfiguration;
|
||||||
import org.springframework.data.redis.cache.RedisCacheManager;
|
import org.springframework.data.redis.cache.RedisCacheManager;
|
||||||
import org.springframework.data.redis.cache.RedisCacheWriter;
|
import org.springframework.data.redis.cache.RedisCacheWriter;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 多租户的 {@link RedisCacheManager} 实现类
|
* 多租户的 {@link RedisCacheManager} 实现类
|
||||||
*
|
*
|
||||||
|
@ -18,16 +21,21 @@ import org.springframework.data.redis.cache.RedisCacheWriter;
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class TenantRedisCacheManager extends TimeoutRedisCacheManager {
|
public class TenantRedisCacheManager extends TimeoutRedisCacheManager {
|
||||||
|
|
||||||
|
private final Set<String> ignoreCaches;
|
||||||
|
|
||||||
public TenantRedisCacheManager(RedisCacheWriter cacheWriter,
|
public TenantRedisCacheManager(RedisCacheWriter cacheWriter,
|
||||||
RedisCacheConfiguration defaultCacheConfiguration) {
|
RedisCacheConfiguration defaultCacheConfiguration,
|
||||||
|
Set<String> ignoreCaches) {
|
||||||
super(cacheWriter, defaultCacheConfiguration);
|
super(cacheWriter, defaultCacheConfiguration);
|
||||||
|
this.ignoreCaches = ignoreCaches;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Cache getCache(String name) {
|
public Cache getCache(String name) {
|
||||||
// 如果开启多租户,则 name 拼接租户后缀
|
// 如果开启多租户,则 name 拼接租户后缀
|
||||||
if (!TenantContextHolder.isIgnore()
|
if (!TenantContextHolder.isIgnore()
|
||||||
&& TenantContextHolder.getTenantId() != null) {
|
&& TenantContextHolder.getTenantId() != null
|
||||||
|
&& !CollUtil.contains(ignoreCaches, name)) {
|
||||||
name = name + ":" + TenantContextHolder.getTenantId();
|
name = name + ":" + TenantContextHolder.getTenantId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,7 @@
|
||||||
package cn.iocoder.yudao.framework.security.core.service;
|
package cn.iocoder.yudao.framework.security.core.service;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.collection.ListUtil;
|
|
||||||
import cn.hutool.core.util.ArrayUtil;
|
|
||||||
import cn.hutool.core.util.HashUtil;
|
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
|
||||||
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
||||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
|
||||||
import cn.iocoder.yudao.framework.common.util.cache.CacheUtils;
|
|
||||||
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
import cn.iocoder.yudao.framework.security.core.LoginUser;
|
||||||
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
||||||
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
|
import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
|
||||||
|
@ -20,7 +14,6 @@ import java.time.Duration;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static cn.iocoder.yudao.framework.common.util.cache.CacheUtils.buildAsyncReloadingCache;
|
|
||||||
import static cn.iocoder.yudao.framework.common.util.cache.CacheUtils.buildCache;
|
import static cn.iocoder.yudao.framework.common.util.cache.CacheUtils.buildCache;
|
||||||
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
|
||||||
|
|
||||||
|
@ -70,7 +63,11 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService {
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public boolean hasAnyPermissions(String... permissions) {
|
public boolean hasAnyPermissions(String... permissions) {
|
||||||
return hasAnyPermissionsCache.get(new KeyValue<>(getLoginUserId(), Arrays.asList(permissions)));
|
Long userId = getLoginUserId();
|
||||||
|
if (userId == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return hasAnyPermissionsCache.get(new KeyValue<>(userId, Arrays.asList(permissions)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -81,7 +78,11 @@ public class SecurityFrameworkServiceImpl implements SecurityFrameworkService {
|
||||||
@Override
|
@Override
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public boolean hasAnyRoles(String... roles) {
|
public boolean hasAnyRoles(String... roles) {
|
||||||
return hasAnyRolesCache.get(new KeyValue<>(getLoginUserId(), Arrays.asList(roles)));
|
Long userId = getLoginUserId();
|
||||||
|
if (userId == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return hasAnyRolesCache.get(new KeyValue<>(userId, Arrays.asList(roles)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,17 +1,23 @@
|
||||||
package cn.iocoder.yudao.framework.apilog.core.interceptor;
|
package cn.iocoder.yudao.framework.apilog.core.interceptor;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.io.FileUtil;
|
||||||
|
import cn.hutool.core.io.resource.ResourceUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
|
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
|
||||||
import cn.iocoder.yudao.framework.common.util.spring.SpringUtils;
|
import cn.iocoder.yudao.framework.common.util.spring.SpringUtils;
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.servlet.http.HttpServletResponse;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.util.StopWatch;
|
import org.springframework.util.StopWatch;
|
||||||
import org.springframework.web.method.HandlerMethod;
|
import org.springframework.web.method.HandlerMethod;
|
||||||
import org.springframework.web.servlet.HandlerInterceptor;
|
import org.springframework.web.servlet.HandlerInterceptor;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import java.lang.reflect.Method;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API 访问日志 Interceptor
|
* API 访问日志 Interceptor
|
||||||
|
@ -49,6 +55,8 @@ public class ApiAccessLogInterceptor implements HandlerInterceptor {
|
||||||
StopWatch stopWatch = new StopWatch();
|
StopWatch stopWatch = new StopWatch();
|
||||||
stopWatch.start();
|
stopWatch.start();
|
||||||
request.setAttribute(ATTRIBUTE_STOP_WATCH, stopWatch);
|
request.setAttribute(ATTRIBUTE_STOP_WATCH, stopWatch);
|
||||||
|
// 打印 Controller 路径
|
||||||
|
printHandlerMethodPosition(handlerMethod);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -64,4 +72,32 @@ public class ApiAccessLogInterceptor implements HandlerInterceptor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打印 Controller 方法路径
|
||||||
|
*/
|
||||||
|
private void printHandlerMethodPosition(HandlerMethod handlerMethod) {
|
||||||
|
if (handlerMethod == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Method method = handlerMethod.getMethod();
|
||||||
|
Class<?> clazz = method.getDeclaringClass();
|
||||||
|
try {
|
||||||
|
// 获取 method 的 lineNumber
|
||||||
|
List<String> clazzContents = FileUtil.readUtf8Lines(
|
||||||
|
ResourceUtil.getResource(null, clazz).getPath().replace("/target/classes/", "/src/main/java/")
|
||||||
|
+ clazz.getSimpleName() + ".java");
|
||||||
|
Optional<Integer> lineNumber = IntStream.range(0, clazzContents.size())
|
||||||
|
.filter(i -> clazzContents.get(i).contains(" " + method.getName() + "(")) // 简单匹配,不考虑方法重名
|
||||||
|
.mapToObj(i -> i + 1) // 行号从 1 开始
|
||||||
|
.findFirst();
|
||||||
|
if (!lineNumber.isPresent()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 打印结果
|
||||||
|
System.out.printf("\tController 方法路径:%s(%s.java:%d)\n", clazz.getName(), clazz.getSimpleName(), lineNumber.get());
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
// 忽略异常。原因:仅仅打印,非重要逻辑
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ public enum BpmProcessInstanceStatusEnum implements IntArrayValuable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int[] array() {
|
public int[] array() {
|
||||||
return new int[0];
|
return ARRAYS;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,7 +112,7 @@ public class AppProductSpuController {
|
||||||
productBrowseHistoryService.createBrowseHistory(getLoginUserId(), id);
|
productBrowseHistoryService.createBrowseHistory(getLoginUserId(), id);
|
||||||
|
|
||||||
// 拼接返回
|
// 拼接返回
|
||||||
spu.setBrowseCount(spu.getBrowseCount() + spu.getVirtualSalesCount());
|
spu.setSalesCount(spu.getSalesCount() + spu.getVirtualSalesCount());
|
||||||
AppProductSpuDetailRespVO spuVO = BeanUtils.toBean(spu, AppProductSpuDetailRespVO.class)
|
AppProductSpuDetailRespVO spuVO = BeanUtils.toBean(spu, AppProductSpuDetailRespVO.class)
|
||||||
.setSkus(BeanUtils.toBean(skus, AppProductSpuDetailRespVO.Sku.class));
|
.setSkus(BeanUtils.toBean(skus, AppProductSpuDetailRespVO.Sku.class));
|
||||||
// 处理 vip 价格
|
// 处理 vip 价格
|
||||||
|
|
|
@ -26,7 +26,7 @@ public interface ProductSkuMapper extends BaseMapperX<ProductSkuDO> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新 SKU 库存(增加)
|
* 更新 SKU 库存(增加)、销量(减少)
|
||||||
*
|
*
|
||||||
* @param id 编号
|
* @param id 编号
|
||||||
* @param incrCount 增加库存(正数)
|
* @param incrCount 增加库存(正数)
|
||||||
|
@ -34,13 +34,14 @@ public interface ProductSkuMapper extends BaseMapperX<ProductSkuDO> {
|
||||||
default void updateStockIncr(Long id, Integer incrCount) {
|
default void updateStockIncr(Long id, Integer incrCount) {
|
||||||
Assert.isTrue(incrCount > 0);
|
Assert.isTrue(incrCount > 0);
|
||||||
LambdaUpdateWrapper<ProductSkuDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<ProductSkuDO>()
|
LambdaUpdateWrapper<ProductSkuDO> lambdaUpdateWrapper = new LambdaUpdateWrapper<ProductSkuDO>()
|
||||||
.setSql(" stock = stock + " + incrCount)
|
.setSql(" stock = stock + " + incrCount
|
||||||
|
+ ", sales_count = sales_count - " + incrCount)
|
||||||
.eq(ProductSkuDO::getId, id);
|
.eq(ProductSkuDO::getId, id);
|
||||||
update(null, lambdaUpdateWrapper);
|
update(null, lambdaUpdateWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 更新 SKU 库存(减少)
|
* 更新 SKU 库存(减少)、销量(增加)
|
||||||
*
|
*
|
||||||
* @param id 编号
|
* @param id 编号
|
||||||
* @param incrCount 减少库存(负数)
|
* @param incrCount 减少库存(负数)
|
||||||
|
@ -48,10 +49,12 @@ public interface ProductSkuMapper extends BaseMapperX<ProductSkuDO> {
|
||||||
*/
|
*/
|
||||||
default int updateStockDecr(Long id, Integer incrCount) {
|
default int updateStockDecr(Long id, Integer incrCount) {
|
||||||
Assert.isTrue(incrCount < 0);
|
Assert.isTrue(incrCount < 0);
|
||||||
|
incrCount = - incrCount; // 取正
|
||||||
LambdaUpdateWrapper<ProductSkuDO> updateWrapper = new LambdaUpdateWrapper<ProductSkuDO>()
|
LambdaUpdateWrapper<ProductSkuDO> updateWrapper = new LambdaUpdateWrapper<ProductSkuDO>()
|
||||||
.setSql(" stock = stock + " + incrCount) // 负数,所以使用 + 号
|
.setSql(" stock = stock - " + incrCount
|
||||||
|
+ ", sales_count = sales_count + " + incrCount)
|
||||||
.eq(ProductSkuDO::getId, id)
|
.eq(ProductSkuDO::getId, id)
|
||||||
.ge(ProductSkuDO::getStock, -incrCount); // cas 逻辑
|
.ge(ProductSkuDO::getStock, incrCount);
|
||||||
return update(null, updateWrapper);
|
return update(null, updateWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -85,9 +85,19 @@ public interface ProductSpuMapper extends BaseMapperX<ProductSpuDO> {
|
||||||
* @param incrCount 增加的库存数量
|
* @param incrCount 增加的库存数量
|
||||||
*/
|
*/
|
||||||
default void updateStock(Long id, Integer incrCount) {
|
default void updateStock(Long id, Integer incrCount) {
|
||||||
|
// 拼接 SQL
|
||||||
|
if (incrCount == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String sql;
|
||||||
|
if (incrCount > 0) {
|
||||||
|
sql = " stock = stock + " + incrCount + ", sales_count = sales_count - " + incrCount;
|
||||||
|
} else {
|
||||||
|
sql = " stock = stock - " + Math.abs(incrCount) + ", sales_count = sales_count + " + Math.abs(incrCount);
|
||||||
|
}
|
||||||
|
// 执行更新
|
||||||
LambdaUpdateWrapper<ProductSpuDO> updateWrapper = new LambdaUpdateWrapper<ProductSpuDO>()
|
LambdaUpdateWrapper<ProductSpuDO> updateWrapper = new LambdaUpdateWrapper<ProductSpuDO>()
|
||||||
// 负数,所以使用 + 号
|
.setSql(sql)
|
||||||
.setSql(" stock = stock +" + incrCount)
|
|
||||||
.eq(ProductSpuDO::getId, id);
|
.eq(ProductSpuDO::getId, id);
|
||||||
update(null, updateWrapper);
|
update(null, updateWrapper);
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,6 +125,7 @@ public class BrokerageUserServiceImpl implements BrokerageUserService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BrokerageUserDO getOrCreateBrokerageUser(Long id) {
|
public BrokerageUserDO getOrCreateBrokerageUser(Long id) {
|
||||||
|
// TODO @芋艿:这块优化下;统一到注册时处理;
|
||||||
BrokerageUserDO brokerageUser = brokerageUserMapper.selectById(id);
|
BrokerageUserDO brokerageUser = brokerageUserMapper.selectById(id);
|
||||||
// 特殊:人人分销的情况下,如果分销人为空则创建分销人
|
// 特殊:人人分销的情况下,如果分销人为空则创建分销人
|
||||||
if (brokerageUser == null && ObjUtil.equal(BrokerageEnabledConditionEnum.ALL.getCondition(),
|
if (brokerageUser == null && ObjUtil.equal(BrokerageEnabledConditionEnum.ALL.getCondition(),
|
||||||
|
|
|
@ -190,6 +190,13 @@ yudao:
|
||||||
- system_mail_template
|
- system_mail_template
|
||||||
- system_mail_log
|
- system_mail_log
|
||||||
- system_notify_template
|
- system_notify_template
|
||||||
|
ignore-caches:
|
||||||
|
- permission_menu_ids
|
||||||
|
- oauth_client
|
||||||
|
- notify_template
|
||||||
|
- mail_account
|
||||||
|
- mail_template
|
||||||
|
- sms_template
|
||||||
sms-code: # 短信验证码相关的配置项
|
sms-code: # 短信验证码相关的配置项
|
||||||
expire-times: 10m
|
expire-times: 10m
|
||||||
send-frequency: 1m
|
send-frequency: 1m
|
||||||
|
|
Loading…
Reference in New Issue