diff --git a/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/tenant/TenantBroker.java b/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/tenant/TenantBroker.java
new file mode 100644
index 000000000..40650bf08
--- /dev/null
+++ b/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/tenant/TenantBroker.java
@@ -0,0 +1,139 @@
+package com.pig4cloud.pigx.common.data.tenant;
+
+import lombok.experimental.UtilityClass;
+import lombok.extern.slf4j.Slf4j;
+
+import java.util.function.Supplier;
+
+/**
+ * 租户运行时代理
+ * 这是一个工具类,用于切换租户运行时,保护租户ID上下文
+ * 下面这段代码演示问题所在
+ *
+ * void methodA(){
+ * // 因为某些特殊原因,需要手动指定租户
+ * TenantContextHolder.setTenantId(1);
+ * // do something ...
+ * }
+ * void methodB(){
+ * // 因为某些特殊原因,需要手动指定租户
+ * TenantContextHolder.setTenantId(2);
+ * methodA();
+ * // 此时租户ID已经变成 1
+ * // do something ...
+ * }
+ *
+ * 嵌套设置租户ID会导致租户上下文难以维护,并且很难察觉,容易导致数据错乱。
+ * 推荐的写法:
+ *
+ * void methodA(){
+ * TenantBroker.RunAs(1,() -> {
+ * // do something ...
+ * });
+ * }
+ * void methodB(){
+ * TenantBroker.RunAs(2,() -> {
+ * methodA();
+ * // do something ...
+ * });
+ * }
+ *
+ *
+ * @author CJ (jclazz@outlook.com)
+ * @date 2020/6/12
+ * @since 3.9
+ */
+@Slf4j
+@UtilityClass
+public class TenantBroker {
+
+ @FunctionalInterface
+ public interface RunAs {
+ /**
+ * 执行业务逻辑
+ * @param tenantId
+ * @throws Exception
+ */
+ void run(T tenantId) throws Exception;
+ }
+
+ @FunctionalInterface
+ public interface ApplyAs {
+ /**
+ * 执行业务逻辑,返回一个值
+ * @param tenantId
+ * @return
+ * @throws Exception
+ */
+ R apply(T tenantId) throws Exception;
+ }
+
+ /**
+ * 以某个租户的身份运行
+ * @param tenant 租户ID
+ * @param func
+ */
+ public void runAs(Integer tenant,RunAs func){
+ final Integer pre = TenantContextHolder.getTenantId();
+ try {
+ log.trace("TenantBroker 切换租户{} -> {}",pre,tenant);
+ TenantContextHolder.setTenantId(tenant);
+ func.run(tenant);
+ } catch (Exception e){
+ throw new TenantBrokerExceptionWrapper(e.getMessage(),e);
+ } finally {
+ log.trace("TenantBroker 还原租户{} <- {}",pre,tenant);
+ TenantContextHolder.setTenantId(pre);
+ }
+ }
+
+ /**
+ * 以某个租户的身份运行
+ * @param tenant 租户ID
+ * @param func
+ * @param 返回数据类型
+ * @return
+ */
+ public T applyAs(Integer tenant, ApplyAs func){
+ final Integer pre = TenantContextHolder.getTenantId();
+ try {
+ log.trace("TenantBroker 切换租户{} -> {}",pre,tenant);
+ TenantContextHolder.setTenantId(tenant);
+ return func.apply(tenant);
+ } catch (Exception e){
+ throw new TenantBrokerExceptionWrapper(e.getMessage(),e);
+ } finally {
+ log.trace("TenantBroker 还原租户{} <- {}",pre,tenant);
+ TenantContextHolder.setTenantId(pre);
+ }
+ }
+
+ /**
+ * 以某个租户的身份运行
+ * @param supplier
+ * @param func
+ */
+ public void runAs(Supplier supplier, RunAs func){
+ runAs(supplier.get(),func);
+ }
+
+ /**
+ * 以某个租户的身份运行
+ * @param supplier
+ * @param func
+ * @param 返回数据类型
+ * @return
+ */
+ public T applyAs(Supplier supplier, ApplyAs func){
+ return applyAs(supplier.get(),func);
+ }
+
+ public static class TenantBrokerExceptionWrapper extends RuntimeException {
+ public TenantBrokerExceptionWrapper(String message, Throwable cause) {
+ super(message, cause);
+ }
+ public TenantBrokerExceptionWrapper(Throwable cause) {
+ super(cause);
+ }
+ }
+}
diff --git a/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/tenant/TenantContextHolder.java b/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/tenant/TenantContextHolder.java
index 785ea86c5..da42e70f5 100644
--- a/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/tenant/TenantContextHolder.java
+++ b/pigx-common/pigx-common-data/src/main/java/com/pig4cloud/pigx/common/data/tenant/TenantContextHolder.java
@@ -32,9 +32,10 @@ public class TenantContextHolder {
/**
- * TTL 设置租户ID
- *
+ * TTL 设置租户ID
+ * 谨慎使用此方法,避免嵌套调用。尽量使用 {@code TenantBroker}
* @param tenantId
+ * @see TenantBroker
*/
public void setTenantId(Integer tenantId) {
THREAD_LOCAL_TENANT.set(tenantId);
diff --git a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysTenantServiceImpl.java b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysTenantServiceImpl.java
index 1be5877fe..52536f21c 100755
--- a/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysTenantServiceImpl.java
+++ b/pigx-upms/pigx-upms-biz/src/main/java/com/pig4cloud/pigx/admin/service/impl/SysTenantServiceImpl.java
@@ -27,7 +27,7 @@ import com.pig4cloud.pigx.admin.service.*;
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
import com.pig4cloud.pigx.common.data.resolver.ParamResolver;
-import com.pig4cloud.pigx.common.data.tenant.TenantContextHolder;
+import com.pig4cloud.pigx.common.data.tenant.TenantBroker;
import lombok.AllArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.cache.annotation.CacheEvict;
@@ -110,73 +110,84 @@ public class SysTenantServiceImpl extends ServiceImpl dictList = new ArrayList<>(dictService.list());
- // 查询系统内置字典项目
- List dictIdList = dictList.stream().map(SysDict::getId)
- .collect(Collectors.toList());
- List dictItemList = new ArrayList<>(dictItemService
- .list(Wrappers.lambdaQuery()
- .in(SysDictItem::getDictId, dictIdList)));
- // 查询当前租户菜单
- List menuList = menuService.list();
- // 查询客户端配置
- List clientDetailsList = clientServices.list();
- // 查询系统参数配置
- List publicParamList = paramService.list();
+ List dictList = new ArrayList<>(32);
+ List dictIdList = new ArrayList<>(32);
+ List dictItemList = new ArrayList<>(64);
+ List menuList = new ArrayList<>(128);
+ List clientDetailsList = new ArrayList<>(16);
+ List publicParamList = new ArrayList<>(64);
+
+ TenantBroker.runAs(defaultId,(id) -> {
+ // 查询系统内置字典
+ dictList.addAll(dictService.list());
+ // 查询系统内置字典项目
+ dictIdList.addAll(dictList.stream().map(SysDict::getId)
+ .collect(Collectors.toList()));
+ dictItemList.addAll(dictItemService
+ .list(Wrappers.lambdaQuery()
+ .in(SysDictItem::getDictId, dictIdList)));
+ // 查询当前租户菜单
+ menuList.addAll(menuService.list());
+ // 查询客户端配置
+ clientDetailsList.addAll(clientServices.list());
+ // 查询系统参数配置
+ publicParamList.addAll(paramService.list());
+ });
+
// 保证插入租户为新的租户
- TenantContextHolder.setTenantId(sysTenant.getId());
+ return TenantBroker.applyAs(sysTenant.getId(),(id -> {
- // 插入部门
- SysDept dept = new SysDept();
- dept.setName(defaultDeptName);
- dept.setParentId(0);
- deptService.save(dept);
- //维护部门关系
- deptRelationService.insertDeptRelation(dept);
- // 构造初始化用户
- SysUser user = new SysUser();
- user.setUsername(defaultUsername);
- user.setPassword(ENCODER.encode(defaultPassword));
- user.setDeptId(dept.getDeptId());
- userService.save(user);
- // 构造新角色
- SysRole role = new SysRole();
- role.setRoleCode(defaultRoleCode);
- role.setRoleName(defaultRoleName);
- roleService.save(role);
- // 用户角色关系
- SysUserRole userRole = new SysUserRole();
- userRole.setUserId(user.getUserId());
- userRole.setRoleId(role.getRoleId());
- userRoleService.save(userRole);
- // 插入新的菜单
- saveTenantMenu(TreeUtil.buildTree(menuList, CommonConstants.MENU_TREE_ROOT_ID), CommonConstants.MENU_TREE_ROOT_ID);
- List newMenuList = menuService.list();
+ // 插入部门
+ SysDept dept = new SysDept();
+ dept.setName(defaultDeptName);
+ dept.setParentId(0);
+ deptService.save(dept);
+ //维护部门关系
+ deptRelationService.insertDeptRelation(dept);
+ // 构造初始化用户
+ SysUser user = new SysUser();
+ user.setUsername(defaultUsername);
+ user.setPassword(ENCODER.encode(defaultPassword));
+ user.setDeptId(dept.getDeptId());
+ userService.save(user);
+ // 构造新角色
+ SysRole role = new SysRole();
+ role.setRoleCode(defaultRoleCode);
+ role.setRoleName(defaultRoleName);
+ roleService.save(role);
+ // 用户角色关系
+ SysUserRole userRole = new SysUserRole();
+ userRole.setUserId(user.getUserId());
+ userRole.setRoleId(role.getRoleId());
+ userRoleService.save(userRole);
+ // 插入新的菜单
+ saveTenantMenu(TreeUtil.buildTree(menuList, CommonConstants.MENU_TREE_ROOT_ID), CommonConstants.MENU_TREE_ROOT_ID);
+ List newMenuList = menuService.list();
- // 查询全部菜单,构造角色菜单关系
- List collect = newMenuList.stream().map(menu -> {
- SysRoleMenu roleMenu = new SysRoleMenu();
- roleMenu.setRoleId(role.getRoleId());
- roleMenu.setMenuId(menu.getMenuId());
- return roleMenu;
- }).collect(Collectors.toList());
- roleMenuService.saveBatch(collect);
- // 插入系统字典
- dictService.saveBatch(dictList);
- // 处理字典项最新关联的字典ID
- List itemList = dictList.stream()
- .flatMap(dict -> dictItemList.stream()
- .filter(item -> item.getType().equals(dict.getType()))
- .peek(item -> item.setDictId(dict.getId())))
- .collect(Collectors.toList());
+ // 查询全部菜单,构造角色菜单关系
+ List collect = newMenuList.stream().map(menu -> {
+ SysRoleMenu roleMenu = new SysRoleMenu();
+ roleMenu.setRoleId(role.getRoleId());
+ roleMenu.setMenuId(menu.getMenuId());
+ return roleMenu;
+ }).collect(Collectors.toList());
+ roleMenuService.saveBatch(collect);
+ // 插入系统字典
+ dictService.saveBatch(dictList);
+ // 处理字典项最新关联的字典ID
+ List itemList = dictList.stream()
+ .flatMap(dict -> dictItemList.stream()
+ .filter(item -> item.getType().equals(dict.getType()))
+ .peek(item -> item.setDictId(dict.getId())))
+ .collect(Collectors.toList());
+
+ //插入客户端
+ clientServices.saveBatch(clientDetailsList);
+ // 插入系统配置
+ paramService.saveBatch(publicParamList);
+ return dictItemService.saveBatch(itemList);
+ }));
- //插入客户端
- clientServices.saveBatch(clientDetailsList);
- // 插入系统配置
- paramService.saveBatch(publicParamList);
- return dictItemService.saveBatch(itemList);
}
/**
diff --git a/pigx-visual/pigx-mp-platform/src/main/java/com/pig4cloud/pigx/mp/config/WxMpInitConfigRunner.java b/pigx-visual/pigx-mp-platform/src/main/java/com/pig4cloud/pigx/mp/config/WxMpInitConfigRunner.java
index 297a72534..614d0c5f9 100755
--- a/pigx-visual/pigx-mp-platform/src/main/java/com/pig4cloud/pigx/mp/config/WxMpInitConfigRunner.java
+++ b/pigx-visual/pigx-mp-platform/src/main/java/com/pig4cloud/pigx/mp/config/WxMpInitConfigRunner.java
@@ -4,7 +4,7 @@ import com.google.common.collect.Maps;
import com.pig4cloud.pigx.admin.api.feign.RemoteTenantService;
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
-import com.pig4cloud.pigx.common.data.tenant.TenantContextHolder;
+import com.pig4cloud.pigx.common.data.tenant.TenantBroker;
import com.pig4cloud.pigx.mp.entity.WxAccount;
import com.pig4cloud.pigx.mp.handler.*;
import com.pig4cloud.pigx.mp.service.WxAccountService;
@@ -73,10 +73,7 @@ public class WxMpInitConfigRunner {
// 获取全部租户 遍历所有租户对应的公众号列表
List accountList = new ArrayList<>();
tenantService.list(SecurityConstants.FROM_IN).getData()
- .forEach(tenant -> {
- TenantContextHolder.setTenantId(tenant.getId());
- accountList.addAll(accountService.list());
- });
+ .forEach( tenant -> TenantBroker.runAs(tenant.getId(), (id) -> accountList.addAll(accountService.list())));
mpServices = accountList.stream().map(a -> {
WxMpInRedisConfigStorage configStorage = new WxMpInRedisConfigStorage(redisTemplate);
diff --git a/pigx-visual/pigx-mp-platform/src/main/java/com/pig4cloud/pigx/mp/controller/WxPortalController.java b/pigx-visual/pigx-mp-platform/src/main/java/com/pig4cloud/pigx/mp/controller/WxPortalController.java
index 213a3d834..19164b772 100755
--- a/pigx-visual/pigx-mp-platform/src/main/java/com/pig4cloud/pigx/mp/controller/WxPortalController.java
+++ b/pigx-visual/pigx-mp-platform/src/main/java/com/pig4cloud/pigx/mp/controller/WxPortalController.java
@@ -2,7 +2,7 @@ package com.pig4cloud.pigx.mp.controller;
import cn.hutool.core.util.StrUtil;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
-import com.pig4cloud.pigx.common.data.tenant.TenantContextHolder;
+import com.pig4cloud.pigx.common.data.tenant.TenantBroker;
import com.pig4cloud.pigx.common.security.annotation.Inner;
import com.pig4cloud.pigx.mp.config.WxMpContextHolder;
import com.pig4cloud.pigx.mp.config.WxMpInitConfigRunner;
@@ -86,9 +86,33 @@ public class WxPortalController {
@RequestParam("openid") String openid,
@RequestParam(name = "encrypt_type", required = false) String encType,
@RequestParam(name = "msg_signature", required = false) String msgSignature) {
- // 查询当前 appId 对应的租户 保存到TTL 中
- Integer tenantId = WxMpInitConfigRunner.getTenants().get(appId);
- TenantContextHolder.setTenantId(tenantId);
+ return TenantBroker.applyAs(
+ () -> WxMpInitConfigRunner.getTenants().get(appId),
+ (id) -> handleMessage(appId,requestBody,signature,timestamp,nonce,openid,encType,msgSignature)
+ );
+ }
+
+ /**
+ * 微信消息处理
+ *
+ * @param appId 多公众号标志位
+ * @param requestBody 请求报文体
+ * @param signature 微信签名
+ * @param encType 加签方式
+ * @param msgSignature 微信签名
+ * @param timestamp 时间戳
+ * @param nonce 随机数
+ * @return
+ */
+ public String handleMessage(String appId,
+ String requestBody,
+ String signature,
+ String timestamp,
+ String nonce,
+ String openid,
+ String encType,
+ String msgSignature) {
+
WxMpContextHolder.setAppId(appId);
final WxMpService wxService = WxMpInitConfigRunner.getMpServices().get(appId);
diff --git a/pigx-visual/pigx-mp-platform/src/main/java/com/pig4cloud/pigx/mp/service/impl/WxAccountFansServiceImpl.java b/pigx-visual/pigx-mp-platform/src/main/java/com/pig4cloud/pigx/mp/service/impl/WxAccountFansServiceImpl.java
index a326f04f7..85d658864 100755
--- a/pigx-visual/pigx-mp-platform/src/main/java/com/pig4cloud/pigx/mp/service/impl/WxAccountFansServiceImpl.java
+++ b/pigx-visual/pigx-mp-platform/src/main/java/com/pig4cloud/pigx/mp/service/impl/WxAccountFansServiceImpl.java
@@ -21,7 +21,7 @@ import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.pig4cloud.pigx.common.data.tenant.TenantContextHolder;
+import com.pig4cloud.pigx.common.data.tenant.TenantBroker;
import com.pig4cloud.pigx.mp.config.WxMpInitConfigRunner;
import com.pig4cloud.pigx.mp.entity.WxAccount;
import com.pig4cloud.pigx.mp.entity.WxAccountFans;
@@ -77,11 +77,7 @@ public class WxAccountFansServiceImpl extends ServiceImplWxMpInitConfigRunner.getTenants().get(appId),(id)->fetchUser(null, wxAccount, wxMpUserService));
return Boolean.TRUE;
}
diff --git a/pigx-visual/pigx-pay-platform/src/main/java/com/pig4cloud/pigx/pay/config/PayConfigParmaInitRunner.java b/pigx-visual/pigx-pay-platform/src/main/java/com/pig4cloud/pigx/pay/config/PayConfigParmaInitRunner.java
index b352d98d1..ae14254c1 100644
--- a/pigx-visual/pigx-pay-platform/src/main/java/com/pig4cloud/pigx/pay/config/PayConfigParmaInitRunner.java
+++ b/pigx-visual/pigx-pay-platform/src/main/java/com/pig4cloud/pigx/pay/config/PayConfigParmaInitRunner.java
@@ -14,7 +14,7 @@ import com.pig4cloud.pigx.admin.api.feign.RemoteTenantService;
import com.pig4cloud.pigx.common.core.constant.CacheConstants;
import com.pig4cloud.pigx.common.core.constant.CommonConstants;
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
-import com.pig4cloud.pigx.common.data.tenant.TenantContextHolder;
+import com.pig4cloud.pigx.common.data.tenant.TenantBroker;
import com.pig4cloud.pigx.pay.entity.PayChannel;
import com.pig4cloud.pigx.pay.service.PayChannelService;
import com.pig4cloud.pigx.pay.utils.PayChannelNameEnum;
@@ -61,13 +61,12 @@ public class PayConfigParmaInitRunner {
List channelList = new ArrayList<>();
tenantService.list(SecurityConstants.FROM_IN).getData()
- .forEach(tenant -> {
- TenantContextHolder.setTenantId(tenant.getId());
+ .forEach(tenant -> TenantBroker.runAs(tenant.getId(), (id) -> {
List payChannelList = channelService
.list(Wrappers.lambdaQuery()
.eq(PayChannel::getState, CommonConstants.STATUS_NORMAL));
channelList.addAll(payChannelList);
- });
+ }));
channelList.forEach(channel -> {
JSONObject params = JSONUtil.parseObj(channel.getParam());