Explorar o código

1、增加防篡改签名的日志
2、增加菜单、角色权限模块

rayson hai 6 meses
pai
achega
3b663de8f4
Modificáronse 33 ficheiros con 1489 adicións e 126 borrados
  1. 2 0
      citu-framework/citu-spring-boot-starter-protection/src/main/java/com/citu/framework/signature/core/aop/ApiSignatureAspect.java
  2. 20 0
      menduner/menduner-common/src/main/java/com/citu/module/menduner/common/CommonConstants.java
  3. 1 1
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/admin/permission/MdePermissionController.java
  4. 62 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/app/recruit/permission/AppMdeMenuController.java
  5. 129 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/app/recruit/permission/AppMdePermissionController.java
  6. 103 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/app/recruit/permission/AppMdeRoleController.java
  7. 2 1
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/app/recruit/person/AppRecruitPersonController.java
  8. 9 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/app/recruit/person/vo/AppRecruitPersonInfoRespVO.java
  9. 16 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/base/permission/menu/MdeMenuListReqVO.java
  10. 69 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/base/permission/menu/MdeMenuRespVO.java
  11. 65 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/base/permission/menu/MdeMenuSaveVO.java
  12. 22 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/base/permission/menu/MdeMenuSimpleRespVO.java
  13. 1 1
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/base/permission/permission/MdePermissionAssignRoleDataScopeReqVO.java
  14. 74 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/base/permission/permission/MdePermissionInfoRespVO.java
  15. 2 2
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/base/permission/role/MdeRoleRespVO.java
  16. 78 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/convert/AuthConvert.java
  17. 107 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/dal/dataobject/permission/MdeMenuDO.java
  18. 4 4
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/dal/dataobject/permission/MdeRoleDO.java
  19. 35 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/dal/dataobject/permission/MdeRoleMenuDO.java
  20. 6 2
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/dal/dataobject/permission/MdeUserRoleDO.java
  21. 31 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/dal/mysql/permission/MdeMenuMapper.java
  22. 40 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/dal/mysql/permission/MdeRoleMenuMapper.java
  23. 16 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/dal/redis/RedisKeyConstants.java
  24. 97 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/permission/MdeMenuService.java
  25. 255 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/permission/MdeMenuServiceImpl.java
  26. 48 11
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/permission/MdePermissionService.java
  27. 157 59
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/permission/MdePermissionServiceImpl.java
  28. 2 2
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/permission/MdeRoleService.java
  29. 2 2
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/permission/MdeRoleServiceImpl.java
  30. 3 1
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/person/PersonIntegrationService.java
  31. 25 37
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/person/PersonIntegrationServiceImpl.java
  32. 3 0
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/user/MdeUserServiceImpl.java
  33. 3 3
      menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/util/MessageUtils.java

+ 2 - 0
citu-framework/citu-spring-boot-starter-protection/src/main/java/com/citu/framework/signature/core/aop/ApiSignatureAspect.java

