1. 增加【默认】的系统租户的概念,禁止修改与删除等操作

2. 修复定时任务在刷新本地缓存时,会过滤租户的问题
3. 调整短信的回调地址,并进行租户的白名单
This commit is contained in:
YunaiV 2022-02-24 00:53:28 +08:00
parent fa62ace6af
commit 75928525ca
13 changed files with 77 additions and 40 deletions

View File

@ -20,6 +20,7 @@ import cn.iocoder.yudao.module.pay.dal.dataobject.merchant.PayChannelDO;
import cn.iocoder.yudao.module.pay.dal.mysql.merchant.PayChannelMapper; import cn.iocoder.yudao.module.pay.dal.mysql.merchant.PayChannelMapper;
import cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants; import cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@ -65,6 +66,10 @@ public class PayChannelServiceImpl implements PayChannelService {
@Resource @Resource
private Validator validator; private Validator validator;
@Resource
@Lazy // 注入自己所以延迟加载
private PayChannelService self;
@Override @Override
@PostConstruct @PostConstruct
@TenantIgnore // 忽略自动化租户全局初始化本地缓存 @TenantIgnore // 忽略自动化租户全局初始化本地缓存
@ -86,7 +91,7 @@ public class PayChannelServiceImpl implements PayChannelService {
@Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD) @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
public void schedulePeriodicRefresh() { public void schedulePeriodicRefresh() {
initPayClients(); self.initPayClients();
} }
/** /**

View File

@ -108,6 +108,7 @@ public interface ErrorCodeConstants {
ErrorCode TENANT_NOT_EXISTS = new ErrorCode(1002014000, "租户不存在"); ErrorCode TENANT_NOT_EXISTS = new ErrorCode(1002014000, "租户不存在");
ErrorCode TENANT_DISABLE = new ErrorCode(1002014001, "名字为【{}】的租户已被禁用"); ErrorCode TENANT_DISABLE = new ErrorCode(1002014001, "名字为【{}】的租户已被禁用");
ErrorCode TENANT_EXPIRE = new ErrorCode(1002014002, "名字为【{}】的租户已过期"); ErrorCode TENANT_EXPIRE = new ErrorCode(1002014002, "名字为【{}】的租户已过期");
ErrorCode TENANT_CAN_NOT_UPDATE_SYSTEM = new ErrorCode(1002014003, "系统租户不能进行修改、删除等操作!");
// ========== 租户套餐 1002015000 ========== // ========== 租户套餐 1002015000 ==========
ErrorCode TENANT_PACKAGE_NOT_EXISTS = new ErrorCode(1002015000, "租户套餐不存在"); ErrorCode TENANT_PACKAGE_NOT_EXISTS = new ErrorCode(1002015000, "租户套餐不存在");

View File

@ -19,7 +19,6 @@ import javax.servlet.http.HttpServletRequest;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
// TODO 芋艿这块的接口命名在纠结下
@Api(tags = "管理后台 - 短信回调") @Api(tags = "管理后台 - 短信回调")
@RestController @RestController
@RequestMapping("/system/sms/callback") @RequestMapping("/system/sms/callback")
@ -28,7 +27,7 @@ public class SmsCallbackController {
@Resource @Resource
private SmsSendService smsSendService; private SmsSendService smsSendService;
@PostMapping("/sms/yunpian") @PostMapping("/yunpian")
@ApiOperation(value = "云片短信的回调", notes = "参见 https://www.yunpian.com/official/document/sms/zh_cn/domestic_push_report 文档") @ApiOperation(value = "云片短信的回调", notes = "参见 https://www.yunpian.com/official/document/sms/zh_cn/domestic_push_report 文档")
@ApiImplicitParam(name = "sms_status", value = "发送状态", required = true, example = "[{具体内容}]", dataTypeClass = String.class) @ApiImplicitParam(name = "sms_status", value = "发送状态", required = true, example = "[{具体内容}]", dataTypeClass = String.class)
@OperateLog(enable = false) @OperateLog(enable = false)
@ -38,7 +37,7 @@ public class SmsCallbackController {
return "SUCCESS"; // 约定返回 SUCCESS 为成功 return "SUCCESS"; // 约定返回 SUCCESS 为成功
} }
@PostMapping("/sms/aliyun") @PostMapping("/aliyun")
@ApiOperation(value = "阿里云短信的回调", notes = "参见 https://help.aliyun.com/document_detail/120998.html 文档") @ApiOperation(value = "阿里云短信的回调", notes = "参见 https://help.aliyun.com/document_detail/120998.html 文档")
@OperateLog(enable = false) @OperateLog(enable = false)
public CommonResult<Boolean> receiveAliyunSmsStatus(HttpServletRequest request) throws Throwable { public CommonResult<Boolean> receiveAliyunSmsStatus(HttpServletRequest request) throws Throwable {

View File

@ -16,7 +16,6 @@ import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.Collection;
import java.util.List; import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@ -63,15 +62,6 @@ public class TenantPackageController {
return success(TenantPackageConvert.INSTANCE.convert(tenantPackage)); return success(TenantPackageConvert.INSTANCE.convert(tenantPackage));
} }
@GetMapping("/list")
@ApiOperation("获得租户套餐列表")
@ApiImplicitParam(name = "ids", value = "编号列表", required = true, example = "1024,2048", dataTypeClass = List.class)
@PreAuthorize("@ss.hasPermission('system:tenant-package:query')")
public CommonResult<List<TenantPackageRespVO>> getTenantPackageList(@RequestParam("ids") Collection<Long> ids) {
List<TenantPackageDO> list = tenantPackageService.getTenantPackageList(ids);
return success(TenantPackageConvert.INSTANCE.convertList(list));
}
@GetMapping("/page") @GetMapping("/page")
@ApiOperation("获得租户套餐分页") @ApiOperation("获得租户套餐分页")
@PreAuthorize("@ss.hasPermission('system:tenant-package:query')") @PreAuthorize("@ss.hasPermission('system:tenant-package:query')")

View File

@ -22,6 +22,11 @@ import java.util.Date;
@NoArgsConstructor @NoArgsConstructor
public class TenantDO extends BaseDO { public class TenantDO extends BaseDO {
/**
* 套餐编号 - 系统
*/
public static final Long PACKAGE_ID_SYSTEM = 0L;
/** /**
* 租户编号自增 * 租户编号自增
*/ */
@ -60,6 +65,7 @@ public class TenantDO extends BaseDO {
* 租户套餐编号 * 租户套餐编号
* *
* 关联 {@link TenantPackageDO#getId()} * 关联 {@link TenantPackageDO#getId()}
* 特殊逻辑系统内置租户不使用套餐暂时使用 {@link #PACKAGE_ID_SYSTEM} 标识
*/ */
private Long packageId; private Long packageId;
/** /**

View File

@ -24,6 +24,7 @@ import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -90,6 +91,10 @@ public class PermissionServiceImpl implements PermissionService {
@Resource @Resource
private PermissionProducer permissionProducer; private PermissionProducer permissionProducer;
@Resource
@Lazy // 注入自己所以延迟加载
private PermissionService self;
/** /**
* 初始化 {@link #roleMenuCache} {@link #menuRoleCache} 缓存 * 初始化 {@link #roleMenuCache} {@link #menuRoleCache} 缓存
*/ */
@ -118,7 +123,7 @@ public class PermissionServiceImpl implements PermissionService {
@Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD) @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
public void schedulePeriodicRefresh() { public void schedulePeriodicRefresh() {
initLocalCache(); self.initLocalCache();
} }
/** /**

View File

@ -20,6 +20,7 @@ import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum;
import cn.iocoder.yudao.module.system.mq.producer.permission.RoleProducer; import cn.iocoder.yudao.module.system.mq.producer.permission.RoleProducer;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@ -72,6 +73,10 @@ public class RoleServiceImpl implements RoleService {
@Resource @Resource
private RoleProducer roleProducer; private RoleProducer roleProducer;
@Resource
@Lazy // 注入自己所以延迟加载
private RoleService self;
/** /**
* 初始化 {@link #roleCache} 缓存 * 初始化 {@link #roleCache} 缓存
*/ */
@ -93,7 +98,7 @@ public class RoleServiceImpl implements RoleService {
@Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD) @Scheduled(fixedDelay = SCHEDULER_PERIOD, initialDelay = SCHEDULER_PERIOD)
public void schedulePeriodicRefresh() { public void schedulePeriodicRefresh() {
initLocalCache(); self.initLocalCache();
} }
/** /**

View File

@ -7,7 +7,6 @@ import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.Tenant
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO; import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.Collection;
import java.util.List; import java.util.List;
/** /**
@ -47,14 +46,6 @@ public interface TenantPackageService {
*/ */
TenantPackageDO getTenantPackage(Long id); TenantPackageDO getTenantPackage(Long id);
/**
* 获得租户套餐列表
*
* @param ids 编号
* @return 租户套餐列表
*/
List<TenantPackageDO> getTenantPackageList(Collection<Long> ids);
/** /**
* 获得租户套餐分页 * 获得租户套餐分页
* *

View File

@ -16,7 +16,6 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Collection;
import java.util.List; import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@ -91,11 +90,6 @@ public class TenantPackageServiceImpl implements TenantPackageService {
return tenantPackageMapper.selectById(id); return tenantPackageMapper.selectById(id);
} }
@Override
public List<TenantPackageDO> getTenantPackageList(Collection<Long> ids) {
return tenantPackageMapper.selectBatchIds(ids);
}
@Override @Override
public PageResult<TenantPackageDO> getTenantPackagePage(TenantPackagePageReqVO pageReqVO) { public PageResult<TenantPackageDO> getTenantPackagePage(TenantPackagePageReqVO pageReqVO) {
return tenantPackageMapper.selectPage(pageReqVO); return tenantPackageMapper.selectPage(pageReqVO);

View File

@ -16,6 +16,7 @@ import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantEx
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantUpdateReqVO; import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantUpdateReqVO;
import cn.iocoder.yudao.module.system.convert.tenant.TenantConvert; import cn.iocoder.yudao.module.system.convert.tenant.TenantConvert;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO;
import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO;
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO; import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO;
@ -23,6 +24,7 @@ import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantMapper;
import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum; import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum;
import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum; import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum;
import cn.iocoder.yudao.module.system.mq.producer.tenant.TenantProducer; import cn.iocoder.yudao.module.system.mq.producer.tenant.TenantProducer;
import cn.iocoder.yudao.module.system.service.permission.MenuService;
import cn.iocoder.yudao.module.system.service.permission.PermissionService; import cn.iocoder.yudao.module.system.service.permission.PermissionService;
import cn.iocoder.yudao.module.system.service.permission.RoleService; import cn.iocoder.yudao.module.system.service.permission.RoleService;
import cn.iocoder.yudao.module.system.service.tenant.handler.TenantInfoHandler; import cn.iocoder.yudao.module.system.service.tenant.handler.TenantInfoHandler;
@ -86,6 +88,8 @@ public class TenantServiceImpl implements TenantService {
@Resource @Resource
private RoleService roleService; private RoleService roleService;
@Resource @Resource
private MenuService menuService;
@Resource
private PermissionService permissionService; private PermissionService permissionService;
@Resource @Resource
@ -205,7 +209,7 @@ public class TenantServiceImpl implements TenantService {
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void updateTenant(TenantUpdateReqVO updateReqVO) { public void updateTenant(TenantUpdateReqVO updateReqVO) {
// 校验存在 // 校验存在
TenantDO tenant = validateTenantExists(updateReqVO.getId()); TenantDO tenant = checkUpdateTenant(updateReqVO.getId());
// 校验套餐被禁用 // 校验套餐被禁用
TenantPackageDO tenantPackage = tenantPackageService.validTenantPackage(updateReqVO.getPackageId()); TenantPackageDO tenantPackage = tenantPackageService.validTenantPackage(updateReqVO.getPackageId());
@ -253,16 +257,20 @@ public class TenantServiceImpl implements TenantService {
@Override @Override
public void deleteTenant(Long id) { public void deleteTenant(Long id) {
// 校验存在 // 校验存在
validateTenantExists(id); checkUpdateTenant(id);
// 删除 // 删除
tenantMapper.deleteById(id); tenantMapper.deleteById(id);
} }
private TenantDO validateTenantExists(Long id) { private TenantDO checkUpdateTenant(Long id) {
TenantDO tenant = tenantMapper.selectById(id); TenantDO tenant = tenantMapper.selectById(id);
if (tenant == null) { if (tenant == null) {
throw exception(TENANT_NOT_EXISTS); throw exception(TENANT_NOT_EXISTS);
} }
// 内置租户不允许删除
if (isSystemTenant(tenant)) {
throw exception(TENANT_CAN_NOT_UPDATE_SYSTEM);
}
return tenant; return tenant;
} }
@ -321,9 +329,18 @@ public class TenantServiceImpl implements TenantService {
} }
// 获得租户然后获得菜单 // 获得租户然后获得菜单
TenantDO tenant = getTenant(TenantContextHolder.getRequiredTenantId()); TenantDO tenant = getTenant(TenantContextHolder.getRequiredTenantId());
TenantPackageDO tenantPackage = tenantPackageService.getTenantPackage(tenant.getPackageId()); Set<Long> menuIds;
if (isSystemTenant(tenant)) { // 系统租户菜单是全量的
menuIds = CollectionUtils.convertSet(menuService.getMenus(), MenuDO::getId);
} else {
menuIds = tenantPackageService.getTenantPackage(tenant.getPackageId()).getMenuIds();
}
// 执行处理器 // 执行处理器
handler.handle(tenantPackage.getMenuIds()); handler.handle(menuIds);
}
private static boolean isSystemTenant(TenantDO tenant) {
return Objects.equals(tenant.getPackageId(), TenantDO.PACKAGE_ID_SYSTEM);
} }
} }

View File

@ -79,8 +79,31 @@ yudao:
- cn.iocoder.yudao.module.tool.enums.ErrorCodeConstants - cn.iocoder.yudao.module.tool.enums.ErrorCodeConstants
tenant: # 多租户相关配置项 tenant: # 多租户相关配置项
enable: true enable: true
ignore-urls: /admin-api/system/tenant/get-id-by-name, /admin-api/system/captcha/get-image, /admin-api/infra/file/get/* ignore-urls:
ignore-tables: infra_config, infra_file, infra_job, infra_job_log, infra_job_log, system_tenant, system_tenant_package, system_dict_data, system_dict_type, system_error_code, system_menu, system_sms_channel, system_sms_template, tool_codegen_column, tool_codegen_table, tool_test_demo, tables, columns - /admin-api/system/tenant/get-id-by-name
- /admin-api/system/captcha/get-image
- /admin-api/infra/file/get/*
- /admin-api/system/sms/callback/*
ignore-tables:
- infra_config
- infra_file
- infra_job
- infra_job_log
- infra_job_log
- system_tenant
- system_tenant_package
- system_dict_data
- system_dict_type
- system_error_code
- system_menu
- system_sms_channel
- system_sms_template
- system_sms_log
- tool_codegen_column
- tool_codegen_table
- tool_test_demo
- tables
- columns
sms-code: # 短信验证码相关的配置项 sms-code: # 短信验证码相关的配置项
expire-times: 10m expire-times: 10m
send-frequency: 1m send-frequency: 1m

View File

@ -43,7 +43,8 @@
<el-table-column label="租户名" align="center" prop="name" /> <el-table-column label="租户名" align="center" prop="name" />
<el-table-column label="租户套餐" align="center" prop="packageId"> <el-table-column label="租户套餐" align="center" prop="packageId">
<template slot-scope="scope"> <template slot-scope="scope">
<el-tag> {{getPackageName(scope.row.packageId)}} </el-tag> <el-tag v-if="scope.row.packageId === 0" type="danger">系统租户</el-tag>
<el-tag v-else> {{getPackageName(scope.row.packageId)}} </el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="联系人" align="center" prop="contactName" /> <el-table-column label="联系人" align="center" prop="contactName" />

View File

@ -35,7 +35,7 @@ TODO
### 🐞 Bug Fixes ### 🐞 Bug Fixes
TODO * 【修复】修复不支持根部门的问题 [commmit](https://gitee.com/zhijiantianya/ruoyi-vue-pro/commit/fa62ace6af5ecc2f3030fa86d2ce222a1392f1a6)
### 🔨 Dependency Upgrades ### 🔨 Dependency Upgrades