Commit 3e91ac73c4fd1bcebe2c608675324d6cf2df5c5a

Authored by puhui999
1 parent 3bab9748

CRM:完善数据分页查询条件构造 CrmQueryWrapperUtils

Showing 13 changed files with 91 additions and 82 deletions
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/permission/CrmPermissionRoleCodeEnum.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.enums.permission;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Getter;
  5 +
  6 +/**
  7 + * Crm 数据权限角色枚举
  8 + *
  9 + * @author HUIHUI
  10 + */
  11 +@Getter
  12 +@AllArgsConstructor
  13 +public enum CrmPermissionRoleCodeEnum {
  14 +
  15 + CRM_ADMIN("crm_admin", "CRM 管理员");
  16 +
  17 + /**
  18 + * 角色标识
  19 + */
  20 + private String code;
  21 + /**
  22 + * 角色名称
  23 + */
  24 + private String name;
  25 +
  26 +}
  27 +
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.http
1 1 ### 请求 /transfer
2 2 PUT {{baseUrl}}/crm/customer/transfer
3   -Content-Type: application/json
  3 +Content-Type: application/-id: {{adminTenentId}}json
4 4 Authorization: Bearer {{token}}
5   -tenant-id: {{adminTenentId}}
  5 +tenant
6 6  
7 7 {
8 8 "id": 10,
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java
... ... @@ -3,8 +3,10 @@ package cn.iocoder.yudao.module.crm.controller.admin.customer;
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 7 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
7 8 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
  9 +import cn.iocoder.yudao.framework.operatelogv2.core.vo.OperateLogV2PageReqVO;
8 10 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.*;
9 11 import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert;
10 12 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
... ... @@ -12,6 +14,7 @@ import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
12 14 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
13 15 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
14 16 import cn.iocoder.yudao.module.system.api.logger.OperateLogApi;
  17 +import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2PageReqDTO;
15 18 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO;
16 19 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
17 20 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
... ... @@ -135,23 +138,14 @@ public class CrmCustomerController {
135 138 return success(true);
136 139 }
137 140  
138   - // TODO @puhui999:operate-log-list 或者 operate-log-page 如果分页
139   - @GetMapping("/operate-log")
  141 + @GetMapping("/operate-log-page")
140 142 @Operation(summary = "获得客户操作日志")
141 143 @Parameter(name = "id", description = "编号", required = true, example = "1024")
142 144 @PreAuthorize("@ss.hasPermission('crm:customer:query')")
143   - // TODO @puhui999:最好有读权限;方法名改成 getCustomerOperateLog
144   - public CommonResult<List<OperateLogV2RespDTO>> getOperateLog(@RequestParam("id") Long id) {
145   - // 1. 获取客户
146   - // TODO @puhui999:这个校验可以去掉哈;
147   - CrmCustomerDO customer = customerService.getCustomer(id);
148   - if (customer == null) {
149   - return success(null);
150   - }
151   -
152   - // 2. 获取操作日志
153   - // TODO @puhui999:操作日志,返回可能要分页哈;
154   - return success(operateLogApi.getOperateLogByModuleAndBizId(CRM_CUSTOMER, id));
  145 + public CommonResult<PageResult<OperateLogV2RespDTO>> getCustomerOperateLog(OperateLogV2PageReqVO reqVO) {
  146 + reqVO.setPageSize(PAGE_SIZE_NONE); // 不分页
  147 + reqVO.setBizType(CRM_CUSTOMER);
  148 + return success(operateLogApi.getOperateLogPage(BeanUtils.toBean(reqVO, OperateLogV2PageReqDTO.class)));
155 149 }
156 150  
157 151 // TODO @Joey:单独建一个属于自己业务的 ReqVO;因为前端如果模拟请求,是不是可以更新其它字段了;
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java
... ... @@ -38,11 +38,8 @@ public interface CrmBusinessMapper extends BaseMapperX&lt;CrmBusinessDO&gt; {
38 38 default PageResult<CrmBusinessDO> selectPage(CrmBusinessPageReqVO pageReqVO, Long userId) {
39 39 MPJLambdaWrapperX<CrmBusinessDO> query = new MPJLambdaWrapperX<>();
40 40 // 拼接数据权限的查询条件
41   - boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_BUSINESS.getType(),
  41 + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_BUSINESS.getType(),
42 42 CrmBusinessDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE);
43   - if (!condition) {
44   - return PageResult.empty();
45   - }
46 43 // 拼接自身的查询条件
47 44 query.selectAll(CrmBusinessDO.class)
48 45 .likeIfPresent(CrmBusinessDO::getName, pageReqVO.getName())
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/clue/CrmClueMapper.java
... ... @@ -30,11 +30,8 @@ public interface CrmClueMapper extends BaseMapperX&lt;CrmClueDO&gt; {
30 30 default PageResult<CrmClueDO> selectPage(CrmCluePageReqVO pageReqVO, Long userId) {
31 31 MPJLambdaWrapperX<CrmClueDO> query = new MPJLambdaWrapperX<>();
32 32 // 拼接数据权限的查询条件
33   - boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_LEADS.getType(),
  33 + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_LEADS.getType(),
34 34 CrmClueDO::getId, userId, pageReqVO.getSceneType(), pageReqVO.getPool());
35   - if (!condition) {
36   - return PageResult.empty();
37   - }
38 35 // 拼接自身的查询条件
39 36 query.selectAll(CrmClueDO.class)
40 37 .likeIfPresent(CrmClueDO::getName, pageReqVO.getName())
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contact/CrmContactMapper.java
... ... @@ -43,11 +43,8 @@ public interface CrmContactMapper extends BaseMapperX&lt;CrmContactDO&gt; {
43 43 default PageResult<CrmContactDO> selectPage(CrmContactPageReqVO pageReqVO, Long userId) {
44 44 MPJLambdaWrapperX<CrmContactDO> query = new MPJLambdaWrapperX<>();
45 45 // 拼接数据权限的查询条件
46   - boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTACT.getType(),
  46 + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTACT.getType(),
47 47 CrmContactDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE);
48   - if (!condition) {
49   - return PageResult.empty();
50   - }
51 48 // 拼接自身的查询条件
52 49 query.selectAll(CrmContactDO.class)
53 50 .likeIfPresent(CrmContactDO::getName, pageReqVO.getName())
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java
... ... @@ -41,11 +41,8 @@ public interface CrmContractMapper extends BaseMapperX&lt;CrmContractDO&gt; {
41 41 default PageResult<CrmContractDO> selectPage(CrmContractPageReqVO pageReqVO, Long userId) {
42 42 MPJLambdaWrapperX<CrmContractDO> mpjLambdaWrapperX = new MPJLambdaWrapperX<>();
43 43 // 拼接数据权限的查询条件
44   - boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(mpjLambdaWrapperX, CrmBizTypeEnum.CRM_CONTACT.getType(),
  44 + CrmQueryWrapperUtils.appendPermissionCondition(mpjLambdaWrapperX, CrmBizTypeEnum.CRM_CONTACT.getType(),
45 45 CrmContractDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE);
46   - if (!condition) {
47   - return PageResult.empty();
48   - }
49 46 // 拼接自身的查询条件
50 47 mpjLambdaWrapperX.selectAll(CrmContractDO.class)
51 48 .likeIfPresent(CrmContractDO::getNo, pageReqVO.getNo())
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java
... ... @@ -30,11 +30,8 @@ public interface CrmCustomerMapper extends BaseMapperX&lt;CrmCustomerDO&gt; {
30 30 default PageResult<CrmCustomerDO> selectPage(CrmCustomerPageReqVO pageReqVO, Long userId) {
31 31 MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>();
32 32 // 拼接数据权限的查询条件
33   - boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(),
  33 + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(),
34 34 CrmCustomerDO::getId, userId, pageReqVO.getSceneType(), pageReqVO.getPool());
35   - if (!condition) {
36   - return PageResult.empty();
37   - }
38 35 // 拼接自身的查询条件
39 36 query.selectAll(CrmCustomerDO.class)
40 37 .likeIfPresent(CrmCustomerDO::getName, pageReqVO.getName())
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivableMapper.java
... ... @@ -39,11 +39,8 @@ public interface CrmReceivableMapper extends BaseMapperX&lt;CrmReceivableDO&gt; {
39 39 default PageResult<CrmReceivableDO> selectPage(CrmReceivablePageReqVO pageReqVO, Long userId) {
40 40 MPJLambdaWrapperX<CrmReceivableDO> query = new MPJLambdaWrapperX<>();
41 41 // 拼接数据权限的查询条件
42   - boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE.getType(),
  42 + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE.getType(),
43 43 CrmReceivableDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE);
44   - if (!condition) {
45   - return PageResult.empty();
46   - }
47 44 // 拼接自身的查询条件
48 45 query.selectAll(CrmReceivableDO.class)
49 46 .eqIfPresent(CrmReceivableDO::getNo, pageReqVO.getNo())
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/receivable/CrmReceivablePlanMapper.java
... ... @@ -38,11 +38,8 @@ public interface CrmReceivablePlanMapper extends BaseMapperX&lt;CrmReceivablePlanDO
38 38 default PageResult<CrmReceivablePlanDO> selectPage(CrmReceivablePlanPageReqVO pageReqVO, Long userId) {
39 39 MPJLambdaWrapperX<CrmReceivablePlanDO> query = new MPJLambdaWrapperX<>();
40 40 // 拼接数据权限的查询条件
41   - boolean condition = CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType(),
  41 + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType(),
42 42 CrmReceivablePlanDO::getId, userId, pageReqVO.getSceneType(), Boolean.FALSE);
43   - if (!condition) {
44   - return PageResult.empty();
45   - }
46 43 // 拼接自身的查询条件
47 44 query.selectAll(CrmReceivablePlanDO.class)
48 45 .eqIfPresent(CrmReceivablePlanDO::getCustomerId, pageReqVO.getCustomerId())
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmQueryWrapperUtils.java
... ... @@ -7,9 +7,12 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
7 7 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
8 8 import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;
9 9 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
  10 +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionRoleCodeEnum;
  11 +import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
10 12 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
11 13 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
12 14 import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
  15 +import com.github.yulichang.autoconfigure.MybatisPlusJoinProperties;
13 16 import com.github.yulichang.wrapper.MPJLambdaWrapper;
14 17  
15 18 import java.util.Collection;
... ... @@ -33,46 +36,41 @@ public class CrmQueryWrapperUtils {
33 36 * @param userId 用户编号
34 37 * @param sceneType 场景类型
35 38 * @param pool 公海
36   - * @return 是否 (是:需要执行查询,否:不需要查询调用方法直接返回空)
37 39 */
38   - // TODO @puhui999:bizId 直接传递会不会简单点 回复:还是需要 SFunction 因为分页连表时不知道 bizId 是多少;是不是把 bizId 传入就好啦?
39   - public static <T extends MPJLambdaWrapper<?>, S> boolean appendPermissionCondition(T query, Integer bizType, SFunction<S, ?> bizId,
40   - Long userId, Integer sceneType, Boolean pool) {
  40 + public static <T extends MPJLambdaWrapper<?>, S> void appendPermissionCondition(T query, Integer bizType, SFunction<S, ?> bizId,
  41 + Long userId, Integer sceneType, Boolean pool) {
  42 + final String ownerUserIdField = SingletonManager.getMybatisPlusJoinProperties().getTableAlias() + ".owner_user_id";
41 43 // 1. 构建数据权限连表条件
42   - if (ObjUtil.notEqual(validateAdminUser(userId), Boolean.TRUE)) { // 管理员不需要数据权限
  44 + if (ObjUtil.notEqual(validateAdminUser(userId), Boolean.TRUE) && ObjUtil.notEqual(pool, Boolean.TRUE)) { // 管理员,公海不需要数据权限
43 45 query.innerJoin(CrmPermissionDO.class, on -> on.eq(CrmPermissionDO::getBizType, bizType)
44   - .eq(CrmPermissionDO::getBizId, bizId)
  46 + .eq(CrmPermissionDO::getBizId, bizId) // 只能使用 SFunction 如果传 id 解析出来的 sql 不对
45 47 .eq(CrmPermissionDO::getUserId, userId));
46 48 }
47 49 // 2.1 场景一:我负责的数据
48 50 if (CrmSceneTypeEnum.isOwner(sceneType)) {
49   - query.eq("owner_user_id", userId);
  51 + query.eq(ownerUserIdField, userId);
50 52 }
51 53 // 2.2 场景二:我参与的数据
52 54 if (CrmSceneTypeEnum.isInvolved(sceneType)) {
53   - query.ne("owner_user_id", userId)
54   - // TODO @puhui999:IN 是不是更合适哈;
55   - .and(q -> q.eq(CrmPermissionDO::getLevel, CrmPermissionLevelEnum.READ.getLevel())
56   - .or()
57   - .eq(CrmPermissionDO::getLevel, CrmPermissionLevelEnum.WRITE.getLevel()));
  55 + query.ne(ownerUserIdField, userId)
  56 + .in(CrmPermissionDO::getLevel, CrmPermissionLevelEnum.READ.getLevel(), CrmPermissionLevelEnum.WRITE.getLevel());
58 57 }
59 58 // 2.3 场景三:下属负责的数据
60 59 if (CrmSceneTypeEnum.isSubordinate(sceneType)) {
61   - // TODO @puhui999:要不如果没有下属,拼一个 owner_user_id in null,不返回结果就好啦;
62   - List<AdminUserRespDTO> subordinateUsers = getAdminUserApi().getUserListBySubordinate(userId);
  60 + List<AdminUserRespDTO> subordinateUsers = SingletonManager.getAdminUserApi().getUserListBySubordinate(userId);
63 61 if (CollUtil.isEmpty(subordinateUsers)) {
64   - return false;
  62 + query.eq(ownerUserIdField, -1); // 不返回任何结果
  63 + } else {
  64 + query.in(ownerUserIdField, convertSet(subordinateUsers, AdminUserRespDTO::getId));
65 65 }
66   - query.in("owner_user_id", convertSet(subordinateUsers, AdminUserRespDTO::getId));
67 66 }
68 67  
69 68 // 3. 拼接公海的查询条件
70 69 if (ObjUtil.equal(pool, Boolean.TRUE)) { // 情况一:公海
71   - query.isNull("owner_user_id");
  70 + query.isNull(ownerUserIdField);
72 71 } else { // 情况二:不是公海
73   - query.isNotNull("owner_user_id");
  72 + query.isNotNull(ownerUserIdField);
74 73 }
75   - return true;
76 74 }
77 75  
78 76 /**
... ... @@ -93,38 +91,38 @@ public class CrmQueryWrapperUtils {
93 91 .in(CollUtil.isNotEmpty(bizIds), CrmPermissionDO::getUserId, userId));
94 92 }
95 93  
96   - private static AdminUserApi getAdminUserApi() {
97   - return AdminUserApiHolder.ADMIN_USER_API;
98   - }
99   -
100 94 /**
101   - * 校验用户是否是管理员
  95 + * 校验用户是否是 CRM 管理员
102 96 *
103 97 * @param userId 用户编号
104 98 * @return 是/否
105 99 */
106 100 private static boolean validateAdminUser(Long userId) {
107   - // TODO 查询权限配置表用户的角色信息
108   - // TODO @puhui999:查询用户的角色;CRM_ADMIN("crm_admin", "CRM 管理员"),
109   - //CrmPermissionConfig permissionConfig = crmPermissionConfigService.getPermissionConfigByUserId(userId);
110   - //if (permissionConfig == null) {
111   - // return false;
112   - //}
113   - //// 校验是否为管理员
114   - //if (permissionConfig.getIsAdmin()){
115   - // return true;
116   - //}
117   - return false;
  101 + return SingletonManager.getPermissionApi().hasAnyRoles(userId, CrmPermissionRoleCodeEnum.CRM_ADMIN.getCode());
118 102 }
119 103  
120 104 /**
121   - * 静态内部类实现 AdminUserApi 单例获取
  105 + * 静态内部类实现单例获取
122 106 *
123 107 * @author HUIHUI
124 108 */
125   - private static class AdminUserApiHolder {
  109 + private static class SingletonManager {
126 110  
127 111 private static final AdminUserApi ADMIN_USER_API = SpringUtil.getBean(AdminUserApi.class);
  112 + private static final PermissionApi PERMISSION_API = SpringUtil.getBean(PermissionApi.class);
  113 + private static final MybatisPlusJoinProperties MYBATIS_PLUS_JOIN_PROPERTIES = SpringUtil.getBean(MybatisPlusJoinProperties.class);
  114 +
  115 + public static AdminUserApi getAdminUserApi() {
  116 + return ADMIN_USER_API;
  117 + }
  118 +
  119 + public static PermissionApi getPermissionApi() {
  120 + return PERMISSION_API;
  121 + }
  122 +
  123 + public static MybatisPlusJoinProperties getMybatisPlusJoinProperties() {
  124 + return MYBATIS_PLUS_JOIN_PROPERTIES;
  125 + }
128 126  
129 127 }
130 128  
... ...
yudao-server/src/main/resources/application-local.yaml
... ... @@ -188,6 +188,7 @@ logging:
188 188 cn.iocoder.yudao.module.trade.dal.mysql: debug
189 189 cn.iocoder.yudao.module.promotion.dal.mysql: debug
190 190 cn.iocoder.yudao.module.statistics.dal.mysql: debug
  191 + cn.iocoder.yudao.module.crm.dal.mysql: debug
191 192  
192 193 debug: false
193 194  
... ...
yudao-server/src/main/resources/application.yaml
... ... @@ -79,7 +79,17 @@ mybatis-plus:
79 79 password: XDV71a+xqStEA3WH # 加解密的秘钥,可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成
80 80  
81 81 mybatis-plus-join:
82   - banner: false # 关闭控制台的 Banner 打印
  82 + #是否打印 mybatis plus join banner 默认true
  83 + banner: false
  84 + #全局启用副表逻辑删除(默认true) 关闭后关联查询不会加副表逻辑删除
  85 + sub-table-logic: true
  86 + #拦截器MappedStatement缓存(默认true)
  87 + ms-cache: true
  88 + #表别名(默认 t)
  89 + table-alias: t
  90 + #副表逻辑删除条件的位置,支持where、on
  91 + #默认ON (1.4.7.2及之前版本默认为where)
  92 + logic-del-type: on
83 93  
84 94 # Spring Data Redis 配置
85 95 spring:
... ...