@@ -85,6 +85,8 @@ public class ApiSignatureAspect {
             return;
         }
         if (null != errorRecordApi) {
+            recordThreadLocal.get().put("url", ServletUtils.getRequest().getRequestURI());
+            recordThreadLocal.get().put("currentTime", System.currentTimeMillis());
             errorRecordApi.create(ErrorRecordReqDTO.builder()
                     .mark(ServletUtils.getRequest().getHeader(signature.timestamp()))
                     .content(JSON.toJSONString(recordThreadLocal.get()))

+ 20 - 0
menduner/menduner-common/src/main/java/com/citu/module/menduner/common/CommonConstants.java

@@ -1,5 +1,6 @@
 package com.citu.module.menduner.common;
 
+import cn.hutool.core.collection.CollUtil;
 import cn.hutool.core.map.MapUtil;
 import com.citu.module.system.api.mail.dto.MailSendSingleToUserReqDTO;
 
@@ -153,6 +154,25 @@ public class CommonConstants {
         put(NOTIFY_SYS_ENTERPRISE_REGISTER, "企业申请注册");
         put(NOTIFY_SYS_HUNTER_SERVICE, "猎头服务");
     }};
+    /**
+     * 获取平台系统消息内容
+     * @param templateCode 模板编号
+     * @param templateParams 模板参数
+     * @return
+     */
+    public static String getPlatformSystemMessageContent(String templateCode,Map<String, Object> templateParams) {
+        String content =  notifySysStrMap.get(templateCode);
+        if(CollUtil.isEmpty(templateParams)) {
+            return content;
+        }
+        // content 后面追加templateParams的每个value,用逗号隔开
+        for (Map.Entry<String, Object> entry : templateParams.entrySet()) {
+            content += "," + entry.getValue();
+        }
+        // content 加双引号包围
+        content = "\"" + content + "\"";
+        return content;
+    }
     /**
      * 系统提示-积分兑换 1-{商品},2-{兑换积分},3-{联系人},4-{联系电话}
      **/

+ 1 - 1
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/admin/permission/MdePermissionController.java

@@ -35,7 +35,7 @@ public class MdePermissionController {
     @Operation(summary = "赋予角色数据权限")
     @PreAuthorize("@ss.hasPermission('menduner:system:permission:assign-role-data-scope')")
     public CommonResult<Boolean> assignRoleDataScope(@Valid @RequestBody MdePermissionAssignRoleDataScopeReqVO reqVO) {
-        permissionService.assignRoleDataScope(reqVO.getRoleId(), reqVO.getDataScope(), reqVO.getDataScopeDeptIds());
+        permissionService.assignRoleDataScope(reqVO.getRoleId(), reqVO.getDataScope(), reqVO.getDataScopeEnterpriseIds());
         return success(true);
     }
 

+ 62 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/app/recruit/permission/AppMdeMenuController.java

@@ -0,0 +1,62 @@
+package com.citu.module.menduner.system.controller.app.recruit.permission;
+
+import com.citu.framework.common.enums.CommonStatusEnum;
+import com.citu.framework.common.pojo.CommonResult;
+import com.citu.framework.common.util.object.BeanUtils;
+import com.citu.framework.security.core.annotations.PreAuthenticated;
+import com.citu.module.menduner.system.controller.base.permission.menu.MdeMenuListReqVO;
+import com.citu.module.menduner.system.controller.base.permission.menu.MdeMenuRespVO;
+import com.citu.module.menduner.system.controller.base.permission.menu.MdeMenuSimpleRespVO;
+import com.citu.module.menduner.system.dal.dataobject.permission.MdeMenuDO;
+import com.citu.module.menduner.system.service.permission.MdeMenuService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import java.util.Comparator;
+import java.util.List;
+
+import static com.citu.framework.common.pojo.CommonResult.success;
+
+@Tag(name = "管理后台 - 菜单")
+@RestController
+@RequestMapping("/menduner/system/recruit/menu")
+@Validated
+public class AppMdeMenuController {
+
+    @Resource
+    private MdeMenuService menuService;
+
+    @GetMapping("/list")
+    @Operation(summary = "获取菜单列表", description = "用于【菜单管理】界面")
+    @PreAuthenticated
+    public CommonResult<List<MdeMenuRespVO>> getMenuList(MdeMenuListReqVO reqVO) {
+        List<MdeMenuDO> list = menuService.getMenuList(reqVO);
+        list.sort(Comparator.comparing(MdeMenuDO::getSort));
+        return success(BeanUtils.toBean(list, MdeMenuRespVO.class));
+    }
+
+    @GetMapping({"/list-all-simple", "simple-list"})
+    @Operation(summary = "获取菜单精简信息列表", description = "只包含被开启的菜单,用于【角色分配菜单】功能的选项。" +
+            "在多租户的场景下,会只返回租户所在套餐有的菜单")
+    @PreAuthenticated
+    public CommonResult<List<MdeMenuSimpleRespVO>> getSimpleMenuList() {
+        List<MdeMenuDO> list = menuService.getMenuListByTenant(
+                new MdeMenuListReqVO().setStatus(CommonStatusEnum.ENABLE.getStatus()));
+        list.sort(Comparator.comparing(MdeMenuDO::getSort));
+        return success(BeanUtils.toBean(list, MdeMenuSimpleRespVO.class));
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获取菜单信息")
+    @PreAuthenticated
+    public CommonResult<MdeMenuRespVO> getMenu(Long id) {
+        MdeMenuDO menu = menuService.getMenu(id);
+        return success(BeanUtils.toBean(menu, MdeMenuRespVO.class));
+    }
+
+}

+ 129 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/app/recruit/permission/AppMdePermissionController.java

@@ -0,0 +1,129 @@
+package com.citu.module.menduner.system.controller.app.recruit.permission;
+
+import cn.hutool.core.collection.CollUtil;
+import com.citu.framework.common.pojo.CommonResult;
+import com.citu.framework.security.core.annotations.PreAuthenticated;
+import com.citu.module.menduner.common.util.LoginUserContext;
+import com.citu.module.menduner.system.controller.app.recruit.user.vo.AppRecruitUserRespVO;
+import com.citu.module.menduner.system.controller.base.enterprise.vip.EnterpriseEntitlementRespVO;
+import com.citu.module.menduner.system.controller.base.permission.permission.MdePermissionAssignRoleDataScopeReqVO;
+import com.citu.module.menduner.system.controller.base.permission.permission.MdePermissionAssignRoleMenuReqVO;
+import com.citu.module.menduner.system.controller.base.permission.permission.MdePermissionAssignUserRoleReqVO;
+import com.citu.module.menduner.system.controller.base.permission.permission.MdePermissionInfoRespVO;
+import com.citu.module.menduner.system.convert.AuthConvert;
+import com.citu.module.menduner.system.dal.dataobject.permission.MdeMenuDO;
+import com.citu.module.menduner.system.dal.dataobject.permission.MdeRoleDO;
+import com.citu.module.menduner.system.enums.MendunerStatusEnum;
+import com.citu.module.menduner.system.service.enterprise.bind.EnterpriseUserBindService;
+import com.citu.module.menduner.system.service.enterprise.vip.EnterpriseEntitlementService;
+import com.citu.module.menduner.system.service.permission.MdeMenuService;
+import com.citu.module.menduner.system.service.permission.MdePermissionService;
+import com.citu.module.menduner.system.service.permission.MdeRoleService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import static com.citu.framework.common.pojo.CommonResult.success;
+import static com.citu.framework.common.util.collection.CollectionUtils.convertSet;
+import static com.citu.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
+
+/**
+ * 权限 Controller,提供赋予用户、角色的权限的 API 接口
+ *
+ * @author Rayson
+ */
+@Tag(name = "管理后台 - 权限")
+@RestController
+@RequestMapping("/menduner/system/recruit/permission")
+public class AppMdePermissionController {
+
+    @Resource
+    private MdePermissionService permissionService;
+    @Resource
+    private MdeRoleService roleService;
+    @Resource
+    private MdeMenuService menuService;
+    @Resource
+    private EnterpriseUserBindService userService;
+    @Resource
+    private EnterpriseEntitlementService entitlementService;
+
+    @Operation(summary = "获得角色拥有的菜单编号")
+    @Parameter(name = "roleId", description = "角色编号", required = true)
+    @GetMapping("/list-role-menus")
+    @PreAuthenticated
+    public CommonResult<Set<Long>> getRoleMenuList(Long roleId) {
+        return success(permissionService.getRoleMenuListByRoleId(roleId));
+    }
+
+    @PostMapping("/assign-role-menu")
+    @Operation(summary = "赋予角色菜单")
+    @PreAuthenticated
+    public CommonResult<Boolean> assignRoleMenu(@Validated @RequestBody MdePermissionAssignRoleMenuReqVO reqVO) {
+        // 执行菜单的分配
+        permissionService.assignRoleMenu(reqVO.getRoleId(), reqVO.getMenuIds());
+        return success(true);
+    }
+
+    @PostMapping("/assign-role-data-scope")
+    @Operation(summary = "赋予角色数据权限")
+    @PreAuthenticated
+    public CommonResult<Boolean> assignRoleDataScope(@Valid @RequestBody MdePermissionAssignRoleDataScopeReqVO reqVO) {
+        permissionService.assignRoleDataScope(reqVO.getRoleId(), reqVO.getDataScope(), reqVO.getDataScopeEnterpriseIds());
+        return success(true);
+    }
+
+    @Operation(summary = "获得管理员拥有的角色编号列表")
+    @Parameter(name = "userId", description = "用户编号", required = true)
+    @GetMapping("/list-user-roles")
+    @PreAuthenticated
+    public CommonResult<Set<Long>> listAdminRoles(@RequestParam("userId") Long userId) {
+        return success(permissionService.getUserRoleIdListByUserId(userId));
+    }
+
+    @Operation(summary = "赋予用户角色")
+    @PostMapping("/assign-user-role")
+    @PreAuthenticated
+    public CommonResult<Boolean> assignUserRole(@Validated @RequestBody MdePermissionAssignUserRoleReqVO reqVO) {
+        permissionService.assignUserRole(reqVO.getUserId(), reqVO.getRoleIds());
+        return success(true);
+    }
+
+    @GetMapping("/get-permission-info")
+    @Operation(summary = "获取登录用户的权限信息")
+    public CommonResult<MdePermissionInfoRespVO> getPermissionInfo() {
+        // 1.1 获得用户信息
+        AppRecruitUserRespVO user = userService.getBindEnterprise();
+        EnterpriseEntitlementRespVO entitlementRespVO = entitlementService
+                .getByEnterpriseIdAndUserId(LoginUserContext.getEnterpriseId(), LoginUserContext.getUserId());
+        user.setEntitlement(entitlementRespVO);
+        if (userService == null) {
+            return success(null);
+        }
+
+        // 1.2 获得角色列表
+        Set<Long> roleIds = permissionService.getUserRoleIdListByUserId(getLoginUserId());
+        if (CollUtil.isEmpty(roleIds)) {
+            return success(AuthConvert.INSTANCE.convert(user, Collections.emptyList(), Collections.emptyList()));
+        }
+        List<MdeRoleDO> roles = roleService.getRoleList(roleIds);
+        roles.removeIf(role -> !MendunerStatusEnum.ENABLE.getStatus().equals(role.getStatus())); // 移除禁用的角色
+
+        // 1.3 获得菜单列表
+        Set<Long> menuIds = permissionService.getRoleMenuListByRoleId(convertSet(roles, MdeRoleDO::getId));
+        List<MdeMenuDO> menuList = menuService.getMenuList(menuIds);
+        menuList.removeIf(menu -> !MendunerStatusEnum.ENABLE.getStatus().equals(menu.getStatus().toString())); // 移除禁用的菜单
+
+        // 2. 拼接结果返回
+        return success(AuthConvert.INSTANCE.convert(user, roles, menuList));
+    }
+
+}

+ 103 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/app/recruit/permission/AppMdeRoleController.java

@@ -0,0 +1,103 @@
+package com.citu.module.menduner.system.controller.app.recruit.permission;
+
+import com.citu.framework.apilog.core.annotation.ApiAccessLog;
+import com.citu.framework.common.pojo.CommonResult;
+import com.citu.framework.common.pojo.PageParam;
+import com.citu.framework.common.pojo.PageResult;
+import com.citu.framework.common.util.object.BeanUtils;
+import com.citu.framework.excel.core.util.ExcelUtils;
+import com.citu.framework.security.core.annotations.PreAuthenticated;
+import com.citu.module.menduner.system.controller.base.permission.role.MdeRolePageReqVO;
+import com.citu.module.menduner.system.controller.base.permission.role.MdeRoleRespVO;
+import com.citu.module.menduner.system.controller.base.permission.role.MdeRoleSaveReqVO;
+import com.citu.module.menduner.system.dal.dataobject.permission.MdeRoleDO;
+import com.citu.module.menduner.system.enums.MendunerStatusEnum;
+import com.citu.module.menduner.system.service.permission.MdeRoleService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletResponse;
+import javax.validation.Valid;
+import java.io.IOException;
+import java.util.Comparator;
+import java.util.List;
+
+import static com.citu.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
+import static com.citu.framework.common.pojo.CommonResult.success;
+import static java.util.Collections.singleton;
+
+@Tag(name = "管理后台 - 角色")
+@RestController
+@RequestMapping("/menduner/system/recruit/role")
+@Validated
+public class AppMdeRoleController {
+
+    @Resource
+    private MdeRoleService roleService;
+
+    @PostMapping("/create")
+    @Operation(summary = "创建角色")
+    @PreAuthenticated
+    public CommonResult<Long> createRole(@Valid @RequestBody MdeRoleSaveReqVO createReqVO) {
+        return success(roleService.createRole(createReqVO, null));
+    }
+
+    @PutMapping("/update")
+    @Operation(summary = "修改角色")
+    @PreAuthenticated
+    public CommonResult<Boolean> updateRole(@Valid @RequestBody MdeRoleSaveReqVO updateReqVO) {
+        roleService.updateRole(updateReqVO);
+        return success(true);
+    }
+
+    @DeleteMapping("/delete")
+    @Operation(summary = "删除角色")
+    @Parameter(name = "id", description = "角色编号", required = true, example = "1024")
+    @PreAuthenticated
+    public CommonResult<Boolean> deleteRole(@RequestParam("id") Long id) {
+        roleService.deleteRole(id);
+        return success(true);
+    }
+
+    @GetMapping("/get")
+    @Operation(summary = "获得角色信息")
+    @PreAuthenticated
+    public CommonResult<MdeRoleRespVO> getRole(@RequestParam("id") Long id) {
+        MdeRoleDO role = roleService.getRole(id);
+        return success(BeanUtils.toBean(role, MdeRoleRespVO.class));
+    }
+
+    @GetMapping("/page")
+    @Operation(summary = "获得角色分页")
+    @PreAuthenticated
+    public CommonResult<PageResult<MdeRoleRespVO>> getRolePage(MdeRolePageReqVO pageReqVO) {
+        PageResult<MdeRoleDO> pageResult = roleService.getRolePage(pageReqVO);
+        return success(BeanUtils.toBean(pageResult, MdeRoleRespVO.class));
+    }
+
+    @GetMapping({"/list-all-simple", "/simple-list"})
+    @Operation(summary = "获取角色精简信息列表", description = "只包含被开启的角色,主要用于前端的下拉选项")
+    @PreAuthenticated
+    public CommonResult<List<MdeRoleRespVO>> getSimpleRoleList() {
+        List<MdeRoleDO> list = roleService.getRoleListByStatus(singleton(MendunerStatusEnum.ENABLE.getStatus()));
+        list.sort(Comparator.comparing(MdeRoleDO::getSort));
+        return success(BeanUtils.toBean(list, MdeRoleRespVO.class));
+    }
+
+    @GetMapping("/export-excel")
+    @Operation(summary = "导出角色 Excel")
+    @ApiAccessLog(operateType = EXPORT)
+    @PreAuthenticated
+    public void export(HttpServletResponse response, @Validated MdeRolePageReqVO exportReqVO) throws IOException {
+        exportReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
+        List<MdeRoleDO> list = roleService.getRolePage(exportReqVO).getList();
+        // 输出
+        ExcelUtils.write(response, "角色数据.xls", "数据", MdeRoleRespVO.class,
+                BeanUtils.toBean(list, MdeRoleRespVO.class));
+    }
+
+}

+ 2 - 1
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/app/recruit/person/AppRecruitPersonController.java

@@ -5,6 +5,7 @@ import com.citu.framework.common.pojo.PageResult;
 import com.citu.framework.security.core.annotations.PreAuthenticated;
 import com.citu.framework.signature.core.annotation.ApiSignature;
 import com.citu.module.menduner.common.util.LoginUserContext;
+import com.citu.module.menduner.system.controller.app.recruit.person.vo.AppRecruitPersonInfoRespVO;
 import com.citu.module.menduner.system.controller.base.person.PersonQueryReqVO;
 import com.citu.module.menduner.system.controller.base.person.PersonMapQueryReqVO;
 import com.citu.module.menduner.system.controller.base.person.PersonRecommendQueryReqVO;
@@ -45,7 +46,7 @@ public class AppRecruitPersonController {
     @GetMapping("/person-search/page")
     @ApiSignature(timeout = 30)
     @Operation(summary = "分页检索人才信息")
-    public CommonResult<PageResult<PersonInfoRespVO>> query(@Valid PersonQueryReqVO reqVO) {
+    public CommonResult<PageResult<AppRecruitPersonInfoRespVO>> query(@Valid PersonQueryReqVO reqVO) {
         return success(personIntegrationService.queryPage(reqVO));
     }
 

+ 9 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/app/recruit/person/vo/AppRecruitPersonInfoRespVO.java

@@ -1,10 +1,13 @@
 package com.citu.module.menduner.system.controller.app.recruit.person.vo;
 
+import com.citu.module.menduner.system.controller.app.recruit.person.job.AppRecruitJobInterestedRespVO;
+import com.citu.module.menduner.system.controller.app.recruit.person.workexp.AppRecruitWorkExpRespVO;
 import com.citu.module.menduner.system.controller.base.person.info.PersonInfoRespVO;
 import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 
 import java.time.LocalDateTime;
+import java.util.List;
 
 @Schema(description = "menduner - 人才信息-人才档案 Response VO")
 @Data
@@ -13,4 +16,10 @@ public class AppRecruitPersonInfoRespVO extends PersonInfoRespVO {
     @Schema(description = "更新时间")
     private LocalDateTime updateTime;
 
+    @Schema(description = "求职意向")
+    private List<AppRecruitJobInterestedRespVO> interestedList;
+
+    @Schema(description = "人才工作经历")
+    private List<AppRecruitWorkExpRespVO> workList;
+
 }

+ 16 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/base/permission/menu/MdeMenuListReqVO.java

@@ -0,0 +1,16 @@
+package com.citu.module.menduner.system.controller.base.permission.menu;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - 菜单列表 Request VO")
+@Data
+public class MdeMenuListReqVO {
+
+    @Schema(description = "菜单名称,模糊匹配", example = "芋道")
+    private String name;
+
+    @Schema(description = "展示状态,参见 CommonStatusEnum 枚举类", example = "1")
+    private Integer status;
+
+}

+ 69 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/base/permission/menu/MdeMenuRespVO.java

@@ -0,0 +1,69 @@
+package com.citu.module.menduner.system.controller.base.permission.menu;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.time.LocalDateTime;
+
+@Schema(description = "管理后台 - 菜单信息 Response VO")
+@Data
+public class MdeMenuRespVO {
+
+    @Schema(description = "菜单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    private Long id;
+
+    @Schema(description = "菜单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
+    @NotBlank(message = "菜单名称不能为空")
+    @Size(max = 50, message = "菜单名称长度不能超过50个字符")
+    private String name;
+
+    @Schema(description = "权限标识,仅菜单类型为按钮时,才需要传递", example = "sys:menu:add")
+    @Size(max = 100)
+    private String permission;
+
+    @Schema(description = "类型,参见 MenuTypeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "菜单类型不能为空")
+    private Integer type;
+
+    @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    @NotNull(message = "显示顺序不能为空")
+    private Integer sort;
+
+    @Schema(description = "父菜单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    @NotNull(message = "父菜单 ID 不能为空")
+    private Long parentId;
+
+    @Schema(description = "路由地址,仅菜单类型为菜单或者目录时,才需要传", example = "post")
+    @Size(max = 200, message = "路由地址不能超过200个字符")
+    private String path;
+
+    @Schema(description = "菜单图标,仅菜单类型为菜单或者目录时,才需要传", example = "/menu/list")
+    private String icon;
+
+    @Schema(description = "组件路径,仅菜单类型为菜单时,才需要传", example = "system/post/index")
+    @Size(max = 200, message = "组件路径不能超过255个字符")
+    private String component;
+
+    @Schema(description = "组件名", example = "SystemUser")
+    private String componentName;
+
+    @Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "状态不能为空")
+    private Integer status;
+
+    @Schema(description = "是否可见", example = "false")
+    private Boolean visible;
+
+    @Schema(description = "是否缓存", example = "false")
+    private Boolean keepAlive;
+
+    @Schema(description = "是否总是显示", example = "false")
+    private Boolean alwaysShow;
+
+    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式")
+    private LocalDateTime createTime;
+
+}

+ 65 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/base/permission/menu/MdeMenuSaveVO.java

@@ -0,0 +1,65 @@
+package com.citu.module.menduner.system.controller.base.permission.menu;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+
+@Schema(description = "管理后台 - 菜单创建/修改 Request VO")
+@Data
+public class MdeMenuSaveVO {
+
+    @Schema(description = "菜单编号", example = "1024")
+    private Long id;
+
+    @Schema(description = "菜单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
+    @NotBlank(message = "菜单名称不能为空")
+    @Size(max = 50, message = "菜单名称长度不能超过50个字符")
+    private String name;
+
+    @Schema(description = "权限标识,仅菜单类型为按钮时,才需要传递", example = "sys:menu:add")
+    @Size(max = 100)
+    private String permission;
+
+    @Schema(description = "类型,参见 MenuTypeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "菜单类型不能为空")
+    private Integer type;
+
+    @Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    @NotNull(message = "显示顺序不能为空")
+    private Integer sort;
+
+    @Schema(description = "父菜单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    @NotNull(message = "父菜单 ID 不能为空")
+    private Long parentId;
+
+    @Schema(description = "路由地址,仅菜单类型为菜单或者目录时,才需要传", example = "post")
+    @Size(max = 200, message = "路由地址不能超过200个字符")
+    private String path;
+
+    @Schema(description = "菜单图标,仅菜单类型为菜单或者目录时,才需要传", example = "/menu/list")
+    private String icon;
+
+    @Schema(description = "组件路径,仅菜单类型为菜单时,才需要传", example = "system/post/index")
+    @Size(max = 200, message = "组件路径不能超过255个字符")
+    private String component;
+
+    @Schema(description = "组件名", example = "SystemUser")
+    private String componentName;
+
+    @Schema(description = "状态,见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    @NotNull(message = "状态不能为空")
+    private Integer status;
+
+    @Schema(description = "是否可见", example = "false")
+    private Boolean visible;
+
+    @Schema(description = "是否缓存", example = "false")
+    private Boolean keepAlive;
+
+    @Schema(description = "是否总是显示", example = "false")
+    private Boolean alwaysShow;
+
+}

+ 22 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/base/permission/menu/MdeMenuSimpleRespVO.java

@@ -0,0 +1,22 @@
+package com.citu.module.menduner.system.controller.base.permission.menu;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Schema(description = "管理后台 - 菜单精简信息 Response VO")
+@Data
+public class MdeMenuSimpleRespVO {
+
+    @Schema(description = "菜单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    private Long id;
+
+    @Schema(description = "菜单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
+    private String name;
+
+    @Schema(description = "父菜单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+    private Long parentId;
+
+    @Schema(description = "类型,参见 MenuTypeEnum 枚举类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
+    private Integer type;
+
+}

+ 1 - 1
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/base/permission/permission/MdePermissionAssignRoleDataScopeReqVO.java

@@ -23,6 +23,6 @@ public class MdePermissionAssignRoleDataScopeReqVO {
     private Integer dataScope;
 
     @Schema(description = "数据编号列表,只有范围类型为 DEPT_CUSTOM 时,该字段才需要", example = "1,3,5")
-    private Set<Long> dataScopeDeptIds = Collections.emptySet(); // 兜底
+    private Set<Long> dataScopeEnterpriseIds = Collections.emptySet(); // 兜底
 
 }

+ 74 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/base/permission/permission/MdePermissionInfoRespVO.java

@@ -0,0 +1,74 @@
+package com.citu.module.menduner.system.controller.base.permission.permission;
+
+import com.citu.module.menduner.system.controller.app.recruit.user.vo.AppRecruitUserRespVO;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+import java.util.Set;
+
+@Schema(description = "管理后台 - 登录用户的权限信息 Response VO,额外包括用户信息和角色列表")
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class MdePermissionInfoRespVO {
+
+    @Schema(description = "用户信息", requiredMode = Schema.RequiredMode.REQUIRED)
+    private AppRecruitUserRespVO user;
+
+    @Schema(description = "角色标识数组", requiredMode = Schema.RequiredMode.REQUIRED)
+    private Set<String> roles;
+
+    @Schema(description = "菜单树", requiredMode = Schema.RequiredMode.REQUIRED)
+    private List<MenuVO> menus;
+
+
+    @Schema(description = "管理后台 - 登录用户的菜单信息 Response VO")
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @Builder
+    public static class MenuVO {
+
+        @Schema(description = "菜单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
+        private Long id;
+
+        @Schema(description = "父菜单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
+        private Long parentId;
+
+        @Schema(description = "菜单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
+        private String name;
+
+        @Schema(description = "路由地址,仅菜单类型为菜单或者目录时,才需要传", example = "post")
+        private String path;
+
+        @Schema(description = "组件路径,仅菜单类型为菜单时,才需要传", example = "system/post/index")
+        private String component;
+
+        @Schema(description = "组件名", example = "SystemUser")
+        private String componentName;
+
+        @Schema(description = "菜单图标,仅菜单类型为菜单或者目录时,才需要传", example = "/menu/list")
+        private String icon;
+
+        @Schema(description = "是否可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
+        private Boolean visible;
+
+        @Schema(description = "是否缓存", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
+        private Boolean keepAlive;
+
+        @Schema(description = "是否总是显示", example = "false")
+        private Boolean alwaysShow;
+
+        /**
+         * 子路由
+         */
+        private List<MenuVO> children;
+
+    }
+
+}

+ 2 - 2
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/controller/base/permission/role/MdeRoleRespVO.java

@@ -49,8 +49,8 @@ public class MdeRoleRespVO {
     @ExcelProperty("数据范围")
     private Integer dataScope;
 
-    @Schema(description = "数据范围(指定部门数组)", example = "1")
-    private Set<Long> dataScopeDeptIds;
+    @Schema(description = "数据范围(指定企业数组)", example = "1")
+    private Set<Long> dataScopeEnterpriseIds;
 
     @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "时间戳格式")
     private LocalDateTime createTime;

+ 78 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/convert/AuthConvert.java

@@ -0,0 +1,78 @@
+package com.citu.module.menduner.system.convert;
+
+import cn.hutool.core.collection.CollUtil;
+import com.citu.framework.common.util.object.BeanUtils;
+import com.citu.module.menduner.system.controller.app.recruit.user.vo.AppRecruitUserRespVO;
+import com.citu.module.menduner.system.controller.base.permission.permission.MdePermissionInfoRespVO;
+import com.citu.module.menduner.system.dal.dataobject.permission.MdeMenuDO;
+import com.citu.module.menduner.system.dal.dataobject.permission.MdeRoleDO;
+import com.citu.module.system.api.sms.dto.code.SmsCodeSendReqDTO;
+import com.citu.module.system.api.sms.dto.code.SmsCodeUseReqDTO;
+import com.citu.module.system.api.social.dto.SocialUserBindReqDTO;
+import com.citu.module.system.enums.permission.MenuTypeEnum;
+import org.mapstruct.Mapper;
+import org.mapstruct.factory.Mappers;
+import org.slf4j.LoggerFactory;
+
+import java.util.*;
+
+import static com.citu.framework.common.util.collection.CollectionUtils.convertSet;
+import static com.citu.framework.common.util.collection.CollectionUtils.filterList;
+import static com.citu.module.menduner.system.dal.dataobject.permission.MdeMenuDO.ID_ROOT;
+
+@Mapper
+public interface AuthConvert {
+
+    AuthConvert INSTANCE = Mappers.getMapper(AuthConvert.class);
+
+
+    default MdePermissionInfoRespVO convert(AppRecruitUserRespVO user, List<MdeRoleDO> roleList, List<MdeMenuDO> menuList) {
+        return MdePermissionInfoRespVO.builder()
+                .user(user)
+                .roles(convertSet(roleList, MdeRoleDO::getCode))
+                // 菜单树
+                .menus(buildMenuTree(menuList))
+                .build();
+    }
+
+    MdePermissionInfoRespVO.MenuVO convertTreeNode(MdeMenuDO menu);
+
+    /**
+     * 将菜单列表,构建成菜单树
+     *
+     * @param menuList 菜单列表
+     * @return 菜单树
+     */
+    default List<MdePermissionInfoRespVO.MenuVO> buildMenuTree(List<MdeMenuDO> menuList) {
+        if (CollUtil.isEmpty(menuList)) {
+            return Collections.emptyList();
+        }
+        // 移除按钮
+        menuList.removeIf(menu -> menu.getType().equals(MenuTypeEnum.BUTTON.getType()));
+        // 排序,保证菜单的有序性
+        menuList.sort(Comparator.comparing(MdeMenuDO::getSort));
+
+        // 构建菜单树
+        // 使用 LinkedHashMap 的原因,是为了排序 。实际也可以用 Stream API ,就是太丑了。
+        Map<Long, MdePermissionInfoRespVO.MenuVO> treeNodeMap = new LinkedHashMap<>();
+        menuList.forEach(menu -> treeNodeMap.put(menu.getId(), AuthConvert.INSTANCE.convertTreeNode(menu)));
+        // 处理父子关系
+        treeNodeMap.values().stream().filter(node -> !node.getParentId().equals(ID_ROOT)).forEach(childNode -> {
+            // 获得父节点
+            MdePermissionInfoRespVO.MenuVO parentNode = treeNodeMap.get(childNode.getParentId());
+            if (parentNode == null) {
+                LoggerFactory.getLogger(getClass()).error("[buildRouterTree][resource({}) 找不到父资源({})]",
+                        childNode.getId(), childNode.getParentId());
+                return;
+            }
+            // 将自己添加到父节点中
+            if (parentNode.getChildren() == null) {
+                parentNode.setChildren(new ArrayList<>());
+            }
+            parentNode.getChildren().add(childNode);
+        });
+        // 获得到所有的根节点
+        return filterList(treeNodeMap.values(), node -> ID_ROOT.equals(node.getParentId()));
+    }
+
+}

+ 107 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/dal/dataobject/permission/MdeMenuDO.java

@@ -0,0 +1,107 @@
+package com.citu.module.menduner.system.dal.dataobject.permission;
+
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.citu.framework.common.enums.CommonStatusEnum;
+import com.citu.framework.tenant.core.db.TenantBaseDO;
+import com.citu.module.system.enums.permission.MenuTypeEnum;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 菜单 DO
+ *
+ * @author ruoyi
+ */
+@TableName("mde_recruit_menu")
+@KeySequence("mde_recruit_menu_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class MdeMenuDO extends TenantBaseDO {
+
+    /**
+     * 菜单编号 - 根节点
+     */
+    public static final Long ID_ROOT = 0L;
+
+    /**
+     * 菜单编号
+     */
+    @TableId
+    private Long id;
+    /**
+     * 菜单名称
+     */
+    private String name;
+    /**
+     * 权限标识
+     * <p>
+     * 一般格式为:${系统}:${模块}:${操作}
+     * 例如说:system:admin:add,即 system 服务的添加管理员。
+     * <p>
+     * 当我们把该 MenuDO 赋予给角色后,意味着该角色有该资源:
+     * - 对于后端,配合 @PreAuthorize 注解,配置 API 接口需要该权限,从而对 API 接口进行权限控制。
+     * - 对于前端,配合前端标签,配置按钮是否展示,避免用户没有该权限时,结果可以看到该操作。
+     */
+    private String permission;
+    /**
+     * 菜单类型
+     * <p>
+     * 枚举 {@link MenuTypeEnum}
+     */
+    private Integer type;
+    /**
+     * 显示顺序
+     */
+    private Integer sort;
+    /**
+     * 父菜单ID
+     */
+    private Long parentId;
+    /**
+     * 路由地址
+     * <p>
+     * 如果 path 为 http(s) 时,则它是外链
+     */
+    private String path;
+    /**
+     * 菜单图标
+     */
+    private String icon;
+    /**
+     * 组件路径
+     */
+    private String component;
+    /**
+     * 组件名
+     */
+    private String componentName;
+    /**
+     * 状态
+     * <p>
+     * 枚举 {@link CommonStatusEnum}
+     */
+    private Integer status;
+    /**
+     * 是否可见
+     * <p>
+     * 只有菜单、目录使用
+     * 当设置为 true 时,该菜单不会展示在侧边栏,但是路由还是存在。例如说,一些独立的编辑页面 /edit/1024 等等
+     */
+    private Boolean visible;
+    /**
+     * 是否缓存
+     * <p>
+     * 只有菜单、目录使用,否使用 Vue 路由的 keep-alive 特性
+     * 注意:如果开启缓存,则必须填写 {@link #componentName} 属性,否则无法缓存
+     */
+    private Boolean keepAlive;
+    /**
+     * 是否总是显示
+     * <p>
+     * 如果为 false 时,当该菜单只有一个子菜单时,不展示自己,直接展示子菜单
+     */
+    private Boolean alwaysShow;
+
+}

+ 4 - 4
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/dal/dataobject/permission/MdeRoleDO.java

@@ -19,8 +19,8 @@ import java.util.Set;
  *
  * @author Rayson
  */
-@TableName(value = "mde_role", autoResultMap = true)
-@KeySequence("mde_role_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@TableName(value = "mde_recruit_role", autoResultMap = true)
+@KeySequence("mde_recruit_role_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
 @Data
 @EqualsAndHashCode(callSuper = true)
 public class MdeRoleDO extends TenantBaseDO {
@@ -68,11 +68,11 @@ public class MdeRoleDO extends TenantBaseDO {
      */
     private Integer dataScope;
     /**
-     * 数据范围(指定部门数组)
+     * 数据范围(指定企业数组)
      *
      * 适用于 {@link #dataScope} 的值为 {@link DataScopeEnum#DEPT_CUSTOM} 时
      */
     @TableField(typeHandler = JacksonTypeHandler.class)
-    private Set<Long> dataScopeDeptIds;
+    private Set<Long> dataScopeEnterpriseIds;
 
 }

+ 35 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/dal/dataobject/permission/MdeRoleMenuDO.java

@@ -0,0 +1,35 @@
+package com.citu.module.menduner.system.dal.dataobject.permission;
+
+import com.baomidou.mybatisplus.annotation.KeySequence;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.citu.framework.tenant.core.db.TenantBaseDO;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 角色和菜单关联
+ *
+ * @author ruoyi
+ */
+@TableName("mde_recruit_role_menu")
+@KeySequence("mde_recruit_role_menu_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class MdeRoleMenuDO extends TenantBaseDO {
+
+    /**
+     * 自增主键
+     */
+    @TableId
+    private Long id;
+    /**
+     * 角色ID
+     */
+    private Long roleId;
+    /**
+     * 菜单ID
+     */
+    private Long menuId;
+
+}

+ 6 - 2
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/dal/dataobject/permission/MdeUserRoleDO.java

@@ -13,8 +13,8 @@ import lombok.EqualsAndHashCode;
  *
  * @author ruoyi
  */
-@TableName("mde_user_role")
-@KeySequence("mde_user_role_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
+@TableName("mde_recruit_user_role")
+@KeySequence("mde_recruit_user_role_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
 @Data
 @EqualsAndHashCode(callSuper = true)
 public class MdeUserRoleDO extends TenantBaseDO {
@@ -24,6 +24,10 @@ public class MdeUserRoleDO extends TenantBaseDO {
      */
     @TableId
     private Long id;
+    /**
+     * 企业ID
+     */
+    private Long enterpriseId;
     /**
      * 用户 ID
      */

+ 31 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/dal/mysql/permission/MdeMenuMapper.java

@@ -0,0 +1,31 @@
+package com.citu.module.menduner.system.dal.mysql.permission;
+
+import com.citu.framework.mybatis.core.mapper.BaseMapperX;
+import com.citu.framework.mybatis.core.query.LambdaQueryWrapperX;
+import com.citu.module.menduner.system.controller.base.permission.menu.MdeMenuListReqVO;
+import com.citu.module.menduner.system.dal.dataobject.permission.MdeMenuDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+@Mapper
+public interface MdeMenuMapper extends BaseMapperX<MdeMenuDO> {
+
+    default MdeMenuDO selectByParentIdAndName(Long parentId, String name) {
+        return selectOne(MdeMenuDO::getParentId, parentId, MdeMenuDO::getName, name);
+    }
+
+    default Long selectCountByParentId(Long parentId) {
+        return selectCount(MdeMenuDO::getParentId, parentId);
+    }
+
+    default List<MdeMenuDO> selectList(MdeMenuListReqVO reqVO) {
+        return selectList(new LambdaQueryWrapperX<MdeMenuDO>()
+                .likeIfPresent(MdeMenuDO::getName, reqVO.getName())
+                .eqIfPresent(MdeMenuDO::getStatus, reqVO.getStatus()));
+    }
+
+    default List<MdeMenuDO> selectListByPermission(String permission) {
+        return selectList(MdeMenuDO::getPermission, permission);
+    }
+}

+ 40 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/dal/mysql/permission/MdeRoleMenuMapper.java

@@ -0,0 +1,40 @@
+package com.citu.module.menduner.system.dal.mysql.permission;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.citu.framework.mybatis.core.mapper.BaseMapperX;
+import com.citu.module.menduner.system.dal.dataobject.permission.MdeRoleMenuDO;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.Collection;
+import java.util.List;
+
+@Mapper
+public interface MdeRoleMenuMapper extends BaseMapperX<MdeRoleMenuDO> {
+
+    default List<MdeRoleMenuDO> selectListByRoleId(Long roleId) {
+        return selectList(MdeRoleMenuDO::getRoleId, roleId);
+    }
+
+    default List<MdeRoleMenuDO> selectListByRoleId(Collection<Long> roleIds) {
+        return selectList(MdeRoleMenuDO::getRoleId, roleIds);
+    }
+
+    default List<MdeRoleMenuDO> selectListByMenuId(Long menuId) {
+        return selectList(MdeRoleMenuDO::getMenuId, menuId);
+    }
+
+    default void deleteListByRoleIdAndMenuIds(Long roleId, Collection<Long> menuIds) {
+        delete(new LambdaQueryWrapper<MdeRoleMenuDO>()
+                .eq(MdeRoleMenuDO::getRoleId, roleId)
+                .in(MdeRoleMenuDO::getMenuId, menuIds));
+    }
+
+    default void deleteListByMenuId(Long menuId) {
+        delete(new LambdaQueryWrapper<MdeRoleMenuDO>().eq(MdeRoleMenuDO::getMenuId, menuId));
+    }
+
+    default void deleteListByRoleId(Long roleId) {
+        delete(new LambdaQueryWrapper<MdeRoleMenuDO>().eq(MdeRoleMenuDO::getRoleId, roleId));
+    }
+
+}

+ 16 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/dal/redis/RedisKeyConstants.java

@@ -32,6 +32,22 @@ public interface RedisKeyConstants {
      */
     String MDE_USER_ROLE_IDS = "mde_user_role_ids";
 
+    /**
+     * 拥有指定菜单的角色编号的缓存
+     * <p>
+     * KEY 格式:mde_user_role_ids:{menuId}
+     * VALUE 数据类型:String 角色编号集合
+     */
+    String MDE_MENU_ROLE_ID_LIST = "mde_menu_role_ids";
+
+    /**
+     * 拥有权限对应的菜单编号数组的缓存
+     * <p>
+     * KEY 格式:mde_permission_menu_ids:{permission}
+     * VALUE 数据类型:String 菜单编号数组
+     */
+    String MDE_PERMISSION_MENU_ID_LIST = "mde_permission_menu_ids";
+
     /**
      * 指定区域的所有子区域信息的缓存
      * <p>

+ 97 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/permission/MdeMenuService.java

@@ -0,0 +1,97 @@
+package com.citu.module.menduner.system.service.permission;
+
+
+
+import com.citu.module.menduner.system.controller.base.permission.menu.MdeMenuListReqVO;
+import com.citu.module.menduner.system.controller.base.permission.menu.MdeMenuSaveVO;
+import com.citu.module.menduner.system.dal.dataobject.permission.MdeMenuDO;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 菜单 Service 接口
+ *
+ * @author Rayson
+ */
+public interface MdeMenuService {
+
+    /**
+     * 创建菜单
+     *
+     * @param createReqVO 菜单信息
+     * @return 创建出来的菜单编号
+     */
+    Long createMenu(MdeMenuSaveVO createReqVO);
+
+    /**
+     * 更新菜单
+     *
+     * @param updateReqVO 菜单信息
+     */
+    void updateMenu(MdeMenuSaveVO updateReqVO);
+
+    /**
+     * 删除菜单
+     *
+     * @param id 菜单编号
+     */
+    void deleteMenu(Long id);
+
+    /**
+     * 获得所有菜单列表
+     *
+     * @return 菜单列表
+     */
+    List<MdeMenuDO> getMenuList();
+
+    /**
+     * 基于租户,筛选菜单列表
+     * 注意,如果是系统租户,返回的还是全菜单
+     *
+     * @param reqVO 筛选条件请求 VO
+     * @return 菜单列表
+     */
+    List<MdeMenuDO> getMenuListByTenant(MdeMenuListReqVO reqVO);
+
+    /**
+     * 过滤掉关闭的菜单及其子菜单
+     *
+     * @param list 菜单列表
+     * @return 过滤后的菜单列表
+     */
+    List<MdeMenuDO> filterDisableMenus(List<MdeMenuDO> list);
+
+    /**
+     * 筛选菜单列表
+     *
+     * @param reqVO 筛选条件请求 VO
+     * @return 菜单列表
+     */
+    List<MdeMenuDO> getMenuList(MdeMenuListReqVO reqVO);
+
+    /**
+     * 获得权限对应的菜单编号数组
+     *
+     * @param permission 权限标识
+     * @return 数组
+     */
+    List<Long> getMenuIdListByPermissionFromCache(String permission);
+
+    /**
+     * 获得菜单
+     *
+     * @param id 菜单编号
+     * @return 菜单
+     */
+    MdeMenuDO getMenu(Long id);
+
+    /**
+     * 获得菜单数组
+     *
+     * @param ids 菜单编号数组
+     * @return 菜单数组
+     */
+    List<MdeMenuDO> getMenuList(Collection<Long> ids);
+
+}

+ 255 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/permission/MdeMenuServiceImpl.java

@@ -0,0 +1,255 @@
+package com.citu.module.menduner.system.service.permission;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjUtil;
+import com.citu.framework.common.enums.CommonStatusEnum;
+import com.citu.framework.common.util.object.BeanUtils;
+import com.citu.module.menduner.system.controller.base.permission.menu.MdeMenuListReqVO;
+import com.citu.module.menduner.system.controller.base.permission.menu.MdeMenuSaveVO;
+import com.citu.module.menduner.system.dal.dataobject.permission.MdeMenuDO;
+import com.citu.module.menduner.system.dal.mysql.permission.MdeMenuMapper;
+import com.citu.module.menduner.system.dal.redis.RedisKeyConstants;
+import com.citu.module.system.enums.permission.MenuTypeEnum;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.Lists;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.cache.annotation.CacheEvict;
+import org.springframework.cache.annotation.Cacheable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.annotation.Resource;
+import java.util.*;
+
+import static com.citu.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static com.citu.framework.common.util.collection.CollectionUtils.convertList;
+import static com.citu.framework.common.util.collection.CollectionUtils.convertMap;
+import static com.citu.module.menduner.system.dal.dataobject.permission.MdeMenuDO.ID_ROOT;
+import static com.citu.module.system.enums.ErrorCodeConstants.*;
+
+/**
+ * 菜单 Service 实现
+ *
+ * @author Rayson
+ */
+@Service
+@Slf4j
+public class MdeMenuServiceImpl implements MdeMenuService {
+
+    @Resource
+    private MdeMenuMapper menuMapper;
+    @Resource
+    private MdePermissionService permissionService;
+
+    @Override
+    @CacheEvict(value = RedisKeyConstants.MDE_PERMISSION_MENU_ID_LIST, key = "#createReqVO.permission",
+            condition = "#createReqVO.permission != null")
+    public Long createMenu(MdeMenuSaveVO createReqVO) {
+        // 校验父菜单存在
+        validateParentMenu(createReqVO.getParentId(), null);
+        // 校验菜单(自己)
+        validateMenu(createReqVO.getParentId(), createReqVO.getName(), null);
+
+        // 插入数据库
+        MdeMenuDO menu = BeanUtils.toBean(createReqVO, MdeMenuDO.class);
+        initMenuProperty(menu);
+        menuMapper.insert(menu);
+        // 返回
+        return menu.getId();
+    }
+
+    @Override
+    @CacheEvict(value = RedisKeyConstants.MDE_PERMISSION_MENU_ID_LIST,
+            allEntries = true) // allEntries 清空所有缓存,因为 permission 如果变更,涉及到新老两个 permission。直接清理,简单有效
+    public void updateMenu(MdeMenuSaveVO updateReqVO) {
+        // 校验更新的菜单是否存在
+        if (menuMapper.selectById(updateReqVO.getId()) == null) {
+            throw exception(MENU_NOT_EXISTS);
+        }
+        // 校验父菜单存在
+        validateParentMenu(updateReqVO.getParentId(), updateReqVO.getId());
+        // 校验菜单(自己)
+        validateMenu(updateReqVO.getParentId(), updateReqVO.getName(), updateReqVO.getId());
+
+        // 更新到数据库
+        MdeMenuDO updateObj = BeanUtils.toBean(updateReqVO, MdeMenuDO.class);
+        initMenuProperty(updateObj);
+        menuMapper.updateById(updateObj);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    @CacheEvict(value = RedisKeyConstants.MDE_PERMISSION_MENU_ID_LIST,
+            allEntries = true) // allEntries 清空所有缓存,因为此时不知道 id 对应的 permission 是多少。直接清理,简单有效
+    public void deleteMenu(Long id) {
+        // 校验是否还有子菜单
+        if (menuMapper.selectCountByParentId(id) > 0) {
+            throw exception(MENU_EXISTS_CHILDREN);
+        }
+        // 校验删除的菜单是否存在
+        if (menuMapper.selectById(id) == null) {
+            throw exception(MENU_NOT_EXISTS);
+        }
+        // 标记删除
+        menuMapper.deleteById(id);
+        // 删除授予给角色的权限
+        permissionService.processMenuDeleted(id);
+    }
+
+    @Override
+    public List<MdeMenuDO> getMenuList() {
+        return menuMapper.selectList();
+    }
+
+    @Override
+    public List<MdeMenuDO> getMenuListByTenant(MdeMenuListReqVO reqVO) {
+        // 查询所有菜单,并过滤掉关闭的节点
+        List<MdeMenuDO> menus = getMenuList(reqVO);
+        return menus;
+    }
+
+    @Override
+    public List<MdeMenuDO> filterDisableMenus(List<MdeMenuDO> menuList) {
+        if (CollUtil.isEmpty(menuList)) {
+            return Collections.emptyList();
+        }
+        Map<Long, MdeMenuDO> menuMap = convertMap(menuList, MdeMenuDO::getId);
+
+        // 遍历 menu 菜单,查找不是禁用的菜单,添加到 enabledMenus 结果
+        List<MdeMenuDO> enabledMenus = new ArrayList<>();
+        Set<Long> disabledMenuCache = new HashSet<>(); // 存下递归搜索过被禁用的菜单,防止重复的搜索
+        for (MdeMenuDO menu : menuList) {
+            if (isMenuDisabled(menu, menuMap, disabledMenuCache)) {
+                continue;
+            }
+            enabledMenus.add(menu);
+        }
+        return enabledMenus;
+    }
+
+    private boolean isMenuDisabled(MdeMenuDO node, Map<Long, MdeMenuDO> menuMap, Set<Long> disabledMenuCache) {
+        // 如果已经判定是禁用的节点,直接结束
+        if (disabledMenuCache.contains(node.getId())) {
+            return true;
+        }
+
+        // 1. 先判断自身是否禁用
+        if (CommonStatusEnum.isDisable(node.getStatus())) {
+            disabledMenuCache.add(node.getId());
+            return true;
+        }
+
+        // 2. 遍历到 parentId 为根节点,则无需判断
+        Long parentId = node.getParentId();
+        if (ObjUtil.equal(parentId, ID_ROOT)) {
+            return false;
+        }
+
+        // 3. 继续遍历 parent 节点
+        MdeMenuDO parent = menuMap.get(parentId);
+        if (parent == null || isMenuDisabled(parent, menuMap, disabledMenuCache)) {
+            disabledMenuCache.add(node.getId());
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public List<MdeMenuDO> getMenuList(MdeMenuListReqVO reqVO) {
+        return menuMapper.selectList(reqVO);
+    }
+
+    @Override
+    @Cacheable(value = RedisKeyConstants.MDE_PERMISSION_MENU_ID_LIST, key = "#permission")
+    public List<Long> getMenuIdListByPermissionFromCache(String permission) {
+        List<MdeMenuDO> menus = menuMapper.selectListByPermission(permission);
+        return convertList(menus, MdeMenuDO::getId);
+    }
+
+    @Override
+    public MdeMenuDO getMenu(Long id) {
+        return menuMapper.selectById(id);
+    }
+
+    @Override
+    public List<MdeMenuDO> getMenuList(Collection<Long> ids) {
+        // 当 ids 为空时,返回一个空的实例对象
+        if (CollUtil.isEmpty(ids)) {
+            return Lists.newArrayList();
+        }
+        return menuMapper.selectBatchIds(ids);
+    }
+
+    /**
+     * 校验父菜单是否合法
+     * <p>
+     * 1. 不能设置自己为父菜单
+     * 2. 父菜单不存在
+     * 3. 父菜单必须是 {@link MenuTypeEnum#MENU} 菜单类型
+     *
+     * @param parentId 父菜单编号
+     * @param childId  当前菜单编号
+     */
+    @VisibleForTesting
+    void validateParentMenu(Long parentId, Long childId) {
+        if (parentId == null || ID_ROOT.equals(parentId)) {
+            return;
+        }
+        // 不能设置自己为父菜单
+        if (parentId.equals(childId)) {
+            throw exception(MENU_PARENT_ERROR);
+        }
+        MdeMenuDO menu = menuMapper.selectById(parentId);
+        // 父菜单不存在
+        if (menu == null) {
+            throw exception(MENU_PARENT_NOT_EXISTS);
+        }
+        // 父菜单必须是目录或者菜单类型
+        if (!MenuTypeEnum.DIR.getType().equals(menu.getType())
+                && !MenuTypeEnum.MENU.getType().equals(menu.getType())) {
+            throw exception(MENU_PARENT_NOT_DIR_OR_MENU);
+        }
+    }
+
+    /**
+     * 校验菜单是否合法
+     * <p>
+     * 1. 校验相同父菜单编号下,是否存在相同的菜单名
+     *
+     * @param name     菜单名字
+     * @param parentId 父菜单编号
+     * @param id       菜单编号
+     */
+    @VisibleForTesting
+    void validateMenu(Long parentId, String name, Long id) {
+        MdeMenuDO menu = menuMapper.selectByParentIdAndName(parentId, name);
+        if (menu == null) {
+            return;
+        }
+        // 如果 id 为空,说明不用比较是否为相同 id 的菜单
+        if (id == null) {
+            throw exception(MENU_NAME_DUPLICATE);
+        }
+        if (!menu.getId().equals(id)) {
+            throw exception(MENU_NAME_DUPLICATE);
+        }
+    }
+
+    /**
+     * 初始化菜单的通用属性。
+     * <p>
+     * 例如说,只有目录或者菜单类型的菜单,才设置 icon
+     *
+     * @param menu 菜单
+     */
+    private void initMenuProperty(MdeMenuDO menu) {
+        // 菜单为按钮类型时,无需 component、icon、path 属性,进行置空
+        if (MenuTypeEnum.BUTTON.getType().equals(menu.getType())) {
+            menu.setComponent("");
+            menu.setComponentName("");
+            menu.setIcon("");
+            menu.setPath("");
+        }
+    }
+
+}

+ 48 - 11
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/permission/MdePermissionService.java

@@ -6,9 +6,10 @@ import com.citu.module.system.api.permission.dto.DataPermissionRespDTO;
 import java.util.Collection;
 import java.util.Set;
 
+import static java.util.Collections.singleton;
+
 /**
- * @author Rayson
- * @description MdePermissionService 权限 Service 接口
+ * MdePermissionService 权限 Service 接口
  * 提供用户-角色、角色-企业的关联权限处理
  **/
 public interface MdePermissionService {
@@ -30,7 +31,15 @@ public interface MdePermissionService {
      */
     boolean hasAnyRoles(Long userId, String... roles);
 
-    // ========== 用户-角色的相关方法  ==========
+    // ========== 角色-菜单的相关方法  ==========
+
+    /**
+     * 设置角色菜单
+     *
+     * @param roleId  角色编号
+     * @param menuIds 菜单编号集合
+     */
+    void assignRoleMenu(Long roleId, Set<Long> menuIds);
 
     /**
      * 处理角色删除时,删除关联授权数据
@@ -39,6 +48,41 @@ public interface MdePermissionService {
      */
     void processRoleDeleted(Long roleId);
 
+    /**
+     * 处理菜单删除时,删除关联授权数据
+     *
+     * @param menuId 菜单编号
+     */
+    void processMenuDeleted(Long menuId);
+
+    /**
+     * 获得角色拥有的菜单编号集合
+     *
+     * @param roleId 角色编号
+     * @return 菜单编号集合
+     */
+    default Set<Long> getRoleMenuListByRoleId(Long roleId) {
+        return getRoleMenuListByRoleId(singleton(roleId));
+    }
+
+    /**
+     * 获得角色们拥有的菜单编号集合
+     *
+     * @param roleIds 角色编号数组
+     * @return 菜单编号集合
+     */
+    Set<Long> getRoleMenuListByRoleId(Collection<Long> roleIds);
+
+    /**
+     * 获得拥有指定菜单的角色编号数组,从缓存中获取
+     *
+     * @param menuId 菜单编号
+     * @return 角色编号数组
+     */
+    Set<Long> getMenuRoleIdListByMenuIdFromCache(Long menuId);
+
+    // ========== 用户-角色的相关方法  ==========
+
     /**
      * 设置用户角色
      *
@@ -97,18 +141,11 @@ public interface MdePermissionService {
      */
     void assignRoleDataScope(Long roleId, Integer dataScope, Set<Long> dataScopeDataIds);
 
-    /**
-     * 获得登陆用户的部门数据权限
-     *
-     * @param userId 用户编号
-     * @return 部门数据权限
-     */
-    DataPermissionRespDTO getDataPermission(Long userId);
 
     /**
      * 获得登陆用户的部门数据权限
      *
-     * @param userId 用户编号
+     * @param userId       用户编号
      * @param enterpriseId 企业id
      * @return 部门数据权限
      */

+ 157 - 59
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/permission/MdePermissionServiceImpl.java

@@ -5,15 +5,18 @@ import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.util.ArrayUtil;
 import cn.hutool.extra.spring.SpringUtil;
 import com.baomidou.dynamic.datasource.annotation.DSTransactional;
+import com.citu.framework.common.enums.CommonStatusEnum;
 import com.citu.framework.common.util.collection.CollectionUtils;
 import com.citu.framework.datapermission.core.annotation.DataPermission;
 import com.citu.module.menduner.system.dal.dataobject.enterprise.EnterpriseUserBindDO;
+import com.citu.module.menduner.system.dal.dataobject.permission.MdeMenuDO;
 import com.citu.module.menduner.system.dal.dataobject.permission.MdeRoleDO;
+import com.citu.module.menduner.system.dal.dataobject.permission.MdeRoleMenuDO;
 import com.citu.module.menduner.system.dal.dataobject.permission.MdeUserRoleDO;
 import com.citu.module.menduner.system.dal.dataobject.user.MdeUserDO;
+import com.citu.module.menduner.system.dal.mysql.permission.MdeRoleMenuMapper;
 import com.citu.module.menduner.system.dal.mysql.permission.MdeUserRoleMapper;
 import com.citu.module.menduner.system.dal.redis.RedisKeyConstants;
-import com.citu.module.menduner.system.enums.MendunerStatusEnum;
 import com.citu.module.menduner.system.enums.permission.MdeDefaultRoleEnum;
 import com.citu.module.menduner.system.service.enterprise.EnterpriseService;
 import com.citu.module.menduner.system.service.enterprise.bind.EnterpriseUserBindService;
@@ -28,12 +31,10 @@ import org.springframework.cache.annotation.Cacheable;
 import org.springframework.cache.annotation.Caching;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Resource;
-import java.util.Collection;
-import java.util.List;
-import java.util.Objects;
-import java.util.Set;
+import java.util.*;
 
 import static com.citu.framework.common.util.collection.CollectionUtils.convertSet;
 import static com.citu.framework.common.util.json.JsonUtils.toJsonString;
@@ -48,11 +49,18 @@ import static com.citu.framework.common.util.json.JsonUtils.toJsonString;
 public class MdePermissionServiceImpl implements MdePermissionService {
 
     @Resource
-    private MdeUserRoleMapper mdeUserRoleMapper;
+    private MdeRoleMenuMapper roleMenuMapper;
+
+    @Resource
+    private MdeUserRoleMapper userRoleMapper;
 
     @Resource
     @Lazy
-    private MdeRoleService mdeRoleService;
+    private MdeRoleService roleService;
+
+    @Resource
+    @Lazy
+    private MdeMenuService menuService;
 
     @Resource
     @Lazy
@@ -79,35 +87,42 @@ public class MdePermissionServiceImpl implements MdePermissionService {
             return false;
         }
 
+        // 情况一:遍历判断每个权限,如果有一满足,说明有权限
+        for (String permission : permissions) {
+            if (hasAnyPermission(roles, permission)) {
+                return true;
+            }
+        }
 
         // 情况二:如果是超管,也说明有权限
-        return mdeRoleService.hasAnySuperAdmin(convertSet(roles, MdeRoleDO::getId));
+        return roleService.hasAnySuperAdmin(convertSet(roles, MdeRoleDO::getId));
     }
 
-
     /**
-     * 获得自身的代理对象,解决 AOP 生效问题
+     * 判断指定角色,是否拥有该 permission 权限
      *
-     * @return 自己
+     * @param roles      指定角色数组
+     * @param permission 权限标识
+     * @return 是否拥有
      */
-    private MdePermissionServiceImpl getSelf() {
-        return SpringUtil.getBean(getClass());
-    }
+    private boolean hasAnyPermission(List<MdeRoleDO> roles, String permission) {
+        List<Long> menuIds = menuService.getMenuIdListByPermissionFromCache(permission);
+        // 采用严格模式,如果权限找不到对应的 Menu 的话,也认为没有权限
+        if (CollUtil.isEmpty(menuIds)) {
+            return false;
+        }
 
-    /**
-     * 获得用户拥有的角色,并且这些角色是开启状态的
-     *
-     * @param userId 用户编号
-     * @return 用户拥有的角色
-     */
-    @VisibleForTesting
-    List<MdeRoleDO> getEnableUserRoleListByUserIdFromCache(Long userId) {
-        // 获得用户拥有的角色编号
-        Set<Long> roleIds = getSelf().getUserRoleIdListByUserIdFromCache(userId);
-        // 获得角色数组,并移除被禁用的
-        List<MdeRoleDO> roles = mdeRoleService.getRoleListFromCache(roleIds);
-        roles.removeIf(role -> !MendunerStatusEnum.ENABLE.getStatus().equals(role.getStatus()));
-        return roles;
+        // 判断是否有权限
+        Set<Long> roleIds = convertSet(roles, MdeRoleDO::getId);
+        for (Long menuId : menuIds) {
+            // 获得拥有该菜单的角色编号集合
+            Set<Long> menuRoleIds = getSelf().getMenuRoleIdListByMenuIdFromCache(menuId);
+            // 如果有交集,说明有权限
+            if (CollUtil.containsAny(menuRoleIds, roleIds)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     @Override
@@ -128,12 +143,86 @@ public class MdePermissionServiceImpl implements MdePermissionService {
         return CollUtil.containsAny(userRoles, Sets.newHashSet(roles));
     }
 
+    // ========== 角色-菜单的相关方法  ==========
+
+    @Override
+    @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换
+    @Caching(evict = {
+            @CacheEvict(value = RedisKeyConstants.MDE_MENU_ROLE_ID_LIST,
+                    allEntries = true),
+            @CacheEvict(value = RedisKeyConstants.MDE_PERMISSION_MENU_ID_LIST,
+                    allEntries = true) // allEntries 清空所有缓存,主要一次更新涉及到的 menuIds 较多,反倒批量会更快
+    })
+    public void assignRoleMenu(Long roleId, Set<Long> menuIds) {
+        // 获得角色拥有菜单编号
+        Set<Long> dbMenuIds = convertSet(roleMenuMapper.selectListByRoleId(roleId), MdeRoleMenuDO::getMenuId);
+        // 计算新增和删除的菜单编号
+        Set<Long> menuIdList = CollUtil.emptyIfNull(menuIds);
+        Collection<Long> createMenuIds = CollUtil.subtract(menuIdList, dbMenuIds);
+        Collection<Long> deleteMenuIds = CollUtil.subtract(dbMenuIds, menuIdList);
+        // 执行新增和删除。对于已经授权的菜单,不用做任何处理
+        if (CollUtil.isNotEmpty(createMenuIds)) {
+            roleMenuMapper.insertBatch(CollectionUtils.convertList(createMenuIds, menuId -> {
+                MdeRoleMenuDO entity = new MdeRoleMenuDO();
+                entity.setRoleId(roleId);
+                entity.setMenuId(menuId);
+                return entity;
+            }));
+        }
+        if (CollUtil.isNotEmpty(deleteMenuIds)) {
+            roleMenuMapper.deleteListByRoleIdAndMenuIds(roleId, deleteMenuIds);
+        }
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    @Caching(evict = {
+            @CacheEvict(value = RedisKeyConstants.MDE_MENU_ROLE_ID_LIST,
+                    allEntries = true), // allEntries 清空所有缓存,此处无法方便获得 roleId 对应的 menu 缓存们
+            @CacheEvict(value = RedisKeyConstants.MDE_USER_ROLE_IDS,
+                    allEntries = true) // allEntries 清空所有缓存,此处无法方便获得 roleId 对应的 user 缓存们
+    })
+    public void processRoleDeleted(Long roleId) {
+        // 标记删除 UserRole
+        userRoleMapper.deleteListByRoleId(roleId);
+        // 标记删除 RoleMenu
+        roleMenuMapper.deleteListByRoleId(roleId);
+    }
+
+    @Override
+    @CacheEvict(value = RedisKeyConstants.MDE_MENU_ROLE_ID_LIST, key = "#menuId")
+    public void processMenuDeleted(Long menuId) {
+        roleMenuMapper.deleteListByMenuId(menuId);
+    }
+
+    @Override
+    public Set<Long> getRoleMenuListByRoleId(Collection<Long> roleIds) {
+        if (CollUtil.isEmpty(roleIds)) {
+            return Collections.emptySet();
+        }
+
+        // 如果是管理员的情况下,获取全部菜单编号
+        if (roleService.hasAnySuperAdmin(roleIds)) {
+            return convertSet(menuService.getMenuList(), MdeMenuDO::getId);
+        }
+        // 如果是非管理员的情况下,获得拥有的菜单编号
+        return convertSet(roleMenuMapper.selectListByRoleId(roleIds), MdeRoleMenuDO::getMenuId);
+    }
+
+    @Override
+    @Cacheable(value = RedisKeyConstants.MDE_MENU_ROLE_ID_LIST, key = "#menuId")
+    public Set<Long> getMenuRoleIdListByMenuIdFromCache(Long menuId) {
+        return convertSet(roleMenuMapper.selectListByMenuId(menuId), MdeRoleMenuDO::getRoleId);
+    }
+
+    // ========== 用户-角色的相关方法  ==========
+
     @Override
     @DSTransactional // 多数据源,使用 @DSTransactional 保证本地事务,以及数据源的切换
     @CacheEvict(value = RedisKeyConstants.MDE_USER_ROLE_IDS, key = "#userId")
     public void assignUserRole(Long userId, Set<Long> roleIds) {
         // 获得角色拥有角色编号
-        Set<Long> dbRoleIds = convertSet(mdeUserRoleMapper.selectListByUserId(userId),
+        Set<Long> dbRoleIds = convertSet(userRoleMapper.selectListByUserId(userId),
                 MdeUserRoleDO::getRoleId);
         // 计算新增和删除的角色编号
         Set<Long> roleIdList = CollUtil.emptyIfNull(roleIds);
@@ -141,7 +230,7 @@ public class MdePermissionServiceImpl implements MdePermissionService {
         Collection<Long> deleteMenuIds = CollUtil.subtract(dbRoleIds, roleIdList);
         // 执行新增和删除。对于已经授权的角色,不用做任何处理
         if (!CollectionUtil.isEmpty(createRoleIds)) {
-            mdeUserRoleMapper.insertBatch(CollectionUtils.convertList(createRoleIds, roleId -> {
+            userRoleMapper.insertBatch(CollectionUtils.convertList(createRoleIds, roleId -> {
                 MdeUserRoleDO entity = new MdeUserRoleDO();
                 entity.setUserId(userId);
                 entity.setRoleId(roleId);
@@ -149,49 +238,31 @@ public class MdePermissionServiceImpl implements MdePermissionService {
             }));
         }
         if (!CollectionUtil.isEmpty(deleteMenuIds)) {
-            mdeUserRoleMapper.deleteListByUserIdAndRoleIdIds(userId, deleteMenuIds);
+            userRoleMapper.deleteListByUserIdAndRoleIdIds(userId, deleteMenuIds);
         }
     }
 
     @Override
-    @DSTransactional // 单机+多数据源方案,使用 @DSTransactional 保证本地事务,以及数据源的切换
     public void assignUserRoleByDefault(Long userId, MdeDefaultRoleEnum roleEnum) {
         // 获得默认角色信息
-        MdeRoleDO role = mdeRoleService.getRoleByDefault(roleEnum);
+        MdeRoleDO role = roleService.getRoleByDefault(roleEnum);
         MdeUserRoleDO entity = new MdeUserRoleDO();
         entity.setUserId(userId);
         entity.setRoleId(role.getId());
-        mdeUserRoleMapper.insert(entity);
-    }
-
-    @Override
-    @DSTransactional // 单机+多数据源方案,使用 @DSTransactional 保证本地事务,以及数据源的切换
-    @Caching(evict = {
-            @CacheEvict(value = RedisKeyConstants.MDE_USER_ROLE_IDS,
-                    allEntries = true) // allEntries 清空所有缓存,此处无法方便获得 roleId 对应的 user 缓存们
-    })
-    public void processRoleDeleted(Long roleId) {
-        // 标记删除 MdeUserRole
-        mdeUserRoleMapper.deleteListByRoleId(roleId);
+        userRoleMapper.insert(entity);
     }
 
     @Override
     @CacheEvict(value = RedisKeyConstants.MDE_USER_ROLE_IDS, key = "#userId")
     public void processUserDeleted(Long userId) {
-        mdeUserRoleMapper.deleteListByUserId(userId);
-    }
-
-    @Override
-    public Set<Long> getUserRoleIdListByRoleId(Collection<Long> roleIds) {
-        return convertSet(mdeUserRoleMapper.selectListByRoleIds(roleIds), MdeUserRoleDO::getUserId);
+        userRoleMapper.deleteListByUserId(userId);
     }
 
     @Override
     public Set<Long> getUserRoleIdListByUserId(Long userId) {
-        return convertSet(mdeUserRoleMapper.selectListByUserId(userId), MdeUserRoleDO::getRoleId);
+        return convertSet(userRoleMapper.selectListByUserId(userId), MdeUserRoleDO::getRoleId);
     }
 
-
     @Override
     @Cacheable(value = RedisKeyConstants.MDE_USER_ROLE_IDS, key = "#userId")
     public Set<Long> getUserRoleIdListByUserIdFromCache(Long userId) {
@@ -199,14 +270,31 @@ public class MdePermissionServiceImpl implements MdePermissionService {
     }
 
     @Override
-    public void assignRoleDataScope(Long roleId, Integer dataScope, Set<Long> dataScopeDataIds) {
-        mdeRoleService.updateRoleDataScope(roleId, dataScope, dataScopeDataIds);
+    public Set<Long> getUserRoleIdListByRoleId(Collection<Long> roleIds) {
+        return convertSet(userRoleMapper.selectListByRoleIds(roleIds), MdeUserRoleDO::getUserId);
+    }
+
+    /**
+     * 获得用户拥有的角色,并且这些角色是开启状态的
+     *
+     * @param userId 用户编号
+     * @return 用户拥有的角色
+     */
+    @VisibleForTesting
+    List<MdeRoleDO> getEnableUserRoleListByUserIdFromCache(Long userId) {
+        // 获得用户拥有的角色编号
+        Set<Long> roleIds = getSelf().getUserRoleIdListByUserIdFromCache(userId);
+        // 获得角色数组,并移除被禁用的
+        List<MdeRoleDO> roles = roleService.getRoleListFromCache(roleIds);
+        roles.removeIf(role -> !CommonStatusEnum.ENABLE.getStatus().equals(role.getStatus()));
+        return roles;
     }
 
+    // ========== 用户-部门的相关方法  ==========
+
     @Override
-    @DataPermission(enable = false) // 关闭数据权限,不然就会出现递归获取数据权限的问题
-    public DataPermissionRespDTO getDataPermission(Long userId) {
-        return getDataPermission(userId, null);
+    public void assignRoleDataScope(Long roleId, Integer dataScope, Set<Long> dataScopeDataIds) {
+        roleService.updateRoleDataScope(roleId, dataScope, dataScopeDataIds);
     }
 
     @Override
@@ -247,7 +335,7 @@ public class MdePermissionServiceImpl implements MdePermissionService {
             }
             // 情况二,DEPT_CUSTOM 自定义
             if (Objects.equals(role.getDataScope(), DataScopeEnum.DEPT_CUSTOM.getScope())) {
-                CollUtil.addAll(result.getDataIds(), role.getDataScopeDeptIds());
+                CollUtil.addAll(result.getDataIds(), role.getDataScopeEnterpriseIds());
                 // 自定义可见部门时,保证可以看到自己所在的部门。否则,一些场景下可能会有问题。
                 // 例如说,登录时,基于 t_user 的 username 查询会可能被 dept_id 过滤掉
                 CollUtil.addAll(result.getDataIds(), enterpriseUserBind.getEnterpriseId());
@@ -283,4 +371,14 @@ public class MdePermissionServiceImpl implements MdePermissionService {
         }
         return result;
     }
+
+
+    /**
+     * 获得自身的代理对象,解决 AOP 生效问题
+     *
+     * @return 自己
+     */
+    private MdePermissionServiceImpl getSelf() {
+        return SpringUtil.getBean(getClass());
+    }
 }

+ 2 - 2
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/permission/MdeRoleService.java

@@ -47,9 +47,9 @@ public interface MdeRoleService {
      *
      * @param id               角色编号
      * @param dataScope        数据范围
-     * @param dataScopeDeptIds 部门编号数组
+     * @param dataScopeEnterpriseIds 部门编号数组
      */
-    void updateRoleDataScope(Long id, Integer dataScope, Set<Long> dataScopeDeptIds);
+    void updateRoleDataScope(Long id, Integer dataScope, Set<Long> dataScopeEnterpriseIds);
 
     /**
      * 获得角色

+ 2 - 2
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/permission/MdeRoleServiceImpl.java

@@ -93,7 +93,7 @@ public class MdeRoleServiceImpl implements MdeRoleService {
 
     @Override
     @CacheEvict(value = RedisKeyConstants.MDE_ROLE, key = "#id")
-    public void updateRoleDataScope(Long id, Integer dataScope, Set<Long> dataScopeDeptIds) {
+    public void updateRoleDataScope(Long id, Integer dataScope, Set<Long> dataScopeEnterpriseIds) {
         // 校验是否可以更新
         validateRoleForUpdate(id);
 
@@ -101,7 +101,7 @@ public class MdeRoleServiceImpl implements MdeRoleService {
         MdeRoleDO updateObject = new MdeRoleDO();
         updateObject.setId(id);
         updateObject.setDataScope(dataScope);
-        updateObject.setDataScopeDeptIds(dataScopeDeptIds);
+        updateObject.setDataScopeEnterpriseIds(dataScopeEnterpriseIds);
         roleMapper.updateById(updateObject);
     }
 

+ 3 - 1
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/person/PersonIntegrationService.java

@@ -3,6 +3,7 @@ package com.citu.module.menduner.system.service.person;
 import com.citu.framework.common.pojo.PageResult;
 import com.citu.module.menduner.system.controller.app.jobhunt.person.vo.AppResumeSaveReqVO;
 import com.citu.module.menduner.system.controller.app.recruit.person.vo.AppRecruitPersonDetailRespVO;
+import com.citu.module.menduner.system.controller.app.recruit.person.vo.AppRecruitPersonInfoRespVO;
 import com.citu.module.menduner.system.controller.base.person.PersonMapQueryReqVO;
 import com.citu.module.menduner.system.controller.base.person.PersonQueryReqVO;
 import com.citu.module.menduner.system.controller.base.person.PersonRecommendQueryReqVO;
@@ -74,7 +75,8 @@ public interface PersonIntegrationService {
      * @param reqVO 查询条件
      * @return PageResult<PersonInfoRespVO>
      **/
-    PageResult<PersonInfoRespVO> queryPage(PersonQueryReqVO reqVO);
+    PageResult<AppRecruitPersonInfoRespVO> queryPage(PersonQueryReqVO reqVO);
+
 
     /**
      * 人才信息-根据发布职位推荐人才

+ 25 - 37
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/person/PersonIntegrationServiceImpl.java

@@ -2,6 +2,7 @@ package com.citu.module.menduner.system.service.person;
 
 import cn.hutool.core.collection.CollUtil;
 import com.citu.framework.common.pojo.PageResult;
+import com.citu.framework.common.util.object.BeanUtils;
 import com.citu.framework.dict.core.DictFrameworkUtils;
 import com.citu.module.menduner.common.util.LoginUserContext;
 import com.citu.module.menduner.system.api.python.GraphApi;
@@ -9,6 +10,7 @@ import com.citu.module.menduner.system.api.python.GraphQueryDTO;
 import com.citu.module.menduner.system.api.python.GraphQueryPageDTO;
 import com.citu.module.menduner.system.api.python.GraphSendDTO;
 import com.citu.module.menduner.system.controller.app.recruit.person.vo.AppRecruitPersonDetailRespVO;
+import com.citu.module.menduner.system.controller.app.recruit.person.vo.AppRecruitPersonInfoRespVO;
 import com.citu.module.menduner.system.controller.base.person.PersonMapQueryReqVO;
 import com.citu.module.menduner.system.controller.base.person.PersonQueryReqVO;
 import com.citu.module.menduner.system.controller.base.person.PersonRecommendQueryReqVO;
@@ -40,6 +42,7 @@ import com.citu.module.menduner.system.enums.sync.SyncConstants;
 import com.citu.module.menduner.system.mq.producer.GraphProducer;
 import com.citu.module.menduner.system.service.area.AreaService;
 import com.citu.module.menduner.system.util.RedisUtils;
+import org.jetbrains.annotations.NotNull;
 import org.springframework.stereotype.Service;
 import org.springframework.util.StringUtils;
 import org.springframework.validation.annotation.Validated;
@@ -166,10 +169,10 @@ public class PersonIntegrationServiceImpl implements PersonIntegrationService {
         respVO.setPerson(
                 PersonConvert.INSTANCE.convert2(personInfoMapper.getByUserId(userId))
         );
-        if(null != respVO.getPerson() && null != respVO.getPerson().getAreaId()) {
+        if (null != respVO.getPerson() && null != respVO.getPerson().getAreaId()) {
             respVO.getPerson().setArea(areaService.getById(respVO.getPerson().getAreaId()));
         }
-        if(null != respVO.getPerson() && null != respVO.getPerson().getRegId()) {
+        if (null != respVO.getPerson() && null != respVO.getPerson().getRegId()) {
             respVO.getPerson().setReg(areaService.getById(respVO.getPerson().getRegId()));
         }
 
@@ -317,25 +320,17 @@ public class PersonIntegrationServiceImpl implements PersonIntegrationService {
 //        }
 
         PageResult<PersonInfoRespVO> result = personInfoMapper.queryPage(reqVO, ids, reqVO.getEnterpriseId());
-        result.getList().forEach(item -> {
-            if (null != item.getAreaId()) {
-                item.setArea(areaService.getById(item.getAreaId()));
-            }
-            if (null != item.getRegId()) {
-                item.setReg(areaService.getById(item.getRegId()));
-            }
-        });
         if (null != reqVO.getEnterpriseId()) {
             redisUtils.setEnterpriseUserLookPersonPermission(
                     result.getList().stream()
                             .map(PersonInfoRespVO::getUserId)
                             .collect(Collectors.toSet()));
         }
-        return result;
+        return fillArea(result);
     }
 
     @Override
-    public PageResult<PersonInfoRespVO> queryPage(PersonQueryReqVO reqVO) {
+    public PageResult<AppRecruitPersonInfoRespVO> queryPage(PersonQueryReqVO reqVO) {
         //TODO 后续改es
         if (null != reqVO.getAreaIds()) {
             // 取最后一个元素
@@ -349,6 +344,22 @@ public class PersonIntegrationServiceImpl implements PersonIntegrationService {
             }
         }
         PageResult<PersonInfoRespVO> result = personInfoMapper.queryPage(reqVO, LoginUserContext.getEnterpriseId());
+        result = fillArea(result);
+        PageResult<AppRecruitPersonInfoRespVO>  recruitResult =
+                BeanUtils.toBean(result, AppRecruitPersonInfoRespVO.class) ;
+        recruitResult.getList().forEach(item -> {
+            item.setInterestedList(
+                    JobInterestedConvert.INSTANCE.convertList2(jobInterestedMapper.selectByUserIdList(item.getUserId()))
+            );
+            item.setWorkList(
+                    WorkExpConvert.INSTANCE.convertList2(workExpMapper.selectByUserIdList(item.getUserId()))
+            );
+        });
+        return recruitResult;
+    }
+
+    @NotNull
+    private PageResult<PersonInfoRespVO> fillArea(PageResult<PersonInfoRespVO> result) {
         result.getList().forEach(item -> {
             if (null != item.getAreaId()) {
                 item.setArea(areaService.getById(item.getAreaId()));
@@ -381,36 +392,13 @@ public class PersonIntegrationServiceImpl implements PersonIntegrationService {
         PageResult<PersonInfoRespVO> result = personInfoMapper.queryPageById(null == ids ? null : Arrays.asList(ids),
                 LoginUserContext.getEnterpriseId(),
                 reqVO);
-        result.getList().forEach(item -> {
-            if (null != item.getAreaId()) {
-                item.setArea(areaService.getById(item.getAreaId()));
-            }
-            if (null != item.getRegId()) {
-                item.setReg(areaService.getById(item.getRegId()));
-            }
-        });
-        redisUtils.setEnterpriseUserLookPersonPermission(
-                result.getList().stream()
-                        .map(PersonInfoRespVO::getUserId)
-                        .collect(Collectors.toSet()));
-        return result;
+        return fillArea(result);
     }
 
     @Override
     public PageResult<PersonInfoRespVO> selectEnterpriseTalentPoolPage(PersonInfoPageReqVO reqVO) {
         PageResult<PersonInfoRespVO> result = personInfoMapper.selectEnterpriseTalentPoolPage(LoginUserContext.getEnterpriseId(), reqVO);
-        result.getList().forEach(item -> {
-            if (null != item.getAreaId()) {
-                item.setArea(areaService.getById(item.getAreaId()));
-            }
-            if (null != item.getRegId()) {
-                item.setReg(areaService.getById(item.getRegId()));
-            }
-        });
-        redisUtils.setEnterpriseUserLookPersonPermission(result.getList().stream()
-                .map(PersonInfoRespVO::getUserId)
-                .collect(Collectors.toSet()));
-        return result;
+        return fillArea(result);
     }
 
     @Override

+ 3 - 0
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/service/user/MdeUserServiceImpl.java

@@ -5,6 +5,7 @@ import cn.hutool.core.lang.Assert;
 import cn.hutool.core.util.IdUtil;
 import cn.hutool.core.util.StrUtil;
 import com.baomidou.dynamic.datasource.annotation.DSTransactional;
+import com.baomidou.lock.annotation.Lock4j;
 import com.citu.framework.common.enums.TerminalEnum;
 import com.citu.framework.common.enums.UserTypeEnum;
 import com.citu.framework.common.pojo.PageResult;
@@ -238,6 +239,7 @@ public class MdeUserServiceImpl implements MdeUserService {
 
     @Override
     @DSTransactional // 单机+多数据源方案,使用 @DSTransactional 保证本地事务,以及数据源的切换
+    @Lock4j(keys = {"#phone"},  acquireTimeout = 6000)
     public MdeUserDO createUserIfAbsent(String phone, String registerIp, String terminal) {
         // 用户已经存在
         MdeUserDO user = mdeUserMapper.selectByPhone(phone);
@@ -250,6 +252,7 @@ public class MdeUserServiceImpl implements MdeUserService {
 
     @Override
     @DSTransactional // 单机+多数据源方案,使用 @DSTransactional 保证本地事务,以及数据源的切换
+    @Lock4j(keys = {"#phone"},  acquireTimeout = 6000)
     public MdeUserDO createUserIfAbsent(String phone, Long inviteCode) {
         // 用户已经存在
         MdeUserDO user = mdeUserMapper.selectByPhone(phone);

+ 3 - 3
menduner/menduner-system-biz/src/main/java/com/citu/module/menduner/system/util/MessageUtils.java

@@ -87,13 +87,13 @@ public class MessageUtils {
                     smsReqDTO.setMobile(data.getMobile());
                     smsReqDTO.setTemplateCode(CommonConstants.SMS_SYS_NOTIFY_TEMPLATE);
                     smsReqDTO.setTemplateParams(MapUtil.<String, Object>builder()
-                            .put("1", notifySysStrMap.get(reqDTO.getTemplateCode()))
+                            .put("1", getPlatformSystemMessageContent(reqDTO.getTemplateCode(), reqDTO.getTemplateParams()))
                             .build());
                     smsSendApi.sendSingleSmsToAdmin(smsReqDTO);
-                    // 发送邮件
 
+                    // 发送邮件
                     mailSendApi.sendSingleMailToAdmin(
-                            packSysNotifyTemplate(data.getEmail(),notifySysStrMap.get(reqDTO.getTemplateCode()))
+                            packSysNotifyTemplate(data.getEmail(),getPlatformSystemMessageContent(reqDTO.getTemplateCode(),reqDTO.getTemplateParams()))
                     );
                 }
             }