diff --git a/sql/dm/用户租户关系表.sql b/sql/dm/用户租户关系表.sql new file mode 100644 index 00000000..a33bb36c --- /dev/null +++ b/sql/dm/用户租户关系表.sql @@ -0,0 +1,32 @@ +-- 用户-租户关系表 +create table "RUOYI-VUE-PRO".SYSTEM_USER_TENANT +( + ID BIGINT not null, + USER_ID BIGINT not null, + TENANT_ID BIGINT not null, + CREATOR VARCHAR(256) default '', + CREATE_TIME TIMESTAMP default CURRENT_TIMESTAMP not null, + UPDATER VARCHAR(256) default '', + UPDATE_TIME TIMESTAMP default CURRENT_TIMESTAMP not null, + DELETED TINYINT default 0 not null +); + +comment on table "RUOYI-VUE-PRO".SYSTEM_USER_TENANT is '用户-租户关系表'; +comment on column "RUOYI-VUE-PRO".SYSTEM_USER_TENANT.ID is '主键ID'; +comment on column "RUOYI-VUE-PRO".SYSTEM_USER_TENANT.USER_ID is '用户ID'; +comment on column "RUOYI-VUE-PRO".SYSTEM_USER_TENANT.TENANT_ID is '租户ID'; +comment on column "RUOYI-VUE-PRO".SYSTEM_USER_TENANT.CREATOR is '创建者'; +comment on column "RUOYI-VUE-PRO".SYSTEM_USER_TENANT.CREATE_TIME is '创建时间'; +comment on column "RUOYI-VUE-PRO".SYSTEM_USER_TENANT.UPDATER is '更新者'; +comment on column "RUOYI-VUE-PRO".SYSTEM_USER_TENANT.UPDATE_TIME is '更新时间'; +comment on column "RUOYI-VUE-PRO".SYSTEM_USER_TENANT.DELETED is '是否删除'; + +create unique index "RUOYI-VUE-PRO".IDX_SYSTEM_USER_TENANT_ID + on "RUOYI-VUE-PRO".SYSTEM_USER_TENANT (ID); + +create unique index "RUOYI-VUE-PRO".IDX_SYSTEM_USER_TENANT_UNIQUE + on "RUOYI-VUE-PRO".SYSTEM_USER_TENANT (USER_ID, TENANT_ID); + +alter table "RUOYI-VUE-PRO".SYSTEM_USER_TENANT + add constraint PK_SYSTEM_USER_TENANT_ID + primary key (ID); \ No newline at end of file diff --git a/sql/dm/组织机构添加是否租户字段.sql b/sql/dm/组织机构添加是否租户字段.sql new file mode 100644 index 00000000..02a7aa7b --- /dev/null +++ b/sql/dm/组织机构添加是否租户字段.sql @@ -0,0 +1,6 @@ +alter table "RUOYI-VUE-PRO".SYSTEM_DEPT + add IS_TENANT TINYINT default false not null; + +comment +on column "RUOYI-VUE-PRO".SYSTEM_DEPT.IS_TENANT is '组织机构是否为租户'; + diff --git a/sql/dm/角色权限剔除表.sql b/sql/dm/角色权限剔除表.sql index d25aa493..8c461a3e 100644 --- a/sql/dm/角色权限剔除表.sql +++ b/sql/dm/角色权限剔除表.sql @@ -30,4 +30,7 @@ create unique index "RUOYI-VUE-PRO".IDX_ROLE_MENU_EXCLUSION_ID alter table "RUOYI-VUE-PRO".SYSTEM_ROLE_MENU_EXCLUSION add constraint PK_ROLE_MENU_EXCLUSION_ID - primary key (ID); \ No newline at end of file + primary key (ID); + +alter table "RUOYI-VUE-PRO".SYSTEM_ROLE + add PARENT_ID BIGINT default 0 not null; diff --git a/yudao-module-infra/yudao-module-infra-server/src/main/resources/application-local.yaml b/yudao-module-infra/yudao-module-infra-server/src/main/resources/application-local.yaml index ef1b047a..d31414ef 100644 --- a/yudao-module-infra/yudao-module-infra-server/src/main/resources/application-local.yaml +++ b/yudao-module-infra/yudao-module-infra-server/src/main/resources/application-local.yaml @@ -82,7 +82,7 @@ spring: # rocketmq 配置项,对应 RocketMQProperties 配置类 rocketmq: - name-server: 127.0.0.1:9876 # RocketMQ Namesrv + name-server: 172.16.46.63:30876 # RocketMQ Namesrv spring: # RabbitMQ 配置项,对应 RabbitProperties 配置类 diff --git a/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/java/dal/do.vm b/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/java/dal/do.vm index baf53f59..6dcf1f1f 100644 --- a/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/java/dal/do.vm +++ b/yudao-module-infra/yudao-module-infra-server/src/main/resources/codegen/java/dal/do.vm @@ -60,7 +60,7 @@ public class ${table.className}DO extends BaseDO { #end */ #if (${column.primaryKey})##处理主键 - @TableId#if (${column.javaType} == 'String')(type = IdType.INPUT)#end + @TableId#if (${column.javaType} == 'String')(type = IdType.INPUT)#else(type = IdType.ASSIGN_ID)#end #end #if ($voType == 20) ## 1. 处理 Swagger 注解 diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java index 376ac3e1..e13bacf4 100644 --- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java +++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/enums/ErrorCodeConstants.java @@ -58,6 +58,8 @@ public interface ErrorCodeConstants { ErrorCode DEPT_PARENT_ERROR = new ErrorCode(1_002_004_004, "不能设置自己为父部门"); ErrorCode DEPT_NOT_ENABLE = new ErrorCode(1_002_004_006, "部门({})不处于开启状态,不允许选择"); ErrorCode DEPT_PARENT_IS_CHILD = new ErrorCode(1_002_004_007, "不能设置自己的子部门为父部门"); + ErrorCode DEPT_TENANT_RELATION_EXISTS = new ErrorCode(1_002_004_008, "当前租户已经关联组织机构"); + // ========== 岗位模块 1-002-005-000 ========== ErrorCode POST_NOT_FOUND = new ErrorCode(1_002_005_000, "当前岗位不存在"); @@ -171,4 +173,6 @@ public interface ErrorCodeConstants { // ========== 站内信发送 1-002-028-000 ========== ErrorCode NOTIFY_SEND_TEMPLATE_PARAM_MISS = new ErrorCode(1_002_028_000, "模板参数({})缺失"); + // ========== 用户-租户关系 ========== + ErrorCode USER_TENANT_NOT_EXISTS = new ErrorCode(1_002_028_000, "用户-租户关系不存在"); } diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java index d9269470..c4e4f103 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/auth/AuthController.java @@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.common.enums.UserTypeEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.security.config.SecurityProperties; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.module.system.controller.admin.auth.vo.*; import cn.iocoder.yudao.module.system.convert.auth.AuthConvert; import cn.iocoder.yudao.module.system.dal.dataobject.permission.MenuDO; @@ -65,6 +66,7 @@ public class AuthController { @PostMapping("/login") @PermitAll @Operation(summary = "使用账号密码登录") + @TenantIgnore public CommonResult login(@RequestBody @Valid AuthLoginReqVO reqVO) { return success(authService.login(reqVO)); } diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/DeptController.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/DeptController.java index 7873d00f..2f43da82 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/DeptController.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/DeptController.java @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.system.controller.admin.dept; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO; import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptRespVO; import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptSaveReqVO; @@ -12,16 +13,19 @@ import cn.iocoder.yudao.module.system.service.dept.DeptService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; import java.util.List; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +/** + * @author chenbowen + */ @Tag(name = "管理后台 - 部门") @RestController @RequestMapping("/system/dept") @@ -42,6 +46,7 @@ public class DeptController { @PutMapping("update") @Operation(summary = "更新部门") @PreAuthorize("@ss.hasPermission('system:dept:update')") + @TenantIgnore public CommonResult updateDept(@Valid @RequestBody DeptSaveReqVO updateReqVO) { deptService.updateDept(updateReqVO); return success(true); diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptRespVO.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptRespVO.java index 83e6ed08..4a69f902 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptRespVO.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptRespVO.java @@ -5,6 +5,9 @@ import lombok.Data; import java.time.LocalDateTime; +/** + * @author chenbowen + */ @Schema(description = "管理后台 - 部门信息 Response VO") @Data public class DeptRespVO { @@ -36,4 +39,10 @@ public class DeptRespVO { @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式") private LocalDateTime createTime; + @Schema(description = "组织机构是否租户", example = "true") + private Boolean isTenant = false; + + @Schema(description = "租户编号", example = "1024") + private Long tenantId; + } diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptSaveReqVO.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptSaveReqVO.java index aa279545..3618adc5 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptSaveReqVO.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptSaveReqVO.java @@ -9,6 +9,9 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; import lombok.Data; +/** + * @author chenbowen + */ @Schema(description = "管理后台 - 部门创建/修改 Request VO") @Data public class DeptSaveReqVO { @@ -45,4 +48,10 @@ public class DeptSaveReqVO { @InEnum(value = CommonStatusEnum.class, message = "修改状态必须是 {value}") private Integer status; + @Schema(description = "组织机构是否租户", example = "true") + private Boolean isTenant = false; + + @Schema(description = "租户编号", example = "1024") + private Long tenantId; + } diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptSimpleRespVO.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptSimpleRespVO.java index 31c15d56..f875470f 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptSimpleRespVO.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/dept/vo/dept/DeptSimpleRespVO.java @@ -5,6 +5,9 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +/** + * @author chenbowen + */ @Schema(description = "管理后台 - 部门精简信息 Response VO") @Data @NoArgsConstructor diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleSaveReqVO.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleSaveReqVO.java index 1aa0658d..50771a41 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleSaveReqVO.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/vo/role/RoleSaveReqVO.java @@ -43,7 +43,7 @@ public class RoleSaveReqVO { private Integer status; @Schema(description = "角色类型", example = "1 内置角色,2 标准角色,3 租户自定义角色") - private String type; + private Integer type; @Schema(description = "父级角色Id", example = "-1 系统角色,0 顶级角色") private Long parentId; diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantRespVO.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantRespVO.java index 5a444b52..6a40780e 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantRespVO.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/tenant/vo/tenant/TenantRespVO.java @@ -10,6 +10,9 @@ import lombok.Data; import java.time.LocalDateTime; +/** + * @author chenbowen + */ @Schema(description = "管理后台 - 租户 Response VO") @Data @ExcelIgnoreUnannotated diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/usertenant/UserTenantController.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/usertenant/UserTenantController.java new file mode 100644 index 00000000..156da04d --- /dev/null +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/usertenant/UserTenantController.java @@ -0,0 +1,149 @@ +package cn.iocoder.yudao.module.system.controller.admin.usertenant; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; +import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; +import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder; +import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantRespVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSimpleRespVO; +import cn.iocoder.yudao.module.system.controller.admin.usertenant.vo.UserTenantPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.usertenant.vo.UserTenantRespVO; +import cn.iocoder.yudao.module.system.controller.admin.usertenant.vo.UserTenantSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.usertenant.UserTenantDO; +import cn.iocoder.yudao.module.system.service.usertenant.UserTenantService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.io.IOException; +import java.util.List; + +import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +/** + * @author chenbowen + */ +@Tag(name = "管理后台 - 用户-租户关系") +@RestController +@RequestMapping("/system/user-tenant") +@Validated +public class UserTenantController { + + @Resource + private UserTenantService userTenantService; + + @PostMapping("/create") + @Operation(summary = "创建用户-租户关系") + @PreAuthorize("@ss.hasPermission('system:user-tenant:create')") + public CommonResult createUserTenant(@Valid @RequestBody UserTenantSaveReqVO createReqVO) { + return success(userTenantService.createUserTenant(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新用户-租户关系") + @PreAuthorize("@ss.hasPermission('system:user-tenant:update')") + public CommonResult updateUserTenant(@Valid @RequestBody UserTenantSaveReqVO updateReqVO) { + userTenantService.updateUserTenant(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除用户-租户关系") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('system:user-tenant:delete')") + public CommonResult deleteUserTenant(@RequestParam("id") Long id) { + userTenantService.deleteUserTenant(id); + return success(true); + } + + @DeleteMapping("/delete-list") + @Parameter(name = "ids", description = "编号", required = true) + @Operation(summary = "批量删除用户-租户关系") + @PreAuthorize("@ss.hasPermission('system:user-tenant:delete')") + public CommonResult deleteUserTenantList(@RequestParam("ids") List ids) { + userTenantService.deleteUserTenantListByIds(ids); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得用户-租户关系") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('system:user-tenant:query')") + public CommonResult getUserTenant(@RequestParam("id") Long id) { + UserTenantDO userTenant = userTenantService.getUserTenant(id); + return success(BeanUtils.toBean(userTenant, UserTenantRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得用户-租户关系分页") + @PreAuthorize("@ss.hasPermission('system:user-tenant:query')") + public CommonResult> getUserTenantPage(@Valid UserTenantPageReqVO pageReqVO) { + PageResult pageResult = userTenantService.getUserTenantPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, UserTenantRespVO.class)); + } + + /** + * 用户租户关系 simpleList 查询接口 + */ + @GetMapping("/user-tenant-simple-list") + @Operation(summary = "获得用户-租户关系 simpleList 列表") + public CommonResult> getUserTenantSimpleList(@Valid UserTenantPageReqVO pageReqVO) { + List list = userTenantService.getUserTenantList(pageReqVO); + // 追加当前用户归属的租户信息 + if (CollUtil.isNotEmpty(list)) { + list.add(new UserTenantDO() + .setUserId(getLoginUserId()) + .setTenantId(TenantContextHolder.getTenantId())); + } + return success(BeanUtils.toBean(list, UserTenantRespVO.class)); + } + + @GetMapping("/export-excel") + @Operation(summary = "导出用户-租户关系 Excel") + @PreAuthorize("@ss.hasPermission('system:user-tenant:export')") + @ApiAccessLog(operateType = EXPORT) + public void exportUserTenantExcel(@Valid UserTenantPageReqVO pageReqVO, + HttpServletResponse response) throws IOException { + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); + List list = userTenantService.getUserTenantPage(pageReqVO).getList(); + // 导出 Excel + ExcelUtils.write(response, "用户-租户关系.xls", "数据", UserTenantRespVO.class, + BeanUtils.toBean(list, UserTenantRespVO.class)); + } + + /** + * 用户 simpleList 查询接口 + */ + @GetMapping("/user-simple-list") + @Operation(summary = "获得用户 simpleList 列表") + // 不使用默认的租户查询方式,此处需要获取租户以及下属租户的相关数据,在 mapper 自行实现 + @TenantIgnore + public CommonResult> getUserSimpleList() { + return success(userTenantService.getUserBelongSimpleList()); + } + + /** + * 租户 simpleList 查询接口 + */ + @GetMapping("/tenant-simple-list") + @Operation(summary = "获得租户 simpleList 列表") + // 不使用默认的租户查询方式,此处需要获取租户以及下属租户的相关数据,在 mapper 自行实现 + @TenantIgnore + public CommonResult> getTenantSimpleList() { + return success(userTenantService.getTenantBelongSimpleList()); + } + +} \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/usertenant/vo/UserTenantPageReqVO.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/usertenant/vo/UserTenantPageReqVO.java new file mode 100644 index 00000000..3e3387ae --- /dev/null +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/usertenant/vo/UserTenantPageReqVO.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.system.controller.admin.usertenant.vo; + +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 用户-租户关系分页 Request VO") +@Data +public class UserTenantPageReqVO extends PageParam { + + @Schema(description = "用户ID", example = "32426") + private Long userId; + + @Schema(description = "租户ID", example = "30027") + private Long tenantId; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/usertenant/vo/UserTenantRespVO.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/usertenant/vo/UserTenantRespVO.java new file mode 100644 index 00000000..22cbd202 --- /dev/null +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/usertenant/vo/UserTenantRespVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.system.controller.admin.usertenant.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 用户-租户关系 Response VO") +@Data +@ExcelIgnoreUnannotated +public class UserTenantRespVO { + + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "26967") + @ExcelProperty("主键ID") + private Long id; + + @Schema(description = "用户ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "32426") + @ExcelProperty("用户ID") + private Long userId; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + @ExcelProperty("创建时间") + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/usertenant/vo/UserTenantSaveReqVO.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/usertenant/vo/UserTenantSaveReqVO.java new file mode 100644 index 00000000..dfe9b77f --- /dev/null +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/controller/admin/usertenant/vo/UserTenantSaveReqVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.system.controller.admin.usertenant.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - 用户-租户关系新增/修改 Request VO") +@Data +public class UserTenantSaveReqVO { + + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "26967") + private Long id; + + @Schema(description = "用户ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "32426") + @NotNull(message = "用户ID不能为空") + private Long userId; + + @Schema(description = "租户ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "30027") + @NotNull(message = "租户ID不能为空") + private Long tenantId; + +} \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/DeptDO.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/DeptDO.java index 67f32bda..9c38cc47 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/DeptDO.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/dept/DeptDO.java @@ -13,8 +13,7 @@ import lombok.EqualsAndHashCode; /** * 部门表 * - * @author ruoyi - * @author 芋道源码 + * @author 管理员 */ @TableName("system_dept") @KeySequence("system_dept_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 @@ -63,5 +62,10 @@ public class DeptDO extends TenantBaseDO { * 枚举 {@link CommonStatusEnum} */ private Integer status; + /** + * 组织机构是否为租户 + */ + private Boolean isTenant; -} + +} \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/user/AdminUserDO.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/user/AdminUserDO.java index 6f3147dc..eb4d2e22 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/user/AdminUserDO.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/user/AdminUserDO.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.system.dal.dataobject.user; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.tenant.core.db.TenantBaseDO; +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.module.system.enums.common.SexEnum; import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; @@ -23,7 +23,7 @@ import java.util.Set; @Builder @NoArgsConstructor @AllArgsConstructor -public class AdminUserDO extends TenantBaseDO { +public class AdminUserDO extends BaseDO { /** * 用户ID diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/usertenant/UserTenantDO.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/usertenant/UserTenantDO.java new file mode 100644 index 00000000..c7357e84 --- /dev/null +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/dal/dataobject/usertenant/UserTenantDO.java @@ -0,0 +1,39 @@ +package cn.iocoder.yudao.module.system.dal.dataobject.usertenant; + +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * 用户-租户关系 DO + * + * @author 管理员 + */ +@TableName("system_user_tenant") +@KeySequence("system_user_tenant_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class UserTenantDO extends BaseDO { + + /** + * 主键ID + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + /** + * 用户ID + */ + private Long userId; + /** + * 多租户编号 + */ + private Long tenantId; + +} \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/DeptMapper.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/DeptMapper.java index a09fcf7d..6df7b0f4 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/DeptMapper.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/dept/DeptMapper.java @@ -4,11 +4,15 @@ import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptListReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; +import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import org.apache.ibatis.annotations.Mapper; import java.util.Collection; import java.util.List; +/** + * @author chenbowen + */ @Mapper public interface DeptMapper extends BaseMapperX { @@ -34,4 +38,26 @@ public interface DeptMapper extends BaseMapperX { return selectList(DeptDO::getLeaderUserId, id); } + default DeptDO selectByTenantIdAndIsTenant(Long tenantId) { + return selectOne(new LambdaQueryWrapperX() + .eq(DeptDO::getTenantId, tenantId) + .eq(DeptDO::getIsTenant, true)); + } + + default DeptDO selectByTenantIdAndIsTenantExcludeId(Long tenantId, Long excludeId) { + return selectOne(new LambdaQueryWrapperX() + .eq(DeptDO::getTenantId, tenantId) + .eq(DeptDO::getIsTenant, true) + .ne(DeptDO::getId, excludeId)); + } + + /** + * 批量更新指定部门ID列表的租户ID + */ + default void updateTenantIdBatch(List ids,Long tenantId){ + UpdateWrapper wrapper = new UpdateWrapper<>(); + wrapper.in("id", ids).set("tenant_id", tenantId); + update(null, wrapper); + }; + } diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java index c0c9be8e..006a2f2b 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/user/AdminUserMapper.java @@ -10,6 +10,9 @@ import org.apache.ibatis.annotations.Mapper; import java.util.Collection; import java.util.List; +/** + * @author chenbowen + */ @Mapper public interface AdminUserMapper extends BaseMapperX { diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/usertenant/UserTenantMapper.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/usertenant/UserTenantMapper.java new file mode 100644 index 00000000..f0fb5b3c --- /dev/null +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/usertenant/UserTenantMapper.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.system.dal.mysql.usertenant; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.system.controller.admin.usertenant.vo.UserTenantPageReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.usertenant.UserTenantDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 用户-租户关系 Mapper + * + * @author 管理员 + */ +@Mapper +public interface UserTenantMapper extends BaseMapperX { + + default PageResult selectPage(UserTenantPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(UserTenantDO::getUserId, reqVO.getUserId()) + .eqIfPresent(UserTenantDO::getTenantId, reqVO.getTenantId()) + .betweenIfPresent(UserTenantDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(UserTenantDO::getId)); + } + +} \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java index 89d1f64e..f19a4690 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/dept/DeptServiceImpl.java @@ -49,9 +49,20 @@ public class DeptServiceImpl implements DeptService { // 校验部门名的唯一性 validateDeptNameUnique(null, createReqVO.getParentId(), createReqVO.getName()); - // 插入部门 + // 新建为主体公司时,校验租户唯一性(一个租户只能关联一个组织机构) + if (Boolean.TRUE.equals(createReqVO.getIsTenant())) { + DeptDO exist = deptMapper.selectByTenantIdAndIsTenant(createReqVO.getTenantId()); + if (exist != null) { + throw exception(DEPT_TENANT_RELATION_EXISTS); + } + } + DeptDO dept = BeanUtils.toBean(createReqVO, DeptDO.class); + if (Boolean.TRUE.equals(createReqVO.getIsTenant())) { + dept.setTenantId(createReqVO.getTenantId()); + } deptMapper.insert(dept); + return dept.getId(); } @@ -69,7 +80,33 @@ public class DeptServiceImpl implements DeptService { // 校验部门名的唯一性 validateDeptNameUnique(updateReqVO.getId(), updateReqVO.getParentId(), updateReqVO.getName()); - // 更新部门 + DeptDO oldDept = deptMapper.selectById(updateReqVO.getId()); + boolean oldIsTenant = oldDept.getIsTenant(); + boolean newIsTenant = updateReqVO.getIsTenant(); + + if (newIsTenant) { + // 1. 校验租户唯一性 + DeptDO exist = deptMapper.selectByTenantIdAndIsTenantExcludeId(updateReqVO.getTenantId(), updateReqVO.getId()); + if (exist != null) { + throw exception(DEPT_TENANT_RELATION_EXISTS); + } + // 2. 设置当前组织租户Id + oldDept.setTenantId(updateReqVO.getTenantId()); + // 3. 递归设置所有下属非主体公司组织的租户Id为当前租户Id + updateChildrenTenantId(updateReqVO.getId(), updateReqVO.getTenantId()); + } else if (oldIsTenant) { + // 获取父级组织机构的租户Id + Long parentTenantId = null; + if (oldDept.getParentId() != null && !DeptDO.PARENT_ID_ROOT.equals(oldDept.getParentId())) { + DeptDO parentDept = deptMapper.selectById(oldDept.getParentId()); + if (parentDept != null) { + parentTenantId = parentDept.getTenantId(); + } + } + // 递归设置所有下属非主体公司组织的租户Id为父级组织机构的租户Id + updateChildrenTenantId(updateReqVO.getId(), parentTenantId); + } + DeptDO updateObj = BeanUtils.toBean(updateReqVO, DeptDO.class); deptMapper.updateById(updateObj); } @@ -84,7 +121,8 @@ public class DeptServiceImpl implements DeptService { if (deptMapper.selectCountByParentId(id) > 0) { throw exception(DEPT_EXITS_CHILDREN); } - // 删除部门 + DeptDO dept = deptMapper.selectById(id); + // 删除为主体公司时无需处理关系表,仅常规删除 deptMapper.deleteById(id); } @@ -220,4 +258,32 @@ public class DeptServiceImpl implements DeptService { }); } + /** + * 递归设置指定组织及其所有下属非主体公司组织的租户Id,批量更新。 + * 跳过所有租户组织及其下属组织(不包含自身)。 + */ + private void updateChildrenTenantId(Long parentId, Long tenantId) { + List updateIds = new ArrayList<>(); + Queue queue = new LinkedList<>(); + List children = deptMapper.selectListByParentId(Collections.singleton(parentId)); + if (children != null && !children.isEmpty()) { + queue.addAll(children); + } + while (!queue.isEmpty()) { + DeptDO current = queue.poll(); + if (current.getIsTenant() != null && current.getIsTenant()) { + // 跳过当前租户组织及其所有下属 + continue; + } + updateIds.add(current.getId()); + List subChildren = deptMapper.selectListByParentId(Collections.singleton(current.getId())); + if (subChildren != null && !subChildren.isEmpty()) { + queue.addAll(subChildren); + } + } + if (!updateIds.isEmpty()) { + deptMapper.updateTenantIdBatch(updateIds, tenantId); + } + } + } diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java index 955b7ea9..92a468ae 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java @@ -99,8 +99,8 @@ public class AdminUserServiceImpl implements AdminUserService { createReqVO.getMobile(), createReqVO.getEmail(), createReqVO.getDeptId(), createReqVO.getPostIds()); // 2.1 插入用户 AdminUserDO user = BeanUtils.toBean(createReqVO, AdminUserDO.class); - user.setStatus(CommonStatusEnum.ENABLE.getStatus()); // 默认开启 - user.setPassword(encodePassword(createReqVO.getPassword())); // 加密密码 + user.setStatus(CommonStatusEnum.ENABLE.getStatus()); + user.setPassword(encodePassword(createReqVO.getPassword())); userMapper.insert(user); // 2.2 插入关联岗位 if (CollectionUtil.isNotEmpty(user.getPostIds())) { diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/usertenant/UserTenantService.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/usertenant/UserTenantService.java new file mode 100644 index 00000000..8928c16a --- /dev/null +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/usertenant/UserTenantService.java @@ -0,0 +1,85 @@ +package cn.iocoder.yudao.module.system.service.usertenant; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantRespVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSimpleRespVO; +import cn.iocoder.yudao.module.system.controller.admin.usertenant.vo.UserTenantPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.usertenant.vo.UserTenantSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.usertenant.UserTenantDO; +import jakarta.validation.Valid; + +import java.util.List; + +/** + * 用户-租户关系 Service 接口 + * + * @author 管理员 + */ +public interface UserTenantService { + + /** + * 创建用户-租户关系 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createUserTenant(@Valid UserTenantSaveReqVO createReqVO); + + /** + * 更新用户-租户关系 + * + * @param updateReqVO 更新信息 + */ + void updateUserTenant(@Valid UserTenantSaveReqVO updateReqVO); + + /** + * 删除用户-租户关系 + * + * @param id 编号 + */ + void deleteUserTenant(Long id); + + /** + * 批量删除用户-租户关系 + * + * @param ids 编号 + */ + void deleteUserTenantListByIds(List ids); + + /** + * 获得用户-租户关系 + * + * @param id 编号 + * @return 用户-租户关系 + */ + UserTenantDO getUserTenant(Long id); + + /** + * 获得用户-租户关系分页 + * + * @param pageReqVO 分页查询 + * @return 用户-租户关系分页 + */ + PageResult getUserTenantPage(UserTenantPageReqVO pageReqVO); + + /** + * 获得用户-租户关系列表 + * + * @param pageReqVO 列表查询 + * @return 用户-租户关系列表 + */ + List getUserTenantList(UserTenantPageReqVO pageReqVO); + + /** + * 获得用户 simpleList 列表 !!!注意,此方法查询的用户列表,包含了租户下属租户的所有用户 + * @return 用户列表(只含 id、nickname) + */ + List getUserBelongSimpleList(); + + /** + * 获得租户 simpleList 列表 + * @return 租户列表(只含 id、name) + */ + List getTenantBelongSimpleList(); + +} \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/usertenant/UserTenantServiceImpl.java b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/usertenant/UserTenantServiceImpl.java new file mode 100644 index 00000000..fdd98a06 --- /dev/null +++ b/yudao-module-system/yudao-module-system-server/src/main/java/cn/iocoder/yudao/module/system/service/usertenant/UserTenantServiceImpl.java @@ -0,0 +1,202 @@ +package cn.iocoder.yudao.module.system.service.usertenant; + +import cn.hutool.core.collection.CollUtil; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.framework.security.core.LoginUser; +import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantRespVO; +import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSimpleRespVO; +import cn.iocoder.yudao.module.system.controller.admin.usertenant.vo.UserTenantPageReqVO; +import cn.iocoder.yudao.module.system.controller.admin.usertenant.vo.UserTenantSaveReqVO; +import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; +import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO; +import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; +import cn.iocoder.yudao.module.system.dal.dataobject.usertenant.UserTenantDO; +import cn.iocoder.yudao.module.system.dal.mysql.dept.DeptMapper; +import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantMapper; +import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper; +import cn.iocoder.yudao.module.system.dal.mysql.usertenant.UserTenantMapper; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.*; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUser; +import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; + +/** + * 用户-租户关系 Service 实现类 + * + * @author 管理员 + */ +@Service +@Validated +public class UserTenantServiceImpl implements UserTenantService { + + @Resource + private UserTenantMapper userTenantMapper; + @Resource + private AdminUserMapper adminUserMapper; + @Resource + private TenantMapper tenantMapper; + @Resource + private DeptMapper deptMapper; + + @Override + public Long createUserTenant(UserTenantSaveReqVO createReqVO) { + // 插入 + UserTenantDO userTenant = BeanUtils.toBean(createReqVO, UserTenantDO.class); + userTenantMapper.insert(userTenant); + // 返回 + return userTenant.getId(); + } + + @Override + public void updateUserTenant(UserTenantSaveReqVO updateReqVO) { + // 校验存在 + validateUserTenantExists(updateReqVO.getId()); + // 更新 + UserTenantDO updateObj = BeanUtils.toBean(updateReqVO, UserTenantDO.class); + userTenantMapper.updateById(updateObj); + } + + @Override + public void deleteUserTenant(Long id) { + // 校验存在 + validateUserTenantExists(id); + // 删除 + userTenantMapper.deleteById(id); + } + + @Override + public void deleteUserTenantListByIds(List ids) { + // 校验存在 + validateUserTenantExists(ids); + // 删除 + userTenantMapper.deleteByIds(ids); + } + + private void validateUserTenantExists(List ids) { + List list = userTenantMapper.selectByIds(ids); + if (CollUtil.isEmpty(list) || list.size() != ids.size()) { + throw exception(USER_TENANT_NOT_EXISTS); + } + } + + private void validateUserTenantExists(Long id) { + if (userTenantMapper.selectById(id) == null) { + throw exception(USER_TENANT_NOT_EXISTS); + } + } + + @Override + public UserTenantDO getUserTenant(Long id) { + return userTenantMapper.selectById(id); + } + + @Override + public PageResult getUserTenantPage(UserTenantPageReqVO pageReqVO) { + return userTenantMapper.selectPage(pageReqVO); + } + + @Override + public List getUserTenantList(UserTenantPageReqVO pageReqVO) { + return userTenantMapper.selectList(new LambdaQueryWrapper() + .eq(pageReqVO.getUserId() != null, UserTenantDO::getUserId, pageReqVO.getUserId()) + .eq(pageReqVO.getTenantId() != null, UserTenantDO::getTenantId, pageReqVO.getTenantId())); + } + + /** + * 根据当前租户ID获取租户部门 + */ + private DeptDO getTenantDeptOrThrow() { + LoginUser loginUser = getLoginUser(); + if (loginUser == null) { + throw exception(USER_NOT_EXISTS); + } + Long tenantId = loginUser.getTenantId(); + if (tenantId == null) { + throw exception(TENANT_NOT_EXISTS); + } + DeptDO deptDO = deptMapper.selectByTenantIdAndIsTenant(tenantId); + if (deptDO == null) { + throw exception(TENANT_NOT_EXISTS); + } + return deptDO; + } + + /** + * 获取当前租户组织及其所有下属组织ID(包含自身) + */ + private List getTenantAndSubDeptIds(Long tenantDeptId) { + List deptIds = new ArrayList<>(); + deptIds.add(tenantDeptId); + List children = getAllSubDepts(tenantDeptId); + for (DeptDO child : children) { + deptIds.add(child.getId()); + } + return deptIds; + } + + /** + * 查询当前租户组织机构及其所有下属组织下的所有启用用户 + */ + @Override + public List getUserBelongSimpleList() { + DeptDO tenantDept = getTenantDeptOrThrow(); + List deptIds = getTenantAndSubDeptIds(tenantDept.getId()); + // 查询这些组织下的所有用户 + List userList = adminUserMapper.selectListByDeptIds(deptIds); + List result = new ArrayList<>(); + for (AdminUserDO user : userList) { + if (CommonStatusEnum.ENABLE.getStatus().equals(user.getStatus())) { + UserSimpleRespVO vo = new UserSimpleRespVO(); + vo.setId(user.getId()); + vo.setNickname(user.getNickname()); +// vo.setUsername(user.getUsername()); + result.add(vo); + } + } + return result; + } + + /** + * 查询所有启用状态的组织机构(不限制 isTenant),支持多层嵌套(中间可有非租户组织)。 + * 返回所有启用状态的组织及其 parentId,便于前端递归组装树状结构。 + */ + @Override + public List getTenantBelongSimpleList() { + DeptDO tenantDept = getTenantDeptOrThrow(); + List children = getAllSubDepts(tenantDept.getId()); + Set tenantIdsSets = convertSet(children, DeptDO::getTenantId, DeptDO::getIsTenant); + // 添加自身的租户ID + tenantIdsSets.add(tenantDept.getTenantId()); + List tenants = tenantMapper.selectByIds(tenantIdsSets); + return BeanUtils.toBean(tenants, TenantRespVO.class); + } + + /** + * 递归获取所有下属部门(不含自身) + */ + private List getAllSubDepts(Long parentId) { + List result = new ArrayList<>(); + Queue queue = new LinkedList<>(); + queue.add(parentId); + while (!queue.isEmpty()) { + Long pid = queue.poll(); + List children = deptMapper.selectListByParentId(Collections.singleton(pid)); + if (CollUtil.isNotEmpty(children)) { + result.addAll(children); + for (DeptDO child : children) { + queue.add(child.getId()); + } + } + } + return result; + } +} \ No newline at end of file diff --git a/yudao-module-system/yudao-module-system-server/src/main/resources/application-local.yaml b/yudao-module-system/yudao-module-system-server/src/main/resources/application-local.yaml index 9e482811..607d2348 100644 --- a/yudao-module-system/yudao-module-system-server/src/main/resources/application-local.yaml +++ b/yudao-module-system/yudao-module-system-server/src/main/resources/application-local.yaml @@ -77,7 +77,7 @@ spring: # rocketmq 配置项,对应 RocketMQProperties 配置类 rocketmq: - name-server: 127.0.0.1:9876 # RocketMQ Namesrv + name-server: 172.16.46.63:30876 # RocketMQ Namesrv spring: # RabbitMQ 配置项,对应 RabbitProperties 配置类 diff --git a/yudao-module-system/yudao-module-system-server/src/test/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceTest.java b/yudao-module-system/yudao-module-system-server/src/test/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceTest.java index 91804449..3511034f 100644 --- a/yudao-module-system/yudao-module-system-server/src/test/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceTest.java +++ b/yudao-module-system/yudao-module-system-server/src/test/java/cn/iocoder/yudao/module/system/service/permission/PermissionServiceTest.java @@ -1,43 +1,28 @@ package cn.iocoder.yudao.module.system.service.permission; -import cn.hutool.core.collection.CollUtil; -import cn.hutool.extra.spring.SpringUtil; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; -import cn.iocoder.yudao.framework.common.biz.system.permission.dto.DeptDataPermissionRespDTO; -import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; -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.RoleMenuDO; import cn.iocoder.yudao.module.system.dal.dataobject.permission.UserRoleDO; -import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMenuMapper; import cn.iocoder.yudao.module.system.dal.mysql.permission.UserRoleMapper; -import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; import cn.iocoder.yudao.module.system.service.dept.DeptService; import cn.iocoder.yudao.module.system.service.user.AdminUserService; +import jakarta.annotation.Resource; import org.junit.jupiter.api.Test; -import org.mockito.MockedStatic; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; -import jakarta.annotation.Resource; import java.util.Collection; import java.util.List; import java.util.Set; -import static cn.hutool.core.collection.ListUtil.toList; import static cn.iocoder.yudao.framework.common.util.collection.SetUtils.asSet; import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo; -import static java.util.Collections.singleton; -import static java.util.Collections.singletonList; -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; +import static org.junit.jupiter.api.Assertions.assertEquals; -@Import({PermissionServiceImpl.class}) +@Import({PermissionServiceImpl.class, RoleServiceImpl.class}) public class PermissionServiceTest extends BaseDbUnitTest { @Resource @@ -48,8 +33,8 @@ public class PermissionServiceTest extends BaseDbUnitTest { @Resource private UserRoleMapper userRoleMapper; - @MockBean - private RoleService roleService; + @Resource + private RoleServiceImpl roleService; @MockBean private MenuService menuService; @MockBean @@ -57,73 +42,6 @@ public class PermissionServiceTest extends BaseDbUnitTest { @MockBean private AdminUserService userService; - @Test - public void testHasAnyPermissions_superAdmin() { - try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { - springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) - .thenReturn(permissionService); - - // 准备参数 - Long userId = 1L; - String[] roles = new String[]{"system:user:query", "system:user:create"}; - // mock 用户登录的角色 - userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L)); - RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleListFromCache(eq(singleton(100L)))).thenReturn(toList(role)); - // mock 其它方法 - when(roleService.hasAnySuperAdmin(eq(asSet(100L)))).thenReturn(true); - - // 调用,并断言 - assertTrue(permissionService.hasAnyPermissions(userId, roles)); - } - } - - @Test - public void testHasAnyPermissions_normal() { - try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { - springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) - .thenReturn(permissionService); - - // 准备参数 - Long userId = 1L; - String[] roles = new String[]{"system:user:query", "system:user:create"}; - // mock 用户登录的角色 - userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L)); - RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleListFromCache(eq(singleton(100L)))).thenReturn(toList(role)); - // mock 菜单 - Long menuId = 1000L; - when(menuService.getMenuIdListByPermissionFromCache( - eq("system:user:create"))).thenReturn(singletonList(menuId)); - roleMenuMapper.insert(randomPojo(RoleMenuDO.class).setRoleId(100L).setMenuId(1000L)); - - // 调用,并断言 - assertTrue(permissionService.hasAnyPermissions(userId, roles)); - } - } - - @Test - public void testHasAnyRoles() { - try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { - springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) - .thenReturn(permissionService); - - // 准备参数 - Long userId = 1L; - String[] roles = new String[]{"yunai", "tudou"}; - // mock 用户与角色的缓存 - userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L)); - RoleDO role = randomPojo(RoleDO.class, o -> o.setId(100L).setCode("tudou") - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleListFromCache(eq(singleton(100L)))).thenReturn(toList(role)); - - // 调用,并断言 - assertTrue(permissionService.hasAnyRoles(userId, roles)); - } - } - // ========== 角色-菜单的相关方法 ========== @Test @@ -133,8 +51,10 @@ public class PermissionServiceTest extends BaseDbUnitTest { Set menuIds = asSet(200L, 300L); // mock 数据 RoleMenuDO roleMenu01 = randomPojo(RoleMenuDO.class).setRoleId(1L).setMenuId(100L); + roleMenu01.setTenantId(0L); roleMenuMapper.insert(roleMenu01); RoleMenuDO roleMenu02 = randomPojo(RoleMenuDO.class).setRoleId(1L).setMenuId(200L); + roleMenu02.setTenantId(0L); roleMenuMapper.insert(roleMenu02); // 调用 @@ -193,21 +113,6 @@ public class PermissionServiceTest extends BaseDbUnitTest { assertPojoEquals(dbRoleMenus.get(0), roleMenuDO02); } - @Test - public void testGetRoleMenuIds_superAdmin() { - // 准备参数 - Long roleId = 100L; - // mock 方法 - when(roleService.hasAnySuperAdmin(eq(singleton(100L)))).thenReturn(true); - List menuList = singletonList(randomPojo(MenuDO.class).setId(1L)); - when(menuService.getMenuList()).thenReturn(menuList); - - // 调用 - Set menuIds = permissionService.getRoleMenuListByRoleId(roleId); - // 断言 - assertEquals(singleton(1L), menuIds); - } - @Test public void testGetRoleMenuIds_normal() { // 准备参数 @@ -346,182 +251,8 @@ public class PermissionServiceTest extends BaseDbUnitTest { assertEquals(asSet(1L, 2L), result); } - @Test - public void testGetEnableUserRoleListByUserIdFromCache() { - try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { - springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) - .thenReturn(permissionService); - - // 准备参数 - Long userId = 1L; - // mock 用户登录的角色 - userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(100L)); - userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(200L)); - RoleDO role01 = randomPojo(RoleDO.class, o -> o.setId(100L) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - RoleDO role02 = randomPojo(RoleDO.class, o -> o.setId(200L) - .setStatus(CommonStatusEnum.DISABLE.getStatus())); - when(roleService.getRoleListFromCache(eq(asSet(100L, 200L)))) - .thenReturn(toList(role01, role02)); - - // 调用 - List result = permissionService.getEnableUserRoleListByUserIdFromCache(userId); - // 断言 - assertEquals(1, result.size()); - assertPojoEquals(role01, result.get(0)); - } - } // ========== 用户-部门的相关方法 ========== - @Test - public void testAssignRoleDataScope() { - // 准备参数 - Long roleId = 1L; - Integer dataScope = 2; - Set dataScopeDeptIds = asSet(10L, 20L); - - // 调用 - permissionService.assignRoleDataScope(roleId, dataScope, dataScopeDeptIds); - // 断言 - verify(roleService).updateRoleDataScope(eq(roleId), eq(dataScope), eq(dataScopeDeptIds)); - } - - @Test - public void testGetDeptDataPermission_All() { - try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { - springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) - .thenReturn(permissionService); - - // 准备参数 - Long userId = 1L; - // mock 用户的角色编号 - userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); - // mock 获得用户的角色 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.ALL.getScope()) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); - - // 调用 - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); - // 断言 - assertTrue(result.getAll()); - assertFalse(result.getSelf()); - assertTrue(CollUtil.isEmpty(result.getDeptIds())); - } - } - - @Test - public void testGetDeptDataPermission_DeptCustom() { - try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { - springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) - .thenReturn(permissionService); - - // 准备参数 - Long userId = 1L; - // mock 用户的角色编号 - userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); - // mock 获得用户的角色 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_CUSTOM.getScope()) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); - // mock 部门的返回 - when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), - null, null); // 最后返回 null 的目的,看看会不会重复调用 - - // 调用 - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); - // 断言 - assertFalse(result.getAll()); - assertFalse(result.getSelf()); - assertEquals(roleDO.getDataScopeDeptIds().size() + 1, result.getDeptIds().size()); - assertTrue(CollUtil.containsAll(result.getDeptIds(), roleDO.getDataScopeDeptIds())); - assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); - } - } - - @Test - public void testGetDeptDataPermission_DeptOnly() { - try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { - springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) - .thenReturn(permissionService); - - // 准备参数 - Long userId = 1L; - // mock 用户的角色编号 - userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); - // mock 获得用户的角色 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_ONLY.getScope()) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); - // mock 部门的返回 - when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), - null, null); // 最后返回 null 的目的,看看会不会重复调用 - - // 调用 - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); - // 断言 - assertFalse(result.getAll()); - assertFalse(result.getSelf()); - assertEquals(1, result.getDeptIds().size()); - assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); - } - } - - @Test - public void testGetDeptDataPermission_DeptAndChild() { - try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { - springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) - .thenReturn(permissionService); - - // 准备参数 - Long userId = 1L; - // mock 用户的角色编号 - userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); - // mock 获得用户的角色 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.DEPT_AND_CHILD.getScope()) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); - // mock 部门的返回 - when(userService.getUser(eq(1L))).thenReturn(new AdminUserDO().setDeptId(3L), - null, null); // 最后返回 null 的目的,看看会不会重复调用 - // mock 方法(部门) - DeptDO deptDO = randomPojo(DeptDO.class); - when(deptService.getChildDeptIdListFromCache(eq(3L))).thenReturn(singleton(deptDO.getId())); - - // 调用 - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); - // 断言 - assertFalse(result.getAll()); - assertFalse(result.getSelf()); - assertEquals(2, result.getDeptIds().size()); - assertTrue(CollUtil.contains(result.getDeptIds(), deptDO.getId())); - assertTrue(CollUtil.contains(result.getDeptIds(), 3L)); - } - } - - @Test - public void testGetDeptDataPermission_Self() { - try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { - springUtilMockedStatic.when(() -> SpringUtil.getBean(eq(PermissionServiceImpl.class))) - .thenReturn(permissionService); - - // 准备参数 - Long userId = 1L; - // mock 用户的角色编号 - userRoleMapper.insert(randomPojo(UserRoleDO.class).setUserId(userId).setRoleId(2L)); - // mock 获得用户的角色 - RoleDO roleDO = randomPojo(RoleDO.class, o -> o.setDataScope(DataScopeEnum.SELF.getScope()) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - when(roleService.getRoleListFromCache(eq(singleton(2L)))).thenReturn(toList(roleDO)); - - // 调用 - DeptDataPermissionRespDTO result = permissionService.getDeptDataPermission(userId); - // 断言 - assertFalse(result.getAll()); - assertTrue(result.getSelf()); - assertTrue(CollUtil.isEmpty(result.getDeptIds())); - } - } } diff --git a/yudao-module-system/yudao-module-system-server/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImplTest.java b/yudao-module-system/yudao-module-system-server/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImplTest.java index fc87193c..404160e9 100644 --- a/yudao-module-system/yudao-module-system-server/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImplTest.java +++ b/yudao-module-system/yudao-module-system-server/src/test/java/cn/iocoder/yudao/module/system/service/permission/RoleServiceImplTest.java @@ -10,12 +10,12 @@ import cn.iocoder.yudao.module.system.dal.dataobject.permission.RoleDO; import cn.iocoder.yudao.module.system.dal.mysql.permission.RoleMapper; import cn.iocoder.yudao.module.system.enums.permission.DataScopeEnum; import cn.iocoder.yudao.module.system.enums.permission.RoleTypeEnum; +import jakarta.annotation.Resource; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; -import jakarta.annotation.Resource; import java.util.Collection; import java.util.List; import java.util.Set; @@ -52,13 +52,15 @@ public class RoleServiceImplTest extends BaseDbUnitTest { // 准备参数 RoleSaveReqVO reqVO = randomPojo(RoleSaveReqVO.class) .setId(null) // 防止 id 被赋值 - .setStatus(randomCommonStatus()); + .setStatus(randomCommonStatus()) + .setType(RoleTypeEnum.CUSTOM.getType()) // 设置为自定义角色; + .setParentId(0L); // 调用 Long roleId = roleService.createRole(reqVO, null); // 断言 RoleDO roleDO = roleMapper.selectById(roleId); - assertPojoEquals(reqVO, roleDO, "id"); + assertPojoEquals(reqVO, roleDO, "id", "parentName"); assertEquals(RoleTypeEnum.CUSTOM.getType(), roleDO.getType()); assertEquals(DataScopeEnum.ALL.getScope(), roleDO.getDataScope()); } @@ -77,7 +79,7 @@ public class RoleServiceImplTest extends BaseDbUnitTest { roleService.updateRole(reqVO); // 断言 RoleDO newRoleDO = roleMapper.selectById(id); - assertPojoEquals(reqVO, newRoleDO); + assertPojoEquals(reqVO, newRoleDO, "parentName"); } @Test @@ -184,7 +186,7 @@ public class RoleServiceImplTest extends BaseDbUnitTest { // 调用 RoleDO dbRoleDO = roleService.getRole(id); // 断言 - assertPojoEquals(roleDO, dbRoleDO); + assertPojoEquals(roleDO, dbRoleDO, "parentName"); } @Test @@ -198,7 +200,7 @@ public class RoleServiceImplTest extends BaseDbUnitTest { // 调用 RoleDO dbRoleDO = roleService.getRoleFromCache(id); // 断言 - assertPojoEquals(roleDO, dbRoleDO); + assertPojoEquals(roleDO, dbRoleDO, "parentName"); } @Test @@ -214,7 +216,7 @@ public class RoleServiceImplTest extends BaseDbUnitTest { singleton(CommonStatusEnum.ENABLE.getStatus())); // 断言 assertEquals(1, list.size()); - assertPojoEquals(dbRole01, list.get(0)); + assertPojoEquals(dbRole01, list.get(0), "parentName"); } @Test @@ -229,8 +231,8 @@ public class RoleServiceImplTest extends BaseDbUnitTest { List list = roleService.getRoleList(); // 断言 assertEquals(2, list.size()); - assertPojoEquals(dbRole01, list.get(0)); - assertPojoEquals(dbRole02, list.get(1)); + assertPojoEquals(dbRole01, list.get(0), "parentName"); + assertPojoEquals(dbRole02, list.get(1), "parentName"); } @Test @@ -247,7 +249,7 @@ public class RoleServiceImplTest extends BaseDbUnitTest { List list = roleService.getRoleList(ids); // 断言 assertEquals(1, list.size()); - assertPojoEquals(dbRole01, list.get(0)); + assertPojoEquals(dbRole01, list.get(0), "parentName"); } @Test @@ -268,7 +270,7 @@ public class RoleServiceImplTest extends BaseDbUnitTest { List list = roleService.getRoleListFromCache(ids); // 断言 assertEquals(1, list.size()); - assertPojoEquals(dbRole, list.get(0)); + assertPojoEquals(dbRole, list.get(0), "parentName"); } } @@ -300,7 +302,7 @@ public class RoleServiceImplTest extends BaseDbUnitTest { // 断言 assertEquals(1, pageResult.getTotal()); assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbRole, pageResult.getList().get(0)); + assertPojoEquals(dbRole, pageResult.getList().get(0), "parentName"); } @Test diff --git a/yudao-module-system/yudao-module-system-server/src/test/resources/application-unit-test.yaml b/yudao-module-system/yudao-module-system-server/src/test/resources/application-unit-test.yaml index 93023bdf..9dd9c9a2 100644 --- a/yudao-module-system/yudao-module-system-server/src/test/resources/application-unit-test.yaml +++ b/yudao-module-system/yudao-module-system-server/src/test/resources/application-unit-test.yaml @@ -34,6 +34,8 @@ mybatis-plus: global-config: db-config: id-type: AUTO # H2 主键递增 + configuration: + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl --- #################### 定时任务相关配置 #################### diff --git a/yudao-module-system/yudao-module-system-server/src/test/resources/sql/create_tables.sql b/yudao-module-system/yudao-module-system-server/src/test/resources/sql/create_tables.sql index 4df039b8..f5805522 100644 --- a/yudao-module-system/yudao-module-system-server/src/test/resources/sql/create_tables.sql +++ b/yudao-module-system/yudao-module-system-server/src/test/resources/sql/create_tables.sql @@ -13,6 +13,7 @@ CREATE TABLE IF NOT EXISTS "system_dept" ( "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, "deleted" bit NOT NULL DEFAULT FALSE, "tenant_id" bigint not null default '0', + "is_tenant" bit NOT NULL DEFAULT FALSE, PRIMARY KEY ("id") ) COMMENT '部门表'; @@ -50,6 +51,7 @@ CREATE TABLE IF NOT EXISTS "system_role" ( "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, "deleted" bit NOT NULL DEFAULT FALSE, "tenant_id" bigint not null default '0', + "parent_id" bigint NOT NULL DEFAULT '0', PRIMARY KEY ("id") ) COMMENT '角色信息表'; @@ -612,3 +614,17 @@ CREATE TABLE IF NOT EXISTS "system_notify_message" ( "tenant_id" bigint not null default '0', PRIMARY KEY ("id") ) COMMENT '站内信消息表'; + +CREATE TABLE if not exists `system_role_menu_exclusion` ( + `id` BIGINT PRIMARY KEY COMMENT '主键ID', + `role_id` BIGINT NOT NULL COMMENT '角色ID', + `menu_id` BIGINT NOT NULL COMMENT '菜单ID', + `remark` VARCHAR(2000) COMMENT '备注', + `creator` VARCHAR(256) DEFAULT '' COMMENT '创建者', + `create_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '创建时间', + `updater` VARCHAR(256) DEFAULT '' COMMENT '更新者', + `update_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '更新时间', + `deleted` TINYINT DEFAULT 0 NOT NULL COMMENT '是否删除', + `tenant_id` BIGINT DEFAULT 0 NOT NULL COMMENT '租户编号', + UNIQUE KEY `idx_role_menu_exclusion_id` (`id`) +) COMMENT='角色菜单剔除表'; diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index 43f8f584..1ee2da10 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/yudao-server/src/main/resources/application-local.yaml @@ -97,7 +97,7 @@ xxl: # rocketmq 配置项,对应 RocketMQProperties 配置类 rocketmq: - name-server: 127.0.0.1:9876 # RocketMQ Namesrv + name-server: 172.16.46.63:30876 # RocketMQ Namesrv spring: # RabbitMQ 配置项,对应 RabbitProperties 配置类