Commit 2598c033a95d4b61d5f5ab3da5f1414f25c510d6
1 parent
6b6d676a
【新增】【优化】新建租户时,自动创建对应的管理员账号、角色等基础信息
Showing
34 changed files
with
425 additions
and
64 deletions
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantSecurityAutoConfiguration.java
| ... | ... | @@ -2,7 +2,9 @@ package cn.iocoder.yudao.framework.tenant.config; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.enums.WebFilterOrderEnum; |
| 4 | 4 | import cn.iocoder.yudao.framework.tenant.core.security.TenantSecurityWebFilter; |
| 5 | +import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService; | |
| 5 | 6 | import cn.iocoder.yudao.framework.web.config.WebProperties; |
| 7 | +import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; | |
| 6 | 8 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| 7 | 9 | import org.springframework.boot.context.properties.EnableConfigurationProperties; |
| 8 | 10 | import org.springframework.boot.web.servlet.FilterRegistrationBean; |
| ... | ... | @@ -22,9 +24,12 @@ public class YudaoTenantSecurityAutoConfiguration { |
| 22 | 24 | |
| 23 | 25 | @Bean |
| 24 | 26 | public FilterRegistrationBean<TenantSecurityWebFilter> tenantSecurityWebFilter(TenantProperties tenantProperties, |
| 25 | - WebProperties webProperties) { | |
| 27 | + WebProperties webProperties, | |
| 28 | + GlobalExceptionHandler globalExceptionHandler, | |
| 29 | + TenantFrameworkService tenantFrameworkService) { | |
| 26 | 30 | FilterRegistrationBean<TenantSecurityWebFilter> registrationBean = new FilterRegistrationBean<>(); |
| 27 | - registrationBean.setFilter(new TenantSecurityWebFilter(tenantProperties, webProperties)); | |
| 31 | + registrationBean.setFilter(new TenantSecurityWebFilter(tenantProperties, webProperties, | |
| 32 | + globalExceptionHandler, tenantFrameworkService)); | |
| 28 | 33 | registrationBean.setOrder(WebFilterOrderEnum.TENANT_SECURITY_FILTER); |
| 29 | 34 | return registrationBean; |
| 30 | 35 | } | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/context/TenantContextHolder.java
| ... | ... | @@ -12,11 +12,6 @@ public class TenantContextHolder { |
| 12 | 12 | private static final ThreadLocal<Long> TENANT_ID = new TransmittableThreadLocal<>(); |
| 13 | 13 | |
| 14 | 14 | /** |
| 15 | - * 租户编号 - 空 | |
| 16 | - */ | |
| 17 | - private static final Long TENANT_ID_NULL = 0L; | |
| 18 | - | |
| 19 | - /** | |
| 20 | 15 | * 获得租户编号。 |
| 21 | 16 | * |
| 22 | 17 | * @return 租户编号 |
| ... | ... | @@ -38,15 +33,6 @@ public class TenantContextHolder { |
| 38 | 33 | return tenantId; |
| 39 | 34 | } |
| 40 | 35 | |
| 41 | - /** | |
| 42 | - * 在一些前端场景下,可能无法请求带上租户。例如说,<img /> 方式获取图片等 | |
| 43 | - * 此时,暂时的解决方案,是在该接口的 Controller 方法上,调用该方法 | |
| 44 | - * TODO 芋艿:思考有没更合适的方案,目标是去掉该方法 | |
| 45 | - */ | |
| 46 | - public static void setNullTenantId() { | |
| 47 | - TENANT_ID.set(TENANT_ID_NULL); | |
| 48 | - } | |
| 49 | - | |
| 50 | 36 | public static void setTenantId(Long tenantId) { |
| 51 | 37 | TENANT_ID.set(tenantId); |
| 52 | 38 | } | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/security/TenantSecurityWebFilter.java
| ... | ... | @@ -8,8 +8,10 @@ import cn.iocoder.yudao.framework.security.core.LoginUser; |
| 8 | 8 | import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; |
| 9 | 9 | import cn.iocoder.yudao.framework.tenant.config.TenantProperties; |
| 10 | 10 | import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; |
| 11 | +import cn.iocoder.yudao.framework.tenant.core.service.TenantFrameworkService; | |
| 11 | 12 | import cn.iocoder.yudao.framework.web.config.WebProperties; |
| 12 | 13 | import cn.iocoder.yudao.framework.web.core.filter.ApiRequestFilter; |
| 14 | +import cn.iocoder.yudao.framework.web.core.handler.GlobalExceptionHandler; | |
| 13 | 15 | import lombok.extern.slf4j.Slf4j; |
| 14 | 16 | import org.springframework.util.AntPathMatcher; |
| 15 | 17 | |
| ... | ... | @@ -24,6 +26,7 @@ import java.util.Objects; |
| 24 | 26 | * 多租户 Security Web 过滤器 |
| 25 | 27 | * 1. 如果是登陆的用户,校验是否有权限访问该租户,避免越权问题。 |
| 26 | 28 | * 2. 如果请求未带租户的编号,检查是否是忽略的 URL,否则也不允许访问。 |
| 29 | + * 3. 校验租户是合法,例如说被禁用、到期 | |
| 27 | 30 | * |
| 28 | 31 | * 校验用户访问的租户,是否是其所在的租户, |
| 29 | 32 | * |
| ... | ... | @@ -33,13 +36,21 @@ import java.util.Objects; |
| 33 | 36 | public class TenantSecurityWebFilter extends ApiRequestFilter { |
| 34 | 37 | |
| 35 | 38 | private final TenantProperties tenantProperties; |
| 39 | + | |
| 36 | 40 | private final AntPathMatcher pathMatcher; |
| 37 | 41 | |
| 42 | + private final GlobalExceptionHandler globalExceptionHandler; | |
| 43 | + private final TenantFrameworkService tenantFrameworkService; | |
| 44 | + | |
| 38 | 45 | public TenantSecurityWebFilter(TenantProperties tenantProperties, |
| 39 | - WebProperties webProperties) { | |
| 46 | + WebProperties webProperties, | |
| 47 | + GlobalExceptionHandler globalExceptionHandler, | |
| 48 | + TenantFrameworkService tenantFrameworkService) { | |
| 40 | 49 | super(webProperties); |
| 41 | 50 | this.tenantProperties = tenantProperties; |
| 42 | 51 | this.pathMatcher = new AntPathMatcher(); |
| 52 | + this.globalExceptionHandler = globalExceptionHandler; | |
| 53 | + this.tenantFrameworkService = tenantFrameworkService; | |
| 43 | 54 | } |
| 44 | 55 | |
| 45 | 56 | @Override |
| ... | ... | @@ -72,6 +83,17 @@ public class TenantSecurityWebFilter extends ApiRequestFilter { |
| 72 | 83 | return; |
| 73 | 84 | } |
| 74 | 85 | |
| 86 | + // 3. 校验租户是合法,例如说被禁用、到期 | |
| 87 | + if (tenantId != null) { | |
| 88 | + try { | |
| 89 | + tenantFrameworkService.validTenant(tenantId); | |
| 90 | + } catch (Throwable ex) { | |
| 91 | + CommonResult<?> result = globalExceptionHandler.allExceptionHandler(request, ex); | |
| 92 | + ServletUtils.writeJSON(response, result); | |
| 93 | + return; | |
| 94 | + } | |
| 95 | + } | |
| 96 | + | |
| 75 | 97 | // 继续过滤 |
| 76 | 98 | chain.doFilter(request, response); |
| 77 | 99 | } | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/service/TenantFrameworkService.java
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/core/util/TenantUtils.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.tenant.core.util; | |
| 2 | + | |
| 3 | +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; | |
| 4 | + | |
| 5 | +/** | |
| 6 | + * 多租户 Util | |
| 7 | + * | |
| 8 | + * @author 芋道源码 | |
| 9 | + */ | |
| 10 | +public class TenantUtils { | |
| 11 | + | |
| 12 | + /** | |
| 13 | + * 使用指定租户,执行对应的逻辑 | |
| 14 | + * | |
| 15 | + * @param tenantId 租户编号 | |
| 16 | + * @param runnable 逻辑 | |
| 17 | + */ | |
| 18 | + public static void execute(Long tenantId, Runnable runnable) { | |
| 19 | + Long oldTenantId = TenantContextHolder.getTenantId(); | |
| 20 | + try { | |
| 21 | + TenantContextHolder.setTenantId(tenantId); | |
| 22 | + // 执行逻辑 | |
| 23 | + runnable.run(); | |
| 24 | + } finally { | |
| 25 | + TenantContextHolder.setTenantId(oldTenantId); | |
| 26 | + } | |
| 27 | + } | |
| 28 | + | |
| 29 | + | |
| 30 | +} | ... | ... |
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java
| ... | ... | @@ -104,10 +104,13 @@ public interface ErrorCodeConstants { |
| 104 | 104 | |
| 105 | 105 | // ========== 租户信息 1002014000 ========== |
| 106 | 106 | ErrorCode TENANT_NOT_EXISTS = new ErrorCode(1002014000, "租户不存在"); |
| 107 | + ErrorCode TENANT_DISABLE = new ErrorCode(1002014001, "名字为【{}】的租户已被禁用"); | |
| 108 | + ErrorCode TENANT_EXPIRE = new ErrorCode(1002014002, "名字为【{}】的租户已过期"); | |
| 107 | 109 | |
| 108 | 110 | // ========== 租户套餐 1002015000 ========== |
| 109 | 111 | ErrorCode TENANT_PACKAGE_NOT_EXISTS = new ErrorCode(1002015000, "租户套餐不存在"); |
| 110 | 112 | ErrorCode TENANT_PACKAGE_USED = new ErrorCode(1002015001, "租户正在使用该套餐,请给租户重新设置套餐后再尝试删除"); |
| 113 | + ErrorCode TENANT_PACKAGE_DISABLE = new ErrorCode(1002015002, "名字为【{}】的租户套餐已被禁用"); | |
| 111 | 114 | |
| 112 | 115 | // ========== 错误码模块 1002016000 ========== |
| 113 | 116 | ErrorCode ERROR_CODE_NOT_EXISTS = new ErrorCode(1002016000, "错误码不存在"); | ... | ... |
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/RoleController.java
| ... | ... | @@ -40,7 +40,7 @@ public class RoleController { |
| 40 | 40 | @ApiOperation("创建角色") |
| 41 | 41 | @PreAuthorize("@ss.hasPermission('system:role:create')") |
| 42 | 42 | public CommonResult<Long> createRole(@Valid @RequestBody RoleCreateReqVO reqVO) { |
| 43 | - return success(roleService.createRole(reqVO)); | |
| 43 | + return success(roleService.createRole(reqVO, null)); | |
| 44 | 44 | } |
| 45 | 45 | |
| 46 | 46 | @PutMapping("/update") |
| ... | ... | @@ -88,7 +88,7 @@ public class RoleController { |
| 88 | 88 | public CommonResult<List<RoleSimpleRespVO>> getSimpleRoles() { |
| 89 | 89 | // 获得角色列表,只要开启状态的 |
| 90 | 90 | List<RoleDO> list = roleService.getRoles(Collections.singleton(CommonStatusEnum.ENABLE.getStatus())); |
| 91 | - // 排序后,返回个诶前端 | |
| 91 | + // 排序后,返回给前端 | |
| 92 | 92 | list.sort(Comparator.comparing(RoleDO::getSort)); |
| 93 | 93 | return success(RoleConvert.INSTANCE.convertList02(list)); |
| 94 | 94 | } | ... | ... |
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleBaseVO.java
| ... | ... | @@ -28,9 +28,6 @@ public class RoleBaseVO { |
| 28 | 28 | @NotNull(message = "显示顺序不能为空") |
| 29 | 29 | private Integer sort; |
| 30 | 30 | |
| 31 | - @ApiModelProperty(value = "角色类型", required = true, example = "1", notes = "见 RoleTypeEnum 枚举") | |
| 32 | - private Integer type; | |
| 33 | - | |
| 34 | 31 | @ApiModelProperty(value = "备注", example = "我是一个角色") |
| 35 | 32 | private String remark; |
| 36 | 33 | ... | ... |
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantController.http
0 → 100644
| 1 | +### 创建租户 /admin-api/system/tenant/create | |
| 2 | +POST {{baseUrl}}/system/tenant/create | |
| 3 | +Content-Type: application/json | |
| 4 | +Authorization: Bearer {{token}} | |
| 5 | +tenant-id: {{adminTenentId}} | |
| 6 | + | |
| 7 | +{ | |
| 8 | + "name": "芋道", | |
| 9 | + "contactName": "芋艿", | |
| 10 | + "contactMobile": "15601691300", | |
| 11 | + "status": 0, | |
| 12 | + "domain": "https://www.iocoder.cn", | |
| 13 | + "packageId": 110, | |
| 14 | + "expireTime": 1699545600000, | |
| 15 | + "accountCount": 20, | |
| 16 | + "username": "admin", | |
| 17 | + "password": "123321" | |
| 18 | +} | ... | ... |
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/TenantPackageController.java
| 1 | 1 | package cn.iocoder.yudao.module.system.controller.admin.tenant; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | |
| 3 | 4 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 4 | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | -import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackageCreateReqVO; | |
| 6 | -import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackagePageReqVO; | |
| 7 | -import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackageRespVO; | |
| 8 | -import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackageUpdateReqVO; | |
| 6 | +import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.*; | |
| 9 | 7 | import cn.iocoder.yudao.module.system.convert.tenant.TenantPackageConvert; |
| 10 | 8 | import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO; |
| 11 | 9 | import cn.iocoder.yudao.module.system.service.tenant.TenantPackageService; |
| ... | ... | @@ -82,4 +80,12 @@ public class TenantPackageController { |
| 82 | 80 | return success(TenantPackageConvert.INSTANCE.convertPage(pageResult)); |
| 83 | 81 | } |
| 84 | 82 | |
| 83 | + @GetMapping("/get-simple-list") | |
| 84 | + @ApiOperation(value = "获取租户套餐精简信息列表", notes = "只包含被开启的租户套餐,主要用于前端的下拉选项") | |
| 85 | + public CommonResult<List<TenantPackageSimpleRespVO>> getTenantPackageList() { | |
| 86 | + // 获得角色列表,只要开启状态的 | |
| 87 | + List<TenantPackageDO> list = tenantPackageService.getTenantPackageListByStatus(CommonStatusEnum.ENABLE.getStatus()); | |
| 88 | + return success(TenantPackageConvert.INSTANCE.convertList02(list)); | |
| 89 | + } | |
| 90 | + | |
| 85 | 91 | } | ... | ... |
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/packages/TenantPackageSimpleRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages; | |
| 2 | + | |
| 3 | +import io.swagger.annotations.ApiModel; | |
| 4 | +import io.swagger.annotations.ApiModelProperty; | |
| 5 | +import lombok.Data; | |
| 6 | + | |
| 7 | +import javax.validation.constraints.NotNull; | |
| 8 | + | |
| 9 | +@ApiModel("管理后台 - 租户套餐精简 Response VO") | |
| 10 | +@Data | |
| 11 | +public class TenantPackageSimpleRespVO { | |
| 12 | + | |
| 13 | + @ApiModelProperty(value = "套餐编号", required = true, example = "1024") | |
| 14 | + @NotNull(message = "套餐编号不能为空") | |
| 15 | + private Long id; | |
| 16 | + | |
| 17 | + @ApiModelProperty(value = "套餐名", required = true, example = "VIP") | |
| 18 | + @NotNull(message = "套餐名不能为空") | |
| 19 | + private String name; | |
| 20 | + | |
| 21 | +} | ... | ... |
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantBaseVO.java
| ... | ... | @@ -2,7 +2,10 @@ package cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant; |
| 2 | 2 | |
| 3 | 3 | import lombok.*; |
| 4 | 4 | import io.swagger.annotations.*; |
| 5 | +import org.hibernate.validator.constraints.URL; | |
| 6 | + | |
| 5 | 7 | import javax.validation.constraints.*; |
| 8 | +import java.util.Date; | |
| 6 | 9 | |
| 7 | 10 | /** |
| 8 | 11 | * 租户 Base VO,提供给添加、修改、详细的子 VO 使用 |
| ... | ... | @@ -22,8 +25,24 @@ public class TenantBaseVO { |
| 22 | 25 | @ApiModelProperty(value = "联系手机", example = "15601691300") |
| 23 | 26 | private String contactMobile; |
| 24 | 27 | |
| 25 | - @ApiModelProperty(value = "租户状态(0正常 1停用)", required = true, example = "1") | |
| 26 | - @NotNull(message = "租户状态(0正常 1停用)不能为空") | |
| 28 | + @ApiModelProperty(value = "租户状态", required = true, example = "1") | |
| 29 | + @NotNull(message = "租户状态") | |
| 27 | 30 | private Integer status; |
| 28 | 31 | |
| 32 | + @ApiModelProperty(value = "绑定域名", example = "https://www.iocoder.cn") | |
| 33 | + @URL(message = "绑定域名的地址非 URL 格式") | |
| 34 | + private String domain; | |
| 35 | + | |
| 36 | + @ApiModelProperty(value = "租户套餐编号", required = true, example = "1024") | |
| 37 | + @NotNull(message = "租户套餐编号不能为空") | |
| 38 | + private Long packageId; | |
| 39 | + | |
| 40 | + @ApiModelProperty(value = "过期时间", required = true) | |
| 41 | + @NotNull(message = "过期时间不能为空") | |
| 42 | + private Date expireTime; | |
| 43 | + | |
| 44 | + @ApiModelProperty(value = "账号数量", required = true, example = "1024") | |
| 45 | + @NotNull(message = "账号数量不能为空") | |
| 46 | + private Integer accountCount; | |
| 47 | + | |
| 29 | 48 | } | ... | ... |
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantCreateReqVO.java
| ... | ... | @@ -2,6 +2,12 @@ package cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant; |
| 2 | 2 | |
| 3 | 3 | import lombok.*; |
| 4 | 4 | import io.swagger.annotations.*; |
| 5 | +import org.hibernate.validator.constraints.Length; | |
| 6 | + | |
| 7 | +import javax.validation.constraints.NotBlank; | |
| 8 | +import javax.validation.constraints.NotEmpty; | |
| 9 | +import javax.validation.constraints.Pattern; | |
| 10 | +import javax.validation.constraints.Size; | |
| 5 | 11 | |
| 6 | 12 | @ApiModel("管理后台 - 租户创建 Request VO") |
| 7 | 13 | @Data |
| ... | ... | @@ -9,4 +15,15 @@ import io.swagger.annotations.*; |
| 9 | 15 | @ToString(callSuper = true) |
| 10 | 16 | public class TenantCreateReqVO extends TenantBaseVO { |
| 11 | 17 | |
| 18 | + @ApiModelProperty(value = "用户账号", required = true, example = "yudao") | |
| 19 | + @NotBlank(message = "用户账号不能为空") | |
| 20 | + @Pattern(regexp = "^[a-zA-Z0-9]{4,30}$", message = "用户账号由 数字、字母 组成") | |
| 21 | + @Size(min = 4, max = 30, message = "用户账号长度为 4-30 个字符") | |
| 22 | + private String username; | |
| 23 | + | |
| 24 | + @ApiModelProperty(value = "密码", required = true, example = "123456") | |
| 25 | + @NotEmpty(message = "密码不能为空") | |
| 26 | + @Length(min = 4, max = 16, message = "密码长度为 4-16 位") | |
| 27 | + private String password; | |
| 28 | + | |
| 12 | 29 | } | ... | ... |
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserBaseVO.java
| 1 | 1 | package cn.iocoder.yudao.module.system.controller.admin.user.vo.user; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.framework.common.validation.Mobile; | |
| 3 | 4 | import io.swagger.annotations.ApiModelProperty; |
| 4 | 5 | import lombok.Data; |
| 5 | -import org.hibernate.validator.constraints.Length; | |
| 6 | 6 | |
| 7 | 7 | import javax.validation.constraints.Email; |
| 8 | 8 | import javax.validation.constraints.NotBlank; |
| ... | ... | @@ -42,7 +42,7 @@ public class UserBaseVO { |
| 42 | 42 | private String email; |
| 43 | 43 | |
| 44 | 44 | @ApiModelProperty(value = "手机号码", example = "15601691300") |
| 45 | - @Length(min = 11, max = 11, message = "手机号长度必须 11 位") | |
| 45 | + @Mobile | |
| 46 | 46 | private String mobile; |
| 47 | 47 | |
| 48 | 48 | @ApiModelProperty(value = "用户性别", example = "1", notes = "参见 SexEnum 枚举类") | ... | ... |
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/convert/permission/RoleConvert.java
| ... | ... | @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.convert.permission; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.*; |
| 4 | 4 | import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; |
| 5 | +import cn.iocoder.yudao.module.system.service.permission.bo.RoleCreateReqBO; | |
| 5 | 6 | import org.mapstruct.Mapper; |
| 6 | 7 | import org.mapstruct.factory.Mappers; |
| 7 | 8 | |
| ... | ... | @@ -22,4 +23,6 @@ public interface RoleConvert { |
| 22 | 23 | |
| 23 | 24 | List<RoleExcelVO> convertList03(List<RoleDO> list); |
| 24 | 25 | |
| 26 | + RoleDO convert(RoleCreateReqBO bean); | |
| 27 | + | |
| 25 | 28 | } | ... | ... |
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/convert/tenant/TenantConvert.java
| 1 | 1 | package cn.iocoder.yudao.module.system.convert.tenant; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 3 | 4 | import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantCreateReqVO; |
| 4 | 5 | import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantExcelVO; |
| 5 | 6 | import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantRespVO; |
| 6 | 7 | import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantUpdateReqVO; |
| 8 | +import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserCreateReqVO; | |
| 7 | 9 | import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; |
| 8 | -import cn.iocoder.yudao.framework.common.pojo.PageResult; | |
| 9 | 10 | import org.mapstruct.Mapper; |
| 10 | 11 | import org.mapstruct.factory.Mappers; |
| 11 | 12 | |
| ... | ... | @@ -33,4 +34,12 @@ public interface TenantConvert { |
| 33 | 34 | |
| 34 | 35 | List<TenantExcelVO> convertList02(List<TenantDO> list); |
| 35 | 36 | |
| 37 | + default UserCreateReqVO convert02(TenantCreateReqVO bean) { | |
| 38 | + UserCreateReqVO reqVO = new UserCreateReqVO(); | |
| 39 | + reqVO.setUsername(bean.getUsername()); | |
| 40 | + reqVO.setPassword(bean.getPassword()); | |
| 41 | + reqVO.setNickname(bean.getContactName()).setMobile(bean.getContactMobile()); | |
| 42 | + return reqVO; | |
| 43 | + } | |
| 44 | + | |
| 36 | 45 | } | ... | ... |
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/convert/tenant/TenantPackageConvert.java
| 1 | 1 | package cn.iocoder.yudao.module.system.convert.tenant; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleSimpleRespVO; | |
| 4 | 5 | import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackageCreateReqVO; |
| 5 | 6 | import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackageRespVO; |
| 7 | +import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackageSimpleRespVO; | |
| 6 | 8 | import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackageUpdateReqVO; |
| 7 | 9 | import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO; |
| 8 | 10 | import org.mapstruct.Mapper; |
| ... | ... | @@ -30,4 +32,6 @@ public interface TenantPackageConvert { |
| 30 | 32 | |
| 31 | 33 | PageResult<TenantPackageRespVO> convertPage(PageResult<TenantPackageDO> page); |
| 32 | 34 | |
| 35 | + List<TenantPackageSimpleRespVO> convertList02(List<TenantPackageDO> list); | |
| 36 | + | |
| 33 | 37 | } | ... | ... |
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/permission/RoleDO.java
| ... | ... | @@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 4 | 4 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 5 | 5 | import cn.iocoder.yudao.framework.mybatis.core.type.JsonLongSetTypeHandler; |
| 6 | 6 | import cn.iocoder.yudao.framework.security.core.enums.DataScopeEnum; |
| 7 | +import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum; | |
| 7 | 8 | import com.baomidou.mybatisplus.annotation.TableField; |
| 8 | 9 | import com.baomidou.mybatisplus.annotation.TableId; |
| 9 | 10 | import com.baomidou.mybatisplus.annotation.TableName; |
| ... | ... | @@ -50,7 +51,7 @@ public class RoleDO extends BaseDO { |
| 50 | 51 | /** |
| 51 | 52 | * 角色类型 |
| 52 | 53 | * |
| 53 | - * 枚举 | |
| 54 | + * 枚举 {@link RoleTypeEnum} | |
| 54 | 55 | */ |
| 55 | 56 | private Integer type; |
| 56 | 57 | /** | ... | ... |
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenant/TenantPackageMapper.java
| ... | ... | @@ -7,6 +7,8 @@ import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.Tenant |
| 7 | 7 | import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO; |
| 8 | 8 | import org.apache.ibatis.annotations.Mapper; |
| 9 | 9 | |
| 10 | +import java.util.List; | |
| 11 | + | |
| 10 | 12 | /** |
| 11 | 13 | * 租户套餐 Mapper |
| 12 | 14 | * |
| ... | ... | @@ -24,4 +26,7 @@ public interface TenantPackageMapper extends BaseMapperX<TenantPackageDO> { |
| 24 | 26 | .orderByDesc(TenantPackageDO::getId)); |
| 25 | 27 | } |
| 26 | 28 | |
| 29 | + default List<TenantPackageDO> selectListByStatus(Integer status) { | |
| 30 | + return selectList(TenantPackageDO::getStatus, status); | |
| 31 | + } | |
| 27 | 32 | } | ... | ... |
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceImpl.java
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleService.java
| ... | ... | @@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleUp |
| 8 | 8 | import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; |
| 9 | 9 | import org.springframework.lang.Nullable; |
| 10 | 10 | |
| 11 | +import javax.validation.Valid; | |
| 11 | 12 | import java.util.Collection; |
| 12 | 13 | import java.util.List; |
| 13 | 14 | import java.util.Set; |
| ... | ... | @@ -28,16 +29,17 @@ public interface RoleService { |
| 28 | 29 | * 创建角色 |
| 29 | 30 | * |
| 30 | 31 | * @param reqVO 创建角色信息 |
| 32 | + * @param type 角色类型 | |
| 31 | 33 | * @return 角色编号 |
| 32 | 34 | */ |
| 33 | - Long createRole(RoleCreateReqVO reqVO); | |
| 35 | + Long createRole(@Valid RoleCreateReqVO reqVO, Integer type); | |
| 34 | 36 | |
| 35 | 37 | /** |
| 36 | 38 | * 更新角色 |
| 37 | 39 | * |
| 38 | 40 | * @param reqVO 更新角色信息 |
| 39 | 41 | */ |
| 40 | - void updateRole(RoleUpdateReqVO reqVO); | |
| 42 | + void updateRole(@Valid RoleUpdateReqVO reqVO); | |
| 41 | 43 | |
| 42 | 44 | /** |
| 43 | 45 | * 删除角色 | ... | ... |
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImpl.java
| ... | ... | @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.service.permission; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.collection.CollectionUtil; |
| 5 | +import cn.hutool.core.util.ObjectUtil; | |
| 5 | 6 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 6 | 7 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 7 | 8 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| ... | ... | @@ -120,12 +121,12 @@ public class RoleServiceImpl implements RoleService { |
| 120 | 121 | } |
| 121 | 122 | |
| 122 | 123 | @Override |
| 123 | - public Long createRole(RoleCreateReqVO reqVO) { | |
| 124 | + public Long createRole(RoleCreateReqVO reqVO, Integer type) { | |
| 124 | 125 | // 校验角色 |
| 125 | 126 | checkDuplicateRole(reqVO.getName(), reqVO.getCode(), null); |
| 126 | 127 | // 插入到数据库 |
| 127 | 128 | RoleDO role = RoleConvert.INSTANCE.convert(reqVO); |
| 128 | - role.setType(RoleTypeEnum.CUSTOM.getType()); | |
| 129 | + role.setType(ObjectUtil.defaultIfNull(type, RoleTypeEnum.CUSTOM.getType())); | |
| 129 | 130 | role.setStatus(CommonStatusEnum.ENABLE.getStatus()); |
| 130 | 131 | role.setDataScope(DataScopeEnum.ALL.getScope()); // 默认可查看所有数据。原因是,可能一些项目不需要项目权限 |
| 131 | 132 | roleMapper.insert(role); | ... | ... |
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/permission/bo/RoleCreateReqBO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.system.service.permission.bo; | |
| 2 | + | |
| 3 | +import lombok.Data; | |
| 4 | + | |
| 5 | +import javax.validation.constraints.NotBlank; | |
| 6 | +import javax.validation.constraints.NotNull; | |
| 7 | +import javax.validation.constraints.Size; | |
| 8 | + | |
| 9 | +/** | |
| 10 | + * 角色创建 Request BO | |
| 11 | + * | |
| 12 | + * @author 芋道源码 | |
| 13 | + */ | |
| 14 | +@Data | |
| 15 | +public class RoleCreateReqBO { | |
| 16 | + | |
| 17 | + /** | |
| 18 | + * 租户编号 | |
| 19 | + */ | |
| 20 | + @NotNull(message = "租户编号不能为空") | |
| 21 | + private Long tenantId; | |
| 22 | + | |
| 23 | + /** | |
| 24 | + * 角色名称 | |
| 25 | + */ | |
| 26 | + @NotBlank(message = "角色名称不能为空") | |
| 27 | + @Size(max = 30, message = "角色名称长度不能超过30个字符") | |
| 28 | + private String name; | |
| 29 | + | |
| 30 | + /** | |
| 31 | + * 角色标志 | |
| 32 | + */ | |
| 33 | + @NotBlank(message = "角色标志不能为空") | |
| 34 | + @Size(max = 100, message = "角色标志长度不能超过100个字符") | |
| 35 | + private String code; | |
| 36 | + | |
| 37 | + /** | |
| 38 | + * 显示顺序 | |
| 39 | + */ | |
| 40 | + @NotNull(message = "显示顺序不能为空") | |
| 41 | + private Integer sort; | |
| 42 | + | |
| 43 | + /** | |
| 44 | + * 角色类型 | |
| 45 | + */ | |
| 46 | + @NotNull(message = "角色类型不能为空") | |
| 47 | + private Integer type; | |
| 48 | + | |
| 49 | +} | ... | ... |
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageService.java
| ... | ... | @@ -63,4 +63,20 @@ public interface TenantPackageService { |
| 63 | 63 | */ |
| 64 | 64 | PageResult<TenantPackageDO> getTenantPackagePage(TenantPackagePageReqVO pageReqVO); |
| 65 | 65 | |
| 66 | + /** | |
| 67 | + * 校验租户套餐 | |
| 68 | + * | |
| 69 | + * @param id 编号 | |
| 70 | + * @return 租户套餐 | |
| 71 | + */ | |
| 72 | + TenantPackageDO validTenantPackage(Long id); | |
| 73 | + | |
| 74 | + /** | |
| 75 | + * 获得指定状态的租户套餐列表 | |
| 76 | + * | |
| 77 | + * @param status 状态 | |
| 78 | + * @return 租户套餐 | |
| 79 | + */ | |
| 80 | + List<TenantPackageDO> getTenantPackageListByStatus(Integer status); | |
| 81 | + | |
| 66 | 82 | } | ... | ... |
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantPackageServiceImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.system.service.tenant; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | |
| 3 | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | 5 | import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackageCreateReqVO; |
| 5 | 6 | import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.TenantPackagePageReqVO; |
| ... | ... | @@ -7,6 +8,7 @@ import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.packages.Tenant |
| 7 | 8 | import cn.iocoder.yudao.module.system.convert.tenant.TenantPackageConvert; |
| 8 | 9 | import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantPackageDO; |
| 9 | 10 | import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantPackageMapper; |
| 11 | +import org.springframework.context.annotation.Lazy; | |
| 10 | 12 | import org.springframework.stereotype.Service; |
| 11 | 13 | import org.springframework.validation.annotation.Validated; |
| 12 | 14 | |
| ... | ... | @@ -15,8 +17,7 @@ import java.util.Collection; |
| 15 | 17 | import java.util.List; |
| 16 | 18 | |
| 17 | 19 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 18 | -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.TENANT_PACKAGE_NOT_EXISTS; | |
| 19 | -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.TENANT_PACKAGE_USED; | |
| 20 | +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; | |
| 20 | 21 | |
| 21 | 22 | /** |
| 22 | 23 | * 租户套餐 Service 实现类 |
| ... | ... | @@ -31,6 +32,7 @@ public class TenantPackageServiceImpl implements TenantPackageService { |
| 31 | 32 | private TenantPackageMapper tenantPackageMapper; |
| 32 | 33 | |
| 33 | 34 | @Resource |
| 35 | + @Lazy // 避免循环依赖的报错 | |
| 34 | 36 | private TenantService tenantService; |
| 35 | 37 | |
| 36 | 38 | @Override |
| ... | ... | @@ -88,4 +90,21 @@ public class TenantPackageServiceImpl implements TenantPackageService { |
| 88 | 90 | return tenantPackageMapper.selectPage(pageReqVO); |
| 89 | 91 | } |
| 90 | 92 | |
| 93 | + @Override | |
| 94 | + public TenantPackageDO validTenantPackage(Long id) { | |
| 95 | + TenantPackageDO tenantPackage = tenantPackageMapper.selectById(id); | |
| 96 | + if (tenantPackage == null) { | |
| 97 | + throw exception(TENANT_PACKAGE_NOT_EXISTS); | |
| 98 | + } | |
| 99 | + if (tenantPackage.getStatus().equals(CommonStatusEnum.DISABLE.getStatus())) { | |
| 100 | + throw exception(TENANT_PACKAGE_DISABLE, tenantPackage.getName()); | |
| 101 | + } | |
| 102 | + return tenantPackage; | |
| 103 | + } | |
| 104 | + | |
| 105 | + @Override | |
| 106 | + public List<TenantPackageDO> getTenantPackageListByStatus(Integer status) { | |
| 107 | + return tenantPackageMapper.selectListByStatus(status); | |
| 108 | + } | |
| 109 | + | |
| 91 | 110 | } | ... | ... |
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImpl.java
| 1 | 1 | package cn.iocoder.yudao.module.system.service.tenant; |
| 2 | 2 | |
| 3 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | |
| 3 | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | 5 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 6 | +import cn.iocoder.yudao.framework.common.util.date.DateUtils; | |
| 7 | +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; | |
| 8 | +import cn.iocoder.yudao.module.system.controller.admin.permission.vo.role.RoleCreateReqVO; | |
| 5 | 9 | import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantCreateReqVO; |
| 6 | 10 | import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantExportReqVO; |
| 7 | 11 | import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantPageReqVO; |
| ... | ... | @@ -9,15 +13,22 @@ import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantUp |
| 9 | 13 | import cn.iocoder.yudao.module.system.convert.tenant.TenantConvert; |
| 10 | 14 | import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; |
| 11 | 15 | import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantMapper; |
| 16 | +import cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum; | |
| 17 | +import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum; | |
| 18 | +import cn.iocoder.yudao.module.system.service.permission.PermissionService; | |
| 19 | +import cn.iocoder.yudao.module.system.service.permission.RoleService; | |
| 20 | +import cn.iocoder.yudao.module.system.service.user.AdminUserService; | |
| 12 | 21 | import org.springframework.stereotype.Service; |
| 22 | +import org.springframework.transaction.annotation.Transactional; | |
| 13 | 23 | import org.springframework.validation.annotation.Validated; |
| 14 | 24 | |
| 15 | 25 | import javax.annotation.Resource; |
| 16 | 26 | import java.util.Collection; |
| 27 | +import java.util.Collections; | |
| 17 | 28 | import java.util.List; |
| 18 | 29 | |
| 19 | 30 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 20 | -import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.TENANT_NOT_EXISTS; | |
| 31 | +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; | |
| 21 | 32 | |
| 22 | 33 | /** |
| 23 | 34 | * 租户 Service 实现类 |
| ... | ... | @@ -31,6 +42,15 @@ public class TenantServiceImpl implements TenantService { |
| 31 | 42 | @Resource |
| 32 | 43 | private TenantMapper tenantMapper; |
| 33 | 44 | |
| 45 | + @Resource | |
| 46 | + private TenantPackageService tenantPackageService; | |
| 47 | + @Resource | |
| 48 | + private AdminUserService userService; | |
| 49 | + @Resource | |
| 50 | + private RoleService roleService; | |
| 51 | + @Resource | |
| 52 | + private PermissionService permissionService; | |
| 53 | + | |
| 34 | 54 | @Override |
| 35 | 55 | public List<Long> getTenantIds() { |
| 36 | 56 | List<TenantDO> tenants = tenantMapper.selectList(); |
| ... | ... | @@ -38,18 +58,62 @@ public class TenantServiceImpl implements TenantService { |
| 38 | 58 | } |
| 39 | 59 | |
| 40 | 60 | @Override |
| 61 | + public void validTenant(Long id) { | |
| 62 | + TenantDO tenant = tenantMapper.selectById(id); | |
| 63 | + if (tenant == null) { | |
| 64 | + throw exception(TENANT_NOT_EXISTS); | |
| 65 | + } | |
| 66 | + if (tenant.getStatus().equals(CommonStatusEnum.DISABLE.getStatus())) { | |
| 67 | + throw exception(TENANT_DISABLE, tenant.getName()); | |
| 68 | + } | |
| 69 | + if (DateUtils.isExpired(tenant.getExpireTime())) { | |
| 70 | + throw exception(TENANT_EXPIRE, tenant.getName()); | |
| 71 | + } | |
| 72 | + } | |
| 73 | + | |
| 74 | + @Override | |
| 75 | + @Transactional(rollbackFor = Exception.class) | |
| 41 | 76 | public Long createTenant(TenantCreateReqVO createReqVO) { |
| 42 | - // 插入 | |
| 77 | + // 校验套餐被禁用 | |
| 78 | + tenantPackageService.validTenantPackage(createReqVO.getPackageId()); | |
| 79 | + | |
| 80 | + // 创建租户 | |
| 43 | 81 | TenantDO tenant = TenantConvert.INSTANCE.convert(createReqVO); |
| 44 | 82 | tenantMapper.insert(tenant); |
| 83 | + | |
| 84 | + TenantUtils.execute(tenant.getId(), () -> { | |
| 85 | + // 创建角色 | |
| 86 | + Long roleId = createRole(); | |
| 87 | + // 创建用户,并分配角色 | |
| 88 | + Long userId = createUser(roleId, createReqVO); | |
| 89 | + // 修改租户的管理员 | |
| 90 | + tenantMapper.updateById(new TenantDO().setId(tenant.getId()).setContactUserId(userId)); | |
| 91 | + }); | |
| 45 | 92 | // 返回 |
| 46 | 93 | return tenant.getId(); |
| 47 | 94 | } |
| 48 | 95 | |
| 96 | + private Long createUser(Long roleId, TenantCreateReqVO createReqVO) { | |
| 97 | + // 创建用户 | |
| 98 | + Long userId = userService.createUser(TenantConvert.INSTANCE.convert02(createReqVO)); | |
| 99 | + // 分配角色 | |
| 100 | + permissionService.assignUserRole(userId, Collections.singleton(roleId)); | |
| 101 | + return userId; | |
| 102 | + } | |
| 103 | + | |
| 104 | + private Long createRole() { | |
| 105 | + RoleCreateReqVO reqVO = new RoleCreateReqVO(); | |
| 106 | + reqVO.setName(RoleCodeEnum.ADMIN.name()).setCode(RoleCodeEnum.ADMIN.getKey()).setSort(0); | |
| 107 | + return roleService.createRole(reqVO, RoleTypeEnum.SYSTEM.getType()); | |
| 108 | + } | |
| 109 | + | |
| 49 | 110 | @Override |
| 50 | 111 | public void updateTenant(TenantUpdateReqVO updateReqVO) { |
| 51 | 112 | // 校验存在 |
| 52 | 113 | this.validateTenantExists(updateReqVO.getId()); |
| 114 | + // 校验套餐被禁用 | |
| 115 | + tenantPackageService.validTenantPackage(updateReqVO.getPackageId()); | |
| 116 | + | |
| 53 | 117 | // 更新 |
| 54 | 118 | TenantDO updateObj = TenantConvert.INSTANCE.convert(updateReqVO); |
| 55 | 119 | tenantMapper.updateById(updateObj); | ... | ... |
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java
| ... | ... | @@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.*; |
| 8 | 8 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 9 | 9 | import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; |
| 10 | 10 | |
| 11 | +import javax.validation.Valid; | |
| 11 | 12 | import java.io.InputStream; |
| 12 | 13 | import java.util.*; |
| 13 | 14 | |
| ... | ... | @@ -24,14 +25,14 @@ public interface AdminUserService { |
| 24 | 25 | * @param reqVO 用户信息 |
| 25 | 26 | * @return 用户编号 |
| 26 | 27 | */ |
| 27 | - Long createUser(UserCreateReqVO reqVO); | |
| 28 | + Long createUser(@Valid UserCreateReqVO reqVO); | |
| 28 | 29 | |
| 29 | 30 | /** |
| 30 | 31 | * 修改用户 |
| 31 | 32 | * |
| 32 | 33 | * @param reqVO 用户信息 |
| 33 | 34 | */ |
| 34 | - void updateUser(UserUpdateReqVO reqVO); | |
| 35 | + void updateUser(@Valid UserUpdateReqVO reqVO); | |
| 35 | 36 | |
| 36 | 37 | /** |
| 37 | 38 | * 更新用户的最后登陆信息 |
| ... | ... | @@ -47,7 +48,7 @@ public interface AdminUserService { |
| 47 | 48 | * @param id 用户编号 |
| 48 | 49 | * @param reqVO 用户个人信息 |
| 49 | 50 | */ |
| 50 | - void updateUserProfile(Long id, UserProfileUpdateReqVO reqVO); | |
| 51 | + void updateUserProfile(Long id, @Valid UserProfileUpdateReqVO reqVO); | |
| 51 | 52 | |
| 52 | 53 | /** |
| 53 | 54 | * 修改用户个人密码 |
| ... | ... | @@ -55,7 +56,7 @@ public interface AdminUserService { |
| 55 | 56 | * @param id 用户编号 |
| 56 | 57 | * @param reqVO 更新用户个人密码 |
| 57 | 58 | */ |
| 58 | - void updateUserPassword(Long id, UserProfileUpdatePasswordReqVO reqVO); | |
| 59 | + void updateUserPassword(Long id, @Valid UserProfileUpdatePasswordReqVO reqVO); | |
| 59 | 60 | |
| 60 | 61 | /** |
| 61 | 62 | * 更新用户头像 | ... | ... |
yudao-module-system/yudao-module-system-impl/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java
| ... | ... | @@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.system.service.user; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.io.IoUtil; |
| 5 | -import cn.hutool.core.util.IdUtil; | |
| 6 | 5 | import cn.hutool.core.util.StrUtil; |
| 7 | 6 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 8 | 7 | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| ... | ... | @@ -258,7 +257,7 @@ public class AdminUserServiceImpl implements AdminUserService { |
| 258 | 257 | // 校验邮箱唯一 |
| 259 | 258 | this.checkEmailUnique(id, email); |
| 260 | 259 | // 校验部门处于开启状态 |
| 261 | - deptService.validDepts(Collections.singleton(deptId)); | |
| 260 | + deptService.validDepts(CollectionUtils.singleton(deptId)); | |
| 262 | 261 | // 校验岗位处于开启状态 |
| 263 | 262 | postService.validPosts(postIds); |
| 264 | 263 | } | ... | ... |
yudao-module-system/yudao-module-system-impl/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceTest.java
| ... | ... | @@ -72,10 +72,9 @@ public class RoleServiceTest extends BaseDbUnitTest { |
| 72 | 72 | o.setCode("role_code"); |
| 73 | 73 | o.setName("role_name"); |
| 74 | 74 | o.setRemark("remark"); |
| 75 | - o.setType(RoleTypeEnum.CUSTOM.getType()); | |
| 76 | 75 | o.setSort(1); |
| 77 | 76 | }); |
| 78 | - Long roleId = sysRoleService.createRole(reqVO); | |
| 77 | + Long roleId = sysRoleService.createRole(reqVO, null); | |
| 79 | 78 | |
| 80 | 79 | //断言 |
| 81 | 80 | assertNotNull(roleId); |
| ... | ... | @@ -96,7 +95,6 @@ public class RoleServiceTest extends BaseDbUnitTest { |
| 96 | 95 | o.setId(roleId); |
| 97 | 96 | o.setCode("role_code"); |
| 98 | 97 | o.setName("update_name"); |
| 99 | - o.setType(RoleTypeEnum.SYSTEM.getType()); | |
| 100 | 98 | o.setSort(999); |
| 101 | 99 | }); |
| 102 | 100 | sysRoleService.updateRole(reqVO); | ... | ... |
yudao-server/src/main/resources/application.yaml
| ... | ... | @@ -80,7 +80,7 @@ yudao: |
| 80 | 80 | tenant: # 多租户相关配置项 |
| 81 | 81 | enable: true |
| 82 | 82 | ignore-urls: /admin-api/system/captcha/get-image, /admin-api/infra/file/get/* |
| 83 | - 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_role, system_role_menu, system_sms_channel, tool_codegen_column, tool_codegen_table, tool_test_demo, tables, columns | |
| 83 | + 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, tool_codegen_column, tool_codegen_table, tool_test_demo, tables, columns | |
| 84 | 84 | sms-code: # 短信验证码相关的配置项 |
| 85 | 85 | expire-times: 10m |
| 86 | 86 | send-frequency: 1m | ... | ... |
yudao-ui-admin/src/api/system/tenantPackage.js
| ... | ... | @@ -43,12 +43,10 @@ export function getTenantPackagePage(query) { |
| 43 | 43 | }) |
| 44 | 44 | } |
| 45 | 45 | |
| 46 | -// 导出租户套餐 Excel | |
| 47 | -export function exportTenantPackageExcel(query) { | |
| 46 | +// 获取租户套餐精简信息列表 | |
| 47 | +export function getTenantPackageList() { | |
| 48 | 48 | return request({ |
| 49 | - url: '/system/tenant-package/export-excel', | |
| 50 | - method: 'get', | |
| 51 | - params: query, | |
| 52 | - responseType: 'blob' | |
| 49 | + url: '/system/tenant-package/get-simple-list', | |
| 50 | + method: 'get' | |
| 53 | 51 | }) |
| 54 | 52 | } | ... | ... |
yudao-ui-admin/src/views/system/tenant/index.vue
| ... | ... | @@ -18,10 +18,6 @@ |
| 18 | 18 | :key="dict.value" :label="dict.label" :value="dict.value"/> |
| 19 | 19 | </el-select> |
| 20 | 20 | </el-form-item> |
| 21 | - <el-form-item label="创建时间"> | |
| 22 | - <el-date-picker v-model="dateRangeCreateTime" size="small" style="width: 240px" value-format="yyyy-MM-dd" | |
| 23 | - type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" /> | |
| 24 | - </el-form-item> | |
| 25 | 21 | <el-form-item> |
| 26 | 22 | <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> |
| 27 | 23 | <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> |
| ... | ... | @@ -45,8 +41,24 @@ |
| 45 | 41 | <el-table v-loading="loading" :data="list"> |
| 46 | 42 | <el-table-column label="租户编号" align="center" prop="id" /> |
| 47 | 43 | <el-table-column label="租户名" align="center" prop="name" /> |
| 44 | + <el-table-column label="租户套餐" align="center" prop="packageId"> | |
| 45 | + <template slot-scope="scope"> | |
| 46 | + <el-tag> {{getPackageName(scope.row.packageId)}} </el-tag> | |
| 47 | + </template> | |
| 48 | + </el-table-column> | |
| 48 | 49 | <el-table-column label="联系人" align="center" prop="contactName" /> |
| 49 | 50 | <el-table-column label="联系手机" align="center" prop="contactMobile" /> |
| 51 | + <el-table-column label="账号额度" align="center" prop="accountCount"> | |
| 52 | + <template slot-scope="scope"> | |
| 53 | + <el-tag> {{scope.row.accountCount}} </el-tag> | |
| 54 | + </template> | |
| 55 | + </el-table-column> | |
| 56 | + <el-table-column label="过期时间" align="center" prop="expireTime" width="180"> | |
| 57 | + <template slot-scope="scope"> | |
| 58 | + <span>{{ parseTime(scope.row.expireTime) }}</span> | |
| 59 | + </template> | |
| 60 | + </el-table-column> | |
| 61 | + <el-table-column label="绑定域名" align="center" prop="domain" width="180" /> | |
| 50 | 62 | <el-table-column label="租户状态" align="center" prop="status"> |
| 51 | 63 | <template slot-scope="scope"> |
| 52 | 64 | <dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status"/> |
| ... | ... | @@ -76,12 +88,33 @@ |
| 76 | 88 | <el-form-item label="租户名" prop="name"> |
| 77 | 89 | <el-input v-model="form.name" placeholder="请输入租户名" /> |
| 78 | 90 | </el-form-item> |
| 91 | + <el-form-item label="租户套餐" prop="packageId"> | |
| 92 | + <el-select v-model="form.packageId" placeholder="请选择租户套餐" clearable size="small"> | |
| 93 | + <el-option v-for="item in packageList" :key="item.id" :label="item.name" :value="item.id"/> | |
| 94 | + </el-select> | |
| 95 | + </el-form-item> | |
| 79 | 96 | <el-form-item label="联系人" prop="contactName"> |
| 80 | 97 | <el-input v-model="form.contactName" placeholder="请输入联系人" /> |
| 81 | 98 | </el-form-item> |
| 82 | 99 | <el-form-item label="联系手机" prop="contactMobile"> |
| 83 | 100 | <el-input v-model="form.contactMobile" placeholder="请输入联系手机" /> |
| 84 | 101 | </el-form-item> |
| 102 | + <el-form-item v-if="form.id === undefined" label="用户名称" prop="username"> | |
| 103 | + <el-input v-model="form.username" placeholder="请输入用户名称" /> | |
| 104 | + </el-form-item> | |
| 105 | + <el-form-item v-if="form.id === undefined" label="用户密码" prop="password"> | |
| 106 | + <el-input v-model="form.password" placeholder="请输入用户密码" type="password" show-password /> | |
| 107 | + </el-form-item> | |
| 108 | + <el-form-item label="账号额度" prop="accountCount"> | |
| 109 | + <el-input-number v-model="form.accountCount" placeholder="请输入账号额度" controls-position="right" :min="0" /> | |
| 110 | + </el-form-item> | |
| 111 | + <el-form-item label="过期时间" prop="expireTime"> | |
| 112 | + <el-date-picker clearable size="small" v-model="form.expireTime" type="date" | |
| 113 | + value-format="timestamp" placeholder="请选择过期时间" /> | |
| 114 | + </el-form-item> | |
| 115 | + <el-form-item label="绑定域名" prop="domain"> | |
| 116 | + <el-input v-model="form.domain" placeholder="请输入绑定域名" /> | |
| 117 | + </el-form-item> | |
| 85 | 118 | <el-form-item label="租户状态" prop="status"> |
| 86 | 119 | <el-radio-group v-model="form.status"> |
| 87 | 120 | <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)" |
| ... | ... | @@ -100,6 +133,7 @@ |
| 100 | 133 | <script> |
| 101 | 134 | import { createTenant, updateTenant, deleteTenant, getTenant, getTenantPage, exportTenantExcel } from "@/api/system/tenant"; |
| 102 | 135 | import { CommonStatusEnum } from '@/utils/constants' |
| 136 | +import {getTenantPackageList} from "@/api/system/tenantPackage"; | |
| 103 | 137 | |
| 104 | 138 | export default { |
| 105 | 139 | name: "Tenant", |
| ... | ... | @@ -117,6 +151,8 @@ export default { |
| 117 | 151 | total: 0, |
| 118 | 152 | // 租户列表 |
| 119 | 153 | list: [], |
| 154 | + // 租户套餐列表 | |
| 155 | + packageList: [], | |
| 120 | 156 | // 弹出层标题 |
| 121 | 157 | title: "", |
| 122 | 158 | // 是否显示弹出层 |
| ... | ... | @@ -136,13 +172,23 @@ export default { |
| 136 | 172 | // 表单校验 |
| 137 | 173 | rules: { |
| 138 | 174 | name: [{ required: true, message: "租户名不能为空", trigger: "blur" }], |
| 175 | + packageId: [{ required: true, message: "租户套餐不能为空", trigger: "blur" }], | |
| 139 | 176 | contactName: [{ required: true, message: "联系人不能为空", trigger: "blur" }], |
| 140 | - status: [{ required: true, message: "租户状态(0正常 1停用)不能为空", trigger: "blur" }], | |
| 177 | + status: [{ required: true, message: "租户状态不能为空", trigger: "blur" }], | |
| 178 | + accountCount: [{ required: true, message: "账号额度不能为空", trigger: "blur" }], | |
| 179 | + expireTime: [{ required: true, message: "过期时间不能为空", trigger: "blur" }], | |
| 180 | + domain: [{ required: true, message: "绑定域名不能为空", trigger: "blur" }], | |
| 181 | + username: [{ required: true, message: "用户名称不能为空", trigger: "blur" }], | |
| 182 | + password: [{ required: true, message: "用户密码不能为空", trigger: "blur" }], | |
| 141 | 183 | } |
| 142 | 184 | }; |
| 143 | 185 | }, |
| 144 | 186 | created() { |
| 145 | 187 | this.getList(); |
| 188 | + // 获得租户套餐列表 | |
| 189 | + getTenantPackageList().then(response => { | |
| 190 | + this.packageList = response.data; | |
| 191 | + }) | |
| 146 | 192 | }, |
| 147 | 193 | methods: { |
| 148 | 194 | /** 查询列表 */ |
| ... | ... | @@ -168,8 +214,12 @@ export default { |
| 168 | 214 | this.form = { |
| 169 | 215 | id: undefined, |
| 170 | 216 | name: undefined, |
| 217 | + packageId: undefined, | |
| 171 | 218 | contactName: undefined, |
| 172 | 219 | contactMobile: undefined, |
| 220 | + accountCount: undefined, | |
| 221 | + expireTime: undefined, | |
| 222 | + domain: undefined, | |
| 173 | 223 | status: CommonStatusEnum.ENABLE, |
| 174 | 224 | }; |
| 175 | 225 | this.resetForm("form"); |
| ... | ... | @@ -249,6 +299,15 @@ export default { |
| 249 | 299 | this.$download.excel(response, '租户.xls'); |
| 250 | 300 | this.exportLoading = false; |
| 251 | 301 | }).catch(() => {}); |
| 302 | + }, | |
| 303 | + /** 套餐名格式化 */ | |
| 304 | + getPackageName(packageId) { | |
| 305 | + for (const item of this.packageList) { | |
| 306 | + if (item.id === packageId) { | |
| 307 | + return item.name; | |
| 308 | + } | |
| 309 | + } | |
| 310 | + return '未知套餐'; | |
| 252 | 311 | } |
| 253 | 312 | } |
| 254 | 313 | }; | ... | ... |
更新日志.md
| ... | ... | @@ -29,7 +29,8 @@ TODO |
| 29 | 29 | * 【新增】后端 `yudao.tenant.enable` 配置项,前端 `VUE_APP_TENANT_ENABLE` 配置项,用于开关租户功能。 [commit](https://gitee.com/zhijiantianya/ruoyi-vue-pro/commit/79311ecc71f0c6beabe0e5f84e1423ce745a5f09) |
| 30 | 30 | * 【优化】调整默认所有表开启多租户的特性,可通过 `yudao.tenant.ignore-tables` 配置项进行忽略,替代原本默认不开启的策略 [commit](https://gitee.com/zhijiantianya/ruoyi-vue-pro/commit/79311ecc71f0c6beabe0e5f84e1423ce745a5f09) |
| 31 | 31 | * 【新增】通过 `yudao.tenant.ignore-urls` 配置忽略多租户的请求,例如说 ,例如说短信回调、支付回调等 Open API [commit](https://gitee.com/zhijiantianya/ruoyi-vue-pro/commit/79311ecc71f0c6beabe0e5f84e1423ce745a5f09) |
| 32 | -* 【新增】租户套餐的管理,可配置每个租户的可使用的功能 | |
| 32 | +* 【新增】租户套餐的管理,可配置每个租户的可使用的功能权限 [commit](https://gitee.com/zhijiantianya/ruoyi-vue-pro/commit/6b6d676a6baa2dad16ae9bf03d5002209064c8cc) | |
| 33 | +* 【优化】新建租户时,自动创建对应的管理员账号、角色等基础信息 []() | |
| 33 | 34 | |
| 34 | 35 | ### 🐞 Bug Fixes |
| 35 | 36 | ... | ... |