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 69 // ========== 商机状态 1_020_011_000 ==========
70 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 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 17 public enum CrmSceneEnum implements IntArrayValuable {
18 18  
19 19 OWNER(1, "我负责的"),
20   - FOLLOW(2, "我关注的");
  20 + FOLLOW(2, "我关注的"),
  21 + SUBORDINATE(3, "下属负责的");
21 22  
22 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 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 46 @Override
42 47 public int[] array() {
43 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 36 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
37 37 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
38 38 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
39   -import static java.util.Collections.singletonList;
40 39  
41 40 @Tag(name = "管理后台 - CRM 客户")
42 41 @RestController
... ... @@ -140,6 +139,24 @@ public class CrmCustomerController {
140 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 162 @PutMapping("/put-pool")
... ... @@ -178,7 +195,7 @@ public class CrmCustomerController {
178 195 @GetMapping("/query-all-list")
179 196 @Operation(summary = "查询客户列表")
180 197 @PreAuthorize("@ss.hasPermission('crm:customer:all')")
181   - public CommonResult<List<CrmCustomerQueryAllRespVO>> queryAll(){
  198 + public CommonResult<List<CrmCustomerQueryAllRespVO>> queryAll() {
182 199 List<CrmCustomerDO> crmCustomerDOList = customerService.getCustomerList();
183 200 List<CrmCustomerQueryAllRespVO> data = CrmCustomerConvert.INSTANCE.convertQueryAll(crmCustomerDOList);
184 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 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 4 import io.swagger.v3.oas.annotations.media.Schema;
6 5 import lombok.Data;
7 6 import lombok.EqualsAndHashCode;
8 7 import lombok.ToString;
9 8  
10   -import javax.validation.constraints.NotNull;
11   -
12 9 @Schema(description = "管理后台 - CRM 客户分页 Request VO")
13 10 @Data
14 11 @EqualsAndHashCode(callSuper = true)
15 12 @ToString(callSuper = true)
16   -public class CrmCustomerPageReqVO extends PageParam {
  13 +public class CrmCustomerPageReqVO extends CrmBasePageReqVO {
17 14  
18 15 @Schema(description = "客户名称", example = "赵六")
19 16 private String name;
... ... @@ -30,16 +27,4 @@ public class CrmCustomerPageReqVO extends PageParam {
30 27 @Schema(description = "客户来源", example = "1")
31 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 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 3 import cn.iocoder.yudao.framework.common.pojo.PageParam;
4 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 5 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
6   -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
7 6 import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
8 7 import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
9 8 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO;
10 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 10 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
  11 +import cn.iocoder.yudao.module.crm.util.CrmQueryPageUtils;
14 12 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
15 13 import com.baomidou.mybatisplus.core.metadata.IPage;
16 14 import org.apache.ibatis.annotations.Mapper;
17 15  
  16 +import java.util.Collection;
18 17 import java.util.List;
19 18  
20 19 /**
... ... @@ -31,28 +30,21 @@ public interface CrmCustomerMapper extends BaseMapperX&lt;CrmCustomerDO&gt; {
31 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 43 IPage<CrmCustomerDO> mpPage = MyBatisUtils.buildPage(pageReqVO);
49 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 48 mpjLambdaWrapperX
57 49 .selectAll(CrmCustomerDO.class)
58 50 .likeIfPresent(CrmCustomerDO::getName, pageReqVO.getName())
... ... @@ -69,28 +61,4 @@ public interface CrmCustomerMapper extends BaseMapperX&lt;CrmCustomerDO&gt; {
69 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 90 */
91 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 125  
110 126 /**
111 127 * 获取客户列表
  128 + *
112 129 * @return 客户列表
113 130 * @author zyna
114 131 */
115 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 1 package cn.iocoder.yudao.module.crm.service.customer;
2 2  
3 3 import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.ObjUtil;
4 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 7 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerCreateReqVO;
6 8 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO;
7 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 11 import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert;
10 12 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
11 13 import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerMapper;
12   -import cn.iocoder.yudao.module.crm.framework.core.annotations.CrmPermission;
13 14 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
14 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 19 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
16 20 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
17 21 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
... ... @@ -23,6 +27,7 @@ import javax.annotation.Resource;
23 27 import java.util.*;
24 28  
25 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 31 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
27 32 import static java.util.Collections.singletonList;
28 33  
... ... @@ -42,6 +47,9 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
42 47 private CrmPermissionService crmPermissionService;
43 48  
44 49 @Resource
  50 + private CrmConcernedService crmConcernedService;
  51 +
  52 + @Resource
45 53 private AdminUserApi adminUserApi;
46 54  
47 55 @Override
... ... @@ -102,12 +110,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
102 110  
103 111 @Override
104 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 156 }
153 157  
154 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 186 @Transactional(rollbackFor = Exception.class)
156 187 @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
157 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 import java.util.Collection;
7 7 import java.util.List;
8 8 import java.util.Map;
  9 +import java.util.Set;
9 10  
10 11 /**
11 12 * Admin 用户 API 接口
... ... @@ -23,6 +24,14 @@ public interface AdminUserApi {
23 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 35 * 通过用户 ID 查询用户们
27 36 *
28 37 * @param ids 用户 ID 们
... ...
yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java
1 1 package cn.iocoder.yudao.module.system.api.user;
2 2  
3 3 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
  4 +import cn.hutool.core.util.ObjUtil;
4 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 8 import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
  9 +import cn.iocoder.yudao.module.system.service.dept.DeptService;
6 10 import cn.iocoder.yudao.module.system.service.user.AdminUserService;
7 11 import org.springframework.stereotype.Service;
8 12  
9 13 import javax.annotation.Resource;
10 14 import java.util.Collection;
  15 +import java.util.Collections;
11 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 22 * Admin 用户 API 实现类
... ... @@ -20,6 +28,8 @@ public class AdminUserApiImpl implements AdminUserApi {
20 28  
21 29 @Resource
22 30 private AdminUserService userService;
  31 + @Resource
  32 + private DeptService deptService;
23 33  
24 34 @Override
25 35 public AdminUserRespDTO getUser(Long id) {
... ... @@ -28,6 +38,22 @@ public class AdminUserApiImpl implements AdminUserApi {
28 38 }
29 39  
30 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 57 public List<AdminUserRespDTO> getUserList(Collection<Long> ids) {
32 58 List<AdminUserDO> users = userService.getUserList(ids);
33 59 return BeanUtils.toBean(users, AdminUserRespDTO.class);
... ...