Commit d430063eec02abe4b571a1c36f0ea872cad4b3df

Authored by YunaiV
2 parents a36fe8c4 d59d1428

Merge remote-tracking branch 'puhui/feature/crm' into feature/crm

# Conflicts:
#	yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java
Showing 16 changed files with 458 additions and 74 deletions
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
@@ -69,7 +69,10 @@ public interface ErrorCodeConstants { @@ -69,7 +69,10 @@ public interface ErrorCodeConstants {
69 // ========== 商机状态 1_020_011_000 ========== 69 // ========== 商机状态 1_020_011_000 ==========
70 ErrorCode BUSINESS_STATUS_NOT_EXISTS = new ErrorCode(1_020_011_000, "商机状态不存在"); 70 ErrorCode BUSINESS_STATUS_NOT_EXISTS = new ErrorCode(1_020_011_000, "商机状态不存在");
71 71
72 - // ========== 客户公海规则设置 1_020_011_000 ========== 72 + // ========== 客户公海规则设置 1_020_012_000 ==========
73 ErrorCode CUSTOMER_LIMIT_CONFIG_NOT_EXISTS = new ErrorCode(1_020_012_000, "客户限制配置不存在"); 73 ErrorCode CUSTOMER_LIMIT_CONFIG_NOT_EXISTS = new ErrorCode(1_020_012_000, "客户限制配置不存在");
74 74
  75 + // ========== 关注的数据 1_020_013_000 ==========
  76 + ErrorCode CRM_CONCERNED_NOT_EXISTS = new ErrorCode(1_020_013_000, "关注数据不存在");
  77 +
75 } 78 }
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmSceneEnum.java
@@ -17,7 +17,8 @@ import java.util.Arrays; @@ -17,7 +17,8 @@ import java.util.Arrays;
17 public enum CrmSceneEnum implements IntArrayValuable { 17 public enum CrmSceneEnum implements IntArrayValuable {
18 18
19 OWNER(1, "我负责的"), 19 OWNER(1, "我负责的"),
20 - FOLLOW(2, "我关注的"); 20 + FOLLOW(2, "我关注的"),
  21 + SUBORDINATE(3, "下属负责的");
21 22
22 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmSceneEnum::getType).toArray(); 23 public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmSceneEnum::getType).toArray();
23 24
@@ -38,6 +39,10 @@ public enum CrmSceneEnum implements IntArrayValuable { @@ -38,6 +39,10 @@ public enum CrmSceneEnum implements IntArrayValuable {
38 return ObjUtil.equal(FOLLOW.getType(), type); 39 return ObjUtil.equal(FOLLOW.getType(), type);
39 } 40 }
40 41
  42 + public static boolean isSubordinate(Integer type) {
  43 + return ObjUtil.equal(SUBORDINATE.getType(), type);
  44 + }
  45 +
41 @Override 46 @Override
42 public int[] array() { 47 public int[] array() {
43 return ARRAYS; 48 return ARRAYS;
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java
@@ -36,7 +36,6 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. @@ -36,7 +36,6 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
36 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; 36 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
37 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; 37 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
38 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; 38 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
39 -import static java.util.Collections.singletonList;  
40 39
41 @Tag(name = "管理后台 - CRM 客户") 40 @Tag(name = "管理后台 - CRM 客户")
42 @RestController 41 @RestController
@@ -140,6 +139,24 @@ public class CrmCustomerController { @@ -140,6 +139,24 @@ public class CrmCustomerController {
140 return success(true); 139 return success(true);
141 } 140 }
142 141
  142 + @PutMapping("/concern")
  143 + @Operation(summary = "关注客户")
  144 + @Parameter(name = "ids", description = "编号", required = true, example = "[1024]")
  145 + @PreAuthorize("@ss.hasPermission('crm:customer:update')")
  146 + public CommonResult<Boolean> concernCustomer(@RequestParam("ids") List<Long> ids) {
  147 + customerService.concernCustomer(ids, getLoginUserId());
  148 + return success(true);
  149 + }
  150 +
  151 + @PutMapping("/cancel-concern")
  152 + @Operation(summary = "取消关注客户")
  153 + @Parameter(name = "ids", description = "编号", required = true, example = "[1024]")
  154 + @PreAuthorize("@ss.hasPermission('crm:customer:update')")
  155 + public CommonResult<Boolean> cancelConcernCustomer(@RequestParam("ids") List<Long> ids) {
  156 + customerService.cancelConcernCustomer(ids, getLoginUserId());
  157 + return success(true);
  158 + }
  159 +
143 // ==================== 公海相关操作 ==================== 160 // ==================== 公海相关操作 ====================
144 161
145 @PutMapping("/put-pool") 162 @PutMapping("/put-pool")
@@ -178,7 +195,7 @@ public class CrmCustomerController { @@ -178,7 +195,7 @@ public class CrmCustomerController {
178 @GetMapping("/query-all-list") 195 @GetMapping("/query-all-list")
179 @Operation(summary = "查询客户列表") 196 @Operation(summary = "查询客户列表")
180 @PreAuthorize("@ss.hasPermission('crm:customer:all')") 197 @PreAuthorize("@ss.hasPermission('crm:customer:all')")
181 - public CommonResult<List<CrmCustomerQueryAllRespVO>> queryAll(){ 198 + public CommonResult<List<CrmCustomerQueryAllRespVO>> queryAll() {
182 List<CrmCustomerDO> crmCustomerDOList = customerService.getCustomerList(); 199 List<CrmCustomerDO> crmCustomerDOList = customerService.getCustomerList();
183 List<CrmCustomerQueryAllRespVO> data = CrmCustomerConvert.INSTANCE.convertQueryAll(crmCustomerDOList); 200 List<CrmCustomerQueryAllRespVO> data = CrmCustomerConvert.INSTANCE.convertQueryAll(crmCustomerDOList);
184 return success(data); 201 return success(data);
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerPageReqVO.java
1 package cn.iocoder.yudao.module.crm.controller.admin.customer.vo; 1 package cn.iocoder.yudao.module.crm.controller.admin.customer.vo;
2 2
3 -import cn.iocoder.yudao.framework.common.pojo.PageParam;  
4 -import cn.iocoder.yudao.module.crm.enums.common.CrmSceneEnum; 3 +import cn.iocoder.yudao.module.crm.framework.vo.CrmBasePageReqVO;
5 import io.swagger.v3.oas.annotations.media.Schema; 4 import io.swagger.v3.oas.annotations.media.Schema;
6 import lombok.Data; 5 import lombok.Data;
7 import lombok.EqualsAndHashCode; 6 import lombok.EqualsAndHashCode;
8 import lombok.ToString; 7 import lombok.ToString;
9 8
10 -import javax.validation.constraints.NotNull;  
11 -  
12 @Schema(description = "管理后台 - CRM 客户分页 Request VO") 9 @Schema(description = "管理后台 - CRM 客户分页 Request VO")
13 @Data 10 @Data
14 @EqualsAndHashCode(callSuper = true) 11 @EqualsAndHashCode(callSuper = true)
15 @ToString(callSuper = true) 12 @ToString(callSuper = true)
16 -public class CrmCustomerPageReqVO extends PageParam { 13 +public class CrmCustomerPageReqVO extends CrmBasePageReqVO {
17 14
18 @Schema(description = "客户名称", example = "赵六") 15 @Schema(description = "客户名称", example = "赵六")
19 private String name; 16 private String name;
@@ -30,16 +27,4 @@ public class CrmCustomerPageReqVO extends PageParam { @@ -30,16 +27,4 @@ public class CrmCustomerPageReqVO extends PageParam {
30 @Schema(description = "客户来源", example = "1") 27 @Schema(description = "客户来源", example = "1")
31 private Integer source; 28 private Integer source;
32 29
33 - /**  
34 - * 场景类型  
35 - *  
36 - * 关联 {@link CrmSceneEnum}  
37 - */  
38 - @Schema(description = "场景类型", example = "1")  
39 - private Integer sceneType;  
40 -  
41 - @Schema(description = "是否为公海数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")  
42 - @NotNull(message = "是否为公海数据不能为空")  
43 - private Boolean pool;  
44 -  
45 } 30 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/concerned/CrmConcernedDO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.dal.dataobject.concerned;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
  4 +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
  5 +import com.baomidou.mybatisplus.annotation.KeySequence;
  6 +import com.baomidou.mybatisplus.annotation.TableId;
  7 +import com.baomidou.mybatisplus.annotation.TableName;
  8 +import lombok.*;
  9 +
  10 +/**
  11 + * CRM 关注的数据 DO
  12 + *
  13 + * @author HUIHUI
  14 + */
  15 +@TableName("crm_concerned")
  16 +@KeySequence("crm_concerned_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  17 +@Data
  18 +@EqualsAndHashCode(callSuper = true)
  19 +@ToString(callSuper = true)
  20 +@Builder
  21 +@NoArgsConstructor
  22 +@AllArgsConstructor
  23 +public class CrmConcernedDO extends BaseDO {
  24 +
  25 + /**
  26 + * 编号,主键自增
  27 + */
  28 + @TableId
  29 + private Long id;
  30 +
  31 + /**
  32 + * 数据类型
  33 + *
  34 + * 枚举 {@link CrmBizTypeEnum}
  35 + */
  36 + private Integer bizType;
  37 + /**
  38 + * 数据编号
  39 + *
  40 + * 关联 {@link CrmBizTypeEnum} 对应模块 DO 的 id 字段
  41 + */
  42 + private Long bizId;
  43 +
  44 + /**
  45 + * 用户编号
  46 + *
  47 + * 关联 AdminUser 的 id 字段
  48 + */
  49 + private Long userId;
  50 +
  51 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/concerned/CrmConcernedMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.dal.mysql.concerned;
  2 +
  3 +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
  4 +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
  5 +import cn.iocoder.yudao.module.crm.dal.dataobject.concerned.CrmConcernedDO;
  6 +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
  7 +import org.apache.ibatis.annotations.Mapper;
  8 +
  9 +import java.util.Collection;
  10 +import java.util.List;
  11 +
  12 +/**
  13 + * CRM 关注的数据 Mapper
  14 + *
  15 + * @author HUIHUI
  16 + */
  17 +@Mapper
  18 +public interface CrmConcernedMapper extends BaseMapperX<CrmConcernedDO> {
  19 +
  20 + /**
  21 + * 查询用户关注的数据
  22 + *
  23 + * @param bizType 数据类型,关联 {@link CrmBizTypeEnum}
  24 + * @param bizIds 数据编号,关联 {@link CrmBizTypeEnum} 对应模块 DO#getId()
  25 + * @param userId 用户编号
  26 + * @return 关注的数据
  27 + */
  28 + default List<CrmConcernedDO> selectList(Integer bizType, Collection<Long> bizIds, Long userId) {
  29 + return selectList(new LambdaQueryWrapperX<CrmConcernedDO>()
  30 + .eq(CrmConcernedDO::getBizType, bizType)
  31 + .in(CrmConcernedDO::getBizId, bizIds)
  32 + .eq(CrmConcernedDO::getUserId, userId));
  33 + }
  34 +
  35 +}
0 \ No newline at end of file 36 \ No newline at end of file
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java
@@ -3,18 +3,17 @@ package cn.iocoder.yudao.module.crm.dal.mysql.customer; @@ -3,18 +3,17 @@ package cn.iocoder.yudao.module.crm.dal.mysql.customer;
3 import cn.iocoder.yudao.framework.common.pojo.PageParam; 3 import cn.iocoder.yudao.framework.common.pojo.PageParam;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
6 -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;  
7 import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
8 import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils; 7 import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
9 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO; 8 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO;
10 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 9 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
11 -import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;  
12 -import cn.iocoder.yudao.module.crm.enums.common.CrmSceneEnum;  
13 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 10 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
  11 +import cn.iocoder.yudao.module.crm.util.CrmQueryPageUtils;
14 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; 12 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
15 import com.baomidou.mybatisplus.core.metadata.IPage; 13 import com.baomidou.mybatisplus.core.metadata.IPage;
16 import org.apache.ibatis.annotations.Mapper; 14 import org.apache.ibatis.annotations.Mapper;
17 15
  16 +import java.util.Collection;
18 import java.util.List; 17 import java.util.List;
19 18
20 /** 19 /**
@@ -31,28 +30,21 @@ public interface CrmCustomerMapper extends BaseMapperX&lt;CrmCustomerDO&gt; { @@ -31,28 +30,21 @@ public interface CrmCustomerMapper extends BaseMapperX&lt;CrmCustomerDO&gt; {
31 .set(CrmCustomerDO::getOwnerUserId, ownerUserId)); 30 .set(CrmCustomerDO::getOwnerUserId, ownerUserId));
32 } 31 }
33 32
34 - default PageResult<CrmCustomerDO> selectPageWithAdmin(CrmCustomerPageReqVO pageReqVO, Long userId) {  
35 - // 情况一:管理员查看  
36 - LambdaQueryWrapperX<CrmCustomerDO> queryWrapperX = new LambdaQueryWrapperX<>();  
37 - appendQueryParams(queryWrapperX, pageReqVO, userId);  
38 - return selectPage(pageReqVO, queryWrapperX  
39 - .likeIfPresent(CrmCustomerDO::getName, pageReqVO.getName())  
40 - .eqIfPresent(CrmCustomerDO::getMobile, pageReqVO.getMobile())  
41 - .eqIfPresent(CrmCustomerDO::getIndustryId, pageReqVO.getIndustryId())  
42 - .eqIfPresent(CrmCustomerDO::getLevel, pageReqVO.getLevel())  
43 - .eqIfPresent(CrmCustomerDO::getSource, pageReqVO.getSource()));  
44 - }  
45 -  
46 - default PageResult<CrmCustomerDO> selectPage(CrmCustomerPageReqVO pageReqVO, Long userId) {  
47 - // 情况二:获取当前用户能看的分页数据 33 + /**
  34 + * 获取客户分页
  35 + *
  36 + * @param pageReqVO 请求
  37 + * @param userId 用户编号
  38 + * @param subordinateIds 下属用户编号
  39 + * @param isAdmin 是否为管理
  40 + * @return 客户分页数据
  41 + */
  42 + default PageResult<CrmCustomerDO> selectPage(CrmCustomerPageReqVO pageReqVO, Long userId, Collection<Long> subordinateIds, Boolean isAdmin) {
48 IPage<CrmCustomerDO> mpPage = MyBatisUtils.buildPage(pageReqVO); 43 IPage<CrmCustomerDO> mpPage = MyBatisUtils.buildPage(pageReqVO);
49 MPJLambdaWrapperX<CrmCustomerDO> mpjLambdaWrapperX = new MPJLambdaWrapperX<>(); 44 MPJLambdaWrapperX<CrmCustomerDO> mpjLambdaWrapperX = new MPJLambdaWrapperX<>();
50 // 构建数据权限连表条件 45 // 构建数据权限连表条件
51 - mpjLambdaWrapperX  
52 - .innerJoin(CrmPermissionDO.class, CrmPermissionDO::getBizId, CrmCustomerDO::getId)  
53 - .eq(CrmPermissionDO::getBizType, CrmBizTypeEnum.CRM_CUSTOMER.getType())  
54 - .eq(CrmPermissionDO::getUserId, userId);  
55 - appendQueryParams(mpjLambdaWrapperX, pageReqVO, userId); 46 + CrmQueryPageUtils.builderQuery(mpjLambdaWrapperX, pageReqVO, userId,
  47 + CrmBizTypeEnum.CRM_CUSTOMER.getType(), CrmCustomerDO::getId, subordinateIds, isAdmin);
56 mpjLambdaWrapperX 48 mpjLambdaWrapperX
57 .selectAll(CrmCustomerDO.class) 49 .selectAll(CrmCustomerDO.class)
58 .likeIfPresent(CrmCustomerDO::getName, pageReqVO.getName()) 50 .likeIfPresent(CrmCustomerDO::getName, pageReqVO.getName())
@@ -69,28 +61,4 @@ public interface CrmCustomerMapper extends BaseMapperX&lt;CrmCustomerDO&gt; { @@ -69,28 +61,4 @@ public interface CrmCustomerMapper extends BaseMapperX&lt;CrmCustomerDO&gt; {
69 return new PageResult<>(mpPage.getRecords(), mpPage.getTotal()); 61 return new PageResult<>(mpPage.getRecords(), mpPage.getTotal());
70 } 62 }
71 63
72 - static void appendQueryParams(MPJLambdaWrapperX<CrmCustomerDO> mpjLambdaWrapperX, CrmCustomerPageReqVO pageReqVO, Long userId) {  
73 - if (pageReqVO.getPool()) { // 情况一:公海  
74 - mpjLambdaWrapperX.isNull(CrmCustomerDO::getOwnerUserId);  
75 - } else { // 情况二:不是公海  
76 - mpjLambdaWrapperX.isNotNull(CrmCustomerDO::getOwnerUserId);  
77 - }  
78 - // TODO 场景数据过滤  
79 - if (CrmSceneEnum.isOwner(pageReqVO.getSceneType())) { // 场景一:我负责的数据  
80 - mpjLambdaWrapperX.eq(CrmCustomerDO::getOwnerUserId, userId);  
81 - }  
82 - }  
83 -  
84 - static void appendQueryParams(LambdaQueryWrapperX<CrmCustomerDO> lambdaQueryWrapperX, CrmCustomerPageReqVO pageReqVO, Long userId) {  
85 - if (pageReqVO.getPool()) { // 情况一:公海  
86 - lambdaQueryWrapperX.isNull(CrmCustomerDO::getOwnerUserId);  
87 - } else { // 情况二:不是公海  
88 - lambdaQueryWrapperX.isNotNull(CrmCustomerDO::getOwnerUserId);  
89 - }  
90 - // TODO 场景数据过滤  
91 - if (CrmSceneEnum.isOwner(pageReqVO.getSceneType())) { // 场景一:我负责的数据  
92 - lambdaQueryWrapperX.eq(CrmCustomerDO::getOwnerUserId, userId);  
93 - }  
94 - }  
95 -  
96 } 64 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/vo/CrmBasePageReqVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.framework.vo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.PageParam;
  4 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  5 +import cn.iocoder.yudao.module.crm.enums.common.CrmSceneEnum;
  6 +import io.swagger.v3.oas.annotations.media.Schema;
  7 +import lombok.Data;
  8 +import lombok.EqualsAndHashCode;
  9 +
  10 +@Schema(description = "管理后台 - CRM 分页 Base Request VO")
  11 +@Data
  12 +@EqualsAndHashCode(callSuper = true)
  13 +public class CrmBasePageReqVO extends PageParam {
  14 +
  15 + /**
  16 + * 场景类型, 为 null 时则表示全部
  17 + *
  18 + * 关联 {@link CrmSceneEnum}
  19 + */
  20 + @Schema(description = "场景类型", example = "1")
  21 + @InEnum(CrmSceneEnum.class)
  22 + private Integer sceneType;
  23 +
  24 + @Schema(description = "是否为公海数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
  25 + private Boolean pool; // null 则表示为不是公海数据
  26 +
  27 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/concerned/CrmConcernedService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.service.concerned;
  2 +
  3 +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
  4 +import cn.iocoder.yudao.module.crm.service.concerned.bo.CrmConcernedCreateReqBO;
  5 +
  6 +import javax.validation.Valid;
  7 +import java.util.Collection;
  8 +
  9 +/**
  10 + * CRM 关注的数据 Service 接口
  11 + *
  12 + * @author HUIHUI
  13 + */
  14 +public interface CrmConcernedService {
  15 +
  16 + /**
  17 + * 创建关注数据
  18 + *
  19 + * @param createReqBO 创建请求
  20 + * @return 编号
  21 + */
  22 + Long createConcerned(@Valid CrmConcernedCreateReqBO createReqBO);
  23 +
  24 + /**
  25 + * 批量创建关注数据
  26 + *
  27 + * @param createReqBO 创建请求
  28 + */
  29 + void createConcernedBatch(@Valid Collection<CrmConcernedCreateReqBO> createReqBO);
  30 +
  31 + /**
  32 + * 删除关注数据
  33 + *
  34 + * @param bizType 数据类型,关联 {@link CrmBizTypeEnum}
  35 + * @param bizIds 数据编号,关联 {@link CrmBizTypeEnum} 对应模块 DO#getId()
  36 + * @param userId 用户编号
  37 + */
  38 + void deleteConcerned(Integer bizType, Collection<Long> bizIds, Long userId);
  39 +
  40 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/concerned/CrmConcernedServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.service.concerned;
  2 +
  3 +import cn.hutool.core.util.ObjUtil;
  4 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  5 +import cn.iocoder.yudao.module.crm.dal.dataobject.concerned.CrmConcernedDO;
  6 +import cn.iocoder.yudao.module.crm.dal.mysql.concerned.CrmConcernedMapper;
  7 +import cn.iocoder.yudao.module.crm.service.concerned.bo.CrmConcernedCreateReqBO;
  8 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  9 +import org.springframework.stereotype.Service;
  10 +import org.springframework.transaction.annotation.Transactional;
  11 +import org.springframework.validation.annotation.Validated;
  12 +
  13 +import javax.annotation.Resource;
  14 +import java.util.Collection;
  15 +import java.util.Collections;
  16 +import java.util.List;
  17 +
  18 +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  19 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  20 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
  21 +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CRM_CONCERNED_NOT_EXISTS;
  22 +
  23 +/**
  24 + * CRM 关注的数据 Service 接口实现类
  25 + *
  26 + * @author HUIHUI
  27 + */
  28 +@Service
  29 +@Validated
  30 +public class CrmConcernedServiceImpl implements CrmConcernedService {
  31 +
  32 + @Resource
  33 + private CrmConcernedMapper concernedMapper;
  34 + @Resource
  35 + private AdminUserApi adminUserApi;
  36 +
  37 + @Override
  38 + @Transactional(rollbackFor = Exception.class)
  39 + public Long createConcerned(CrmConcernedCreateReqBO createReqBO) {
  40 + // 1. 校验用户是否存在
  41 + adminUserApi.validateUserList(Collections.singletonList(createReqBO.getUserId()));
  42 +
  43 + // 2. 创建
  44 + CrmConcernedDO concerned = BeanUtils.toBean(createReqBO, CrmConcernedDO.class);
  45 + concernedMapper.insert(concerned);
  46 + return concerned.getId();
  47 + }
  48 +
  49 + @Override
  50 + public void createConcernedBatch(Collection<CrmConcernedCreateReqBO> createReqBO) {
  51 + // 1. 校验用户是否存在
  52 + adminUserApi.validateUserList(convertSet(createReqBO, CrmConcernedCreateReqBO::getUserId));
  53 +
  54 + // 2. 创建
  55 + List<CrmConcernedDO> concernedList = convertList(createReqBO, item -> BeanUtils.toBean(item, CrmConcernedDO.class));
  56 + concernedMapper.insertBatch(concernedList);
  57 + }
  58 +
  59 + @Override
  60 + public void deleteConcerned(Integer bizType, Collection<Long> bizIds, Long userId) {
  61 + // 1. 查询关注数据
  62 + List<CrmConcernedDO> concernedList = concernedMapper.selectList(bizType, bizIds, userId);
  63 + if (ObjUtil.notEqual(bizIds.size(), concernedList.size())) {
  64 + throw exception(CRM_CONCERNED_NOT_EXISTS);
  65 + }
  66 +
  67 + // 2. 删除
  68 + concernedMapper.deleteBatchIds(convertList(concernedList, CrmConcernedDO::getId));
  69 + }
  70 +
  71 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/concerned/bo/CrmConcernedCreateReqBO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.service.concerned.bo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.validation.InEnum;
  4 +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
  5 +import lombok.Data;
  6 +
  7 +import javax.validation.constraints.NotNull;
  8 +
  9 +/**
  10 + * CRM 关注的数据 Create Req BO
  11 + *
  12 + * @author HUIHUI
  13 + */
  14 +@Data
  15 +public class CrmConcernedCreateReqBO {
  16 +
  17 + /**
  18 + * 当前登录用户编号
  19 + */
  20 + @NotNull(message = "用户编号不能为空")
  21 + private Long userId;
  22 +
  23 + /**
  24 + * Crm 类型
  25 + */
  26 + @NotNull(message = "Crm 类型不能为空")
  27 + @InEnum(CrmBizTypeEnum.class)
  28 + private Integer bizType;
  29 + /**
  30 + * 数据编号
  31 + */
  32 + @NotNull(message = "Crm 数据编号不能为空")
  33 + private Long bizId;
  34 +
  35 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java
@@ -90,6 +90,22 @@ public interface CrmCustomerService { @@ -90,6 +90,22 @@ public interface CrmCustomerService {
90 */ 90 */
91 void lockCustomer(@Valid CrmCustomerUpdateReqVO updateReqVO); 91 void lockCustomer(@Valid CrmCustomerUpdateReqVO updateReqVO);
92 92
  93 + /**
  94 + * 关注客户
  95 + *
  96 + * @param ids 客户编号
  97 + * @param userId 用户编号
  98 + */
  99 + void concernCustomer(List<Long> ids, Long userId);
  100 +
  101 + /**
  102 + * 取消关注客户
  103 + *
  104 + * @param ids 客户编号
  105 + * @param userId 用户编号
  106 + */
  107 + void cancelConcernCustomer(List<Long> ids, Long userId);
  108 +
93 // ==================== 公海相关操作 ==================== 109 // ==================== 公海相关操作 ====================
94 110
95 /** 111 /**
@@ -109,8 +125,10 @@ public interface CrmCustomerService { @@ -109,8 +125,10 @@ public interface CrmCustomerService {
109 125
110 /** 126 /**
111 * 获取客户列表 127 * 获取客户列表
  128 + *
112 * @return 客户列表 129 * @return 客户列表
113 * @author zyna 130 * @author zyna
114 */ 131 */
115 List<CrmCustomerDO> getCustomerList(); 132 List<CrmCustomerDO> getCustomerList();
  133 +
116 } 134 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java
1 package cn.iocoder.yudao.module.crm.service.customer; 1 package cn.iocoder.yudao.module.crm.service.customer;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.ObjUtil;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerCreateReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerCreateReqVO;
6 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO; 8 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO;
7 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerTransferReqVO; 9 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerTransferReqVO;
@@ -9,9 +11,11 @@ import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerUpdat @@ -9,9 +11,11 @@ import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerUpdat
9 import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert; 11 import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert;
10 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 12 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
11 import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper; 13 import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper;
12 -import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission;  
13 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 14 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
14 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; 15 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
  16 +import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission;
  17 +import cn.iocoder.yudao.module.crm.service.concerned.CrmConcernedService;
  18 +import cn.iocoder.yudao.module.crm.service.concerned.bo.CrmConcernedCreateReqBO;
15 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; 19 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
16 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; 20 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
17 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 21 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
@@ -23,6 +27,7 @@ import javax.annotation.Resource; @@ -23,6 +27,7 @@ import javax.annotation.Resource;
23 import java.util.*; 27 import java.util.*;
24 28
25 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 29 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  30 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
26 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; 31 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
27 import static java.util.Collections.singletonList; 32 import static java.util.Collections.singletonList;
28 33
@@ -42,6 +47,9 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -42,6 +47,9 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
42 private CrmPermissionService crmPermissionService; 47 private CrmPermissionService crmPermissionService;
43 48
44 @Resource 49 @Resource
  50 + private CrmConcernedService crmConcernedService;
  51 +
  52 + @Resource
45 private AdminUserApi adminUserApi; 53 private AdminUserApi adminUserApi;
46 54
47 @Override 55 @Override
@@ -102,12 +110,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -102,12 +110,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
102 110
103 @Override 111 @Override
104 public PageResult<CrmCustomerDO> getCustomerPage(CrmCustomerPageReqVO pageReqVO, Long userId) { 112 public PageResult<CrmCustomerDO> getCustomerPage(CrmCustomerPageReqVO pageReqVO, Long userId) {
105 - boolean admin = false;  
106 - if (admin) { // 1.1. 情况一: TODO 如果是管理员; TODO @puhui999:要不如果是超管,就复用 selectPage;  
107 - customerMapper.selectPageWithAdmin(pageReqVO, userId);  
108 - }  
109 - // 1.2. 情况二:获取当前用户能看的分页数据  
110 - return customerMapper.selectPage(pageReqVO, userId); 113 + boolean admin = false; // TODO 如果是管理员
  114 + return customerMapper.selectPage(pageReqVO, userId, adminUserApi.getSubordinateIds(userId), admin);
111 } 115 }
112 116
113 /** 117 /**
@@ -152,6 +156,33 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -152,6 +156,33 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
152 } 156 }
153 157
154 @Override 158 @Override
  159 + public void concernCustomer(List<Long> ids, Long userId) {
  160 + // 1. 校验客户是否存在
  161 + validateCustomerExists(ids);
  162 +
  163 + // 2. 创建关注
  164 + List<CrmConcernedCreateReqBO> createReqBOs = BeanUtils.toBean(convertList(ids, id -> new CrmConcernedCreateReqBO()
  165 + .setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()).setBizId(id).setUserId(userId)), CrmConcernedCreateReqBO.class);
  166 + crmConcernedService.createConcernedBatch(createReqBOs);
  167 + }
  168 +
  169 + @Override
  170 + public void cancelConcernCustomer(List<Long> ids, Long userId) {
  171 + // 1. 校验客户是否存在
  172 + validateCustomerExists(ids);
  173 +
  174 + // 2. 取消关注
  175 + crmConcernedService.deleteConcerned(CrmBizTypeEnum.CRM_CUSTOMER.getType(), ids, userId);
  176 + }
  177 +
  178 + private void validateCustomerExists(List<Long> ids) {
  179 + List<CrmCustomerDO> customerList = customerMapper.selectBatchIds(ids);
  180 + if (ObjUtil.notEqual(ids.size(), customerList.size())) {
  181 + throw exception(CUSTOMER_NOT_EXISTS);
  182 + }
  183 + }
  184 +
  185 + @Override
155 @Transactional(rollbackFor = Exception.class) 186 @Transactional(rollbackFor = Exception.class)
156 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) 187 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
157 public void putCustomerPool(Long id) { 188 public void putCustomerPool(Long id) {
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmQueryPageUtils.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.util;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.ObjUtil;
  5 +import cn.iocoder.yudao.module.crm.dal.dataobject.concerned.CrmConcernedDO;
  6 +import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
  7 +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
  8 +import cn.iocoder.yudao.module.crm.enums.common.CrmSceneEnum;
  9 +import cn.iocoder.yudao.module.crm.framework.vo.CrmBasePageReqVO;
  10 +import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
  11 +import com.github.yulichang.wrapper.MPJLambdaWrapper;
  12 +
  13 +import javax.annotation.Nullable;
  14 +import java.util.Collection;
  15 +
  16 +/**
  17 + * CRM 分页查询工具类
  18 + *
  19 + * @author HUIHUI
  20 + */
  21 +public class CrmQueryPageUtils {
  22 +
  23 + /**
  24 + * 构造 crm 数据类型数据分页查询条件
  25 + *
  26 + * @param queryMapper 连表查询对象
  27 + * @param reqVO 查询条件
  28 + * @param userId 用户编号
  29 + * @param bizType 数据类型 {@link CrmBizTypeEnum}
  30 + * @param bizId 数据编号
  31 + * @param subordinateIds 下属用户编号,可为空
  32 + */
  33 + public static <T extends MPJLambdaWrapper<?>, V extends CrmBasePageReqVO, S> void builderQuery(T queryMapper, V reqVO, Long userId,
  34 + Integer bizType, SFunction<S, ?> bizId,
  35 + @Nullable Collection<Long> subordinateIds,
  36 + Boolean isAdmin) {
  37 + // 构建数据权限连表条件
  38 + if (ObjUtil.notEqual(isAdmin, Boolean.TRUE)) { // 管理员不需要数据权限
  39 + queryMapper.innerJoin(CrmPermissionDO.class, on ->
  40 + on.eq(CrmPermissionDO::getBizType, bizType).eq(CrmPermissionDO::getBizId, bizId)
  41 + .eq(CrmPermissionDO::getUserId, userId));
  42 + }
  43 + if (ObjUtil.equal(reqVO.getPool(), Boolean.TRUE)) { // 情况一:公海
  44 + queryMapper.isNull("owner_user_id");
  45 + } else { // 情况二:不是公海
  46 + queryMapper.isNotNull("owner_user_id");
  47 + }
  48 + // 场景数据过滤
  49 + if (CrmSceneEnum.isOwner(reqVO.getSceneType())) { // 场景一:我负责的数据
  50 + queryMapper.eq("owner_user_id", userId);
  51 + }
  52 + if (CrmSceneEnum.isFollow(reqVO.getSceneType())) { // 场景二:我关注的数据
  53 + queryMapper.innerJoin(CrmConcernedDO.class, on ->
  54 + on.eq(CrmConcernedDO::getBizType, bizType).eq(CrmConcernedDO::getBizId, bizId)
  55 + .eq(CrmConcernedDO::getUserId, userId));
  56 + }
  57 + // TODO puhui999: 这里有一个疑问:如果下属负责的数据权限中没有自己的话还能看吗?
  58 + if (CrmSceneEnum.isSubordinate(reqVO.getSceneType()) && CollUtil.isNotEmpty(subordinateIds)) { // 场景三:下属负责的数据
  59 + queryMapper.in("owner_user_id", subordinateIds);
  60 + }
  61 + }
  62 +
  63 +}
yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java
@@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
6 import java.util.Collection; 6 import java.util.Collection;
7 import java.util.List; 7 import java.util.List;
8 import java.util.Map; 8 import java.util.Map;
  9 +import java.util.Set;
9 10
10 /** 11 /**
11 * Admin 用户 API 接口 12 * Admin 用户 API 接口
@@ -23,6 +24,14 @@ public interface AdminUserApi { @@ -23,6 +24,14 @@ public interface AdminUserApi {
23 AdminUserRespDTO getUser(Long id); 24 AdminUserRespDTO getUser(Long id);
24 25
25 /** 26 /**
  27 + * 通过用户 ID 查询用户下属
  28 + *
  29 + * @param id 用户编号
  30 + * @return 用户下属用户编号列表
  31 + */
  32 + Set<Long> getSubordinateIds(Long id);
  33 +
  34 + /**
26 * 通过用户 ID 查询用户们 35 * 通过用户 ID 查询用户们
27 * 36 *
28 * @param ids 用户 ID 们 37 * @param ids 用户 ID 们
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java
1 package cn.iocoder.yudao.module.system.api.user; 1 package cn.iocoder.yudao.module.system.api.user;
2 2
3 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 3 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  4 +import cn.hutool.core.util.ObjUtil;
4 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 5 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  6 +import cn.iocoder.yudao.module.system.convert.user.UserConvert;
  7 +import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
5 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO; 8 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
  9 +import cn.iocoder.yudao.module.system.service.dept.DeptService;
6 import cn.iocoder.yudao.module.system.service.user.AdminUserService; 10 import cn.iocoder.yudao.module.system.service.user.AdminUserService;
7 import org.springframework.stereotype.Service; 11 import org.springframework.stereotype.Service;
8 12
9 import javax.annotation.Resource; 13 import javax.annotation.Resource;
10 import java.util.Collection; 14 import java.util.Collection;
  15 +import java.util.Collections;
11 import java.util.List; 16 import java.util.List;
  17 +import java.util.Set;
  18 +
  19 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
12 20
13 /** 21 /**
14 * Admin 用户 API 实现类 22 * Admin 用户 API 实现类
@@ -20,6 +28,8 @@ public class AdminUserApiImpl implements AdminUserApi { @@ -20,6 +28,8 @@ public class AdminUserApiImpl implements AdminUserApi {
20 28
21 @Resource 29 @Resource
22 private AdminUserService userService; 30 private AdminUserService userService;
  31 + @Resource
  32 + private DeptService deptService;
23 33
24 @Override 34 @Override
25 public AdminUserRespDTO getUser(Long id) { 35 public AdminUserRespDTO getUser(Long id) {
@@ -28,6 +38,22 @@ public class AdminUserApiImpl implements AdminUserApi { @@ -28,6 +38,22 @@ public class AdminUserApiImpl implements AdminUserApi {
28 } 38 }
29 39
30 @Override 40 @Override
  41 + public Set<Long> getSubordinateIds(Long id) {
  42 + AdminUserDO user = userService.getUser(id);
  43 + if (user == null) {
  44 + return null;
  45 + }
  46 +
  47 + Set<Long> subordinateIds = null; // 下属用户编号
  48 + DeptDO dept = deptService.getDept(user.getDeptId());
  49 + if (ObjUtil.equal(dept.getLeaderUserId(), id)) { // 校验是否是该部门的负责人
  50 + List<AdminUserDO> users = userService.getUserListByDeptIds(Collections.singletonList(dept.getId()));
  51 + subordinateIds = convertSet(users, AdminUserDO::getId);
  52 + }
  53 + return subordinateIds;
  54 + }
  55 +
  56 + @Override
31 public List<AdminUserRespDTO> getUserList(Collection<Long> ids) { 57 public List<AdminUserRespDTO> getUserList(Collection<Long> ids) {
32 List<AdminUserDO> users = userService.getUserList(ids); 58 List<AdminUserDO> users = userService.getUserList(ids);
33 return BeanUtils.toBean(users, AdminUserRespDTO.class); 59 return BeanUtils.toBean(users, AdminUserRespDTO.class);