# 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.java
pull/126/MERGE
YunaiV 2024-07-24 19:05:47 +08:00
commit 0536fa3957
13 changed files with 285 additions and 290 deletions

File diff suppressed because one or more lines are too long

View File

@ -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();
} }

View File

@ -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());
} }
} }

View File

@ -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();
} }

View File

@ -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

View File

@ -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) {
// 忽略异常。原因:仅仅打印,非重要逻辑
}
}
} }

View File

@ -33,7 +33,7 @@ public enum BpmProcessInstanceStatusEnum implements IntArrayValuable {
@Override @Override
public int[] array() { public int[] array() {
return new int[0]; return ARRAYS;
} }
} }

View File

@ -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 价格

View File

@ -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);
} }

View File

@ -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);
} }

View File

@ -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(),

View File

@ -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