diff --git a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmCustomerSceneEnum.java b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmSceneEnum.java similarity index 77% rename from yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmCustomerSceneEnum.java rename to yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmSceneEnum.java index 8443c7fde..7263a2313 100644 --- a/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmCustomerSceneEnum.java +++ b/yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmSceneEnum.java @@ -8,18 +8,18 @@ import lombok.Getter; import java.util.Arrays; /** - * CRM 客户等级 + * CRM 列表检索场景 * - * @author Wanwan + * @author HUIHUI */ @Getter @AllArgsConstructor -public enum CrmCustomerSceneEnum implements IntArrayValuable { +public enum CrmSceneEnum implements IntArrayValuable { - OWNER(1, "我负责的客户"), - FOLLOW(2, "我关注的客户"); + OWNER(1, "我负责的"), + FOLLOW(2, "我关注的"); - public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmCustomerSceneEnum::getType).toArray(); + public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(CrmSceneEnum::getType).toArray(); /** * 场景类型 diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java index f0da5eb65..8c48d8df2 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.crm.controller.admin.customer; +import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -9,7 +10,6 @@ import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.*; import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; -import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; @@ -51,8 +51,6 @@ public class CrmCustomerController { private DeptApi deptApi; @Resource private AdminUserApi adminUserApi; - @Resource - private CrmPermissionService permissionService; @PostMapping("/create") @Operation(summary = "创建客户") @@ -119,20 +117,14 @@ public class CrmCustomerController { return success(true); } - // TODO @puhui999:可以在 CrmCustomerPageReqVO 里面加个 pool 参数,为 true 时,代表来自公海客户的分页 @GetMapping("/page") @Operation(summary = "获得客户分页") @PreAuthorize("@ss.hasPermission('crm:customer:query')") public CommonResult> getCustomerPage(@Valid CrmCustomerPageReqVO pageVO) { - //PageResult pageResult = customerService.getCustomerPage(pageVO, getLoginUserId()); - //if (CollUtil.isEmpty(pageResult.getList())) { - // return success(PageResult.empty(pageResult.getTotal())); - //} - // 拼接数据 - return convertPage(customerService.getCustomerPage(pageVO, getLoginUserId())); - } - - private CommonResult> convertPage(PageResult pageResult) { + PageResult pageResult = customerService.getCustomerPage(pageVO, getLoginUserId()); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty(pageResult.getTotal())); + } // 1.1 获取负责人详情 Set userIds = convertSet(pageResult.getList(), CrmCustomerDO::getOwnerUserId); userIds.addAll(convertSet(pageResult.getList(), item -> Long.parseLong(item.getCreator()))); // 加入创建者 @@ -172,19 +164,19 @@ public class CrmCustomerController { return success(true); } - @PutMapping("/receive") - @Operation(summary = "领取公海客户") - // TODO @xiaqing:1)receiveCustomer 方法名字;2)cIds 改成 ids,要加下 @RequestParam,还有 swagger 注解;3)参数非空,使用 validator 校验;4)返回 true 即可; - @PreAuthorize("@ss.hasPermission('crm:customer:receive')") - public CommonResult receiveByIds(List cIds) { - // 判断是否为空 - if (CollectionUtils.isEmpty(cIds)) - return error(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), GlobalErrorCodeConstants.BAD_REQUEST.getMsg()); - // 领取公海任务 - // TODO @xiaqing:userid,通过 controller 传递给 service,不要在 service 里面获取,无状态 - customerService.receive(cIds); - return success("领取成功"); - } + //@PutMapping("/receive") + //@Operation(summary = "领取公海客户") + //// TODO @xiaqing:1)receiveCustomer 方法名字;2)cIds 改成 ids,要加下 @RequestParam,还有 swagger 注解;3)参数非空,使用 validator 校验;4)返回 true 即可; + //@PreAuthorize("@ss.hasPermission('crm:customer:receive')") + //public CommonResult receiveByIds(List cIds) { + // // 判断是否为空 + // if (CollectionUtils.isEmpty(cIds)) + // return error(GlobalErrorCodeConstants.BAD_REQUEST.getCode(), GlobalErrorCodeConstants.BAD_REQUEST.getMsg()); + // // 领取公海任务 + // // TODO @xiaqing:userid,通过 controller 传递给 service,不要在 service 里面获取,无状态 + // customerService.receive(cIds); + // return success("领取成功"); + //} // TODO @xiaqing:1)distributeCustomer 方法名;2)cIds 同上;3)参数校验,同上;4)ownerId 改成 ownerUserId,和别的模块统一;5)返回 true 即可; @PutMapping("/distributeByIds") diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerPageReqVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerPageReqVO.java index b7340d095..f1db9c522 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerPageReqVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerPageReqVO.java @@ -1,12 +1,14 @@ package cn.iocoder.yudao.module.crm.controller.admin.customer.vo; import cn.iocoder.yudao.framework.common.pojo.PageParam; -import cn.iocoder.yudao.module.crm.enums.common.CrmCustomerSceneEnum; +import cn.iocoder.yudao.module.crm.enums.common.CrmSceneEnum; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; +import javax.validation.constraints.NotNull; + @Schema(description = "管理后台 - CRM 客户分页 Request VO") @Data @EqualsAndHashCode(callSuper = true) @@ -31,9 +33,13 @@ public class CrmCustomerPageReqVO extends PageParam { /** * 场景类型 * - * 关联 {@link CrmCustomerSceneEnum} + * 关联 {@link CrmSceneEnum} */ @Schema(description = "场景类型", example = "1") private Integer sceneType; + @Schema(description = "是否为公海数据", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + @NotNull(message = "是否为公海数据不能为空") + private Boolean pool; + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java index f4293c565..b281b12e9 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java @@ -7,13 +7,12 @@ import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils; import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO; import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; +import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; +import cn.iocoder.yudao.module.crm.enums.common.CrmSceneEnum; import cn.iocoder.yudao.module.crm.framework.enums.CrmBizTypeEnum; -import cn.iocoder.yudao.module.crm.util.CrmPermissionUtils; import com.baomidou.mybatisplus.core.metadata.IPage; import org.apache.ibatis.annotations.Mapper; -import java.util.Collection; - /** * 客户 Mapper * @@ -22,9 +21,30 @@ import java.util.Collection; @Mapper public interface CrmCustomerMapper extends BaseMapperX { - default PageResult selectPage(CrmCustomerPageReqVO pageReqVO, Collection ids) { - return selectPage(pageReqVO, new LambdaQueryWrapperX() - .inIfPresent(CrmCustomerDO::getId, ids) + default PageResult selectPage(CrmCustomerPageReqVO pageReqVO) { + LambdaQueryWrapperX queryWrapperX = new LambdaQueryWrapperX<>(); + if (pageReqVO.getPool()) { // 情况一:公海 + queryWrapperX.isNull(CrmCustomerDO::getOwnerUserId); + } else {// 情况一:不是公海 + queryWrapperX.isNotNull(CrmCustomerDO::getOwnerUserId); + } + return selectPage(pageReqVO, queryWrapperX + .likeIfPresent(CrmCustomerDO::getName, pageReqVO.getName()) + .eqIfPresent(CrmCustomerDO::getMobile, pageReqVO.getMobile()) + .eqIfPresent(CrmCustomerDO::getIndustryId, pageReqVO.getIndustryId()) + .eqIfPresent(CrmCustomerDO::getLevel, pageReqVO.getLevel()) + .eqIfPresent(CrmCustomerDO::getSource, pageReqVO.getSource())); + } + + default PageResult selectPage1(CrmCustomerPageReqVO pageReqVO, Long userId) { + LambdaQueryWrapperX queryWrapperX = new LambdaQueryWrapperX<>(); + //queryWrapperX.sql + if (pageReqVO.getPool()) { // 情况一:公海 + queryWrapperX.isNull(CrmCustomerDO::getOwnerUserId); + } else {// 情况一:不是公海 + queryWrapperX.isNotNull(CrmCustomerDO::getOwnerUserId); + } + return selectPage(pageReqVO, queryWrapperX .likeIfPresent(CrmCustomerDO::getName, pageReqVO.getName()) .eqIfPresent(CrmCustomerDO::getMobile, pageReqVO.getMobile()) .eqIfPresent(CrmCustomerDO::getIndustryId, pageReqVO.getIndustryId()) @@ -33,11 +53,50 @@ public interface CrmCustomerMapper extends BaseMapperX { } default PageResult selectPage(CrmCustomerPageReqVO pageReqVO, Long userId) { - // MyBatis Plus 查询 IPage mpPage = MyBatisUtils.buildPage(pageReqVO); MPJLambdaWrapperX mpjLambdaWrapperX = new MPJLambdaWrapperX<>(); // 构建数据权限连表条件 - CrmPermissionUtils.builderLeftJoinQuery(mpjLambdaWrapperX, CrmBizTypeEnum.CRM_CUSTOMER.getType(), userId); + //CrmPermissionUtils.builderRightJoinQuery(mpjLambdaWrapperX, CrmBizTypeEnum.CRM_CUSTOMER.getType(), userId); + mpjLambdaWrapperX + //.rightJoin("(SELECT t1.biz_id FROM crm_permission t1 WHERE (t1.biz_type = 1 AND t1.user_id = 1)) t2 on t.id = t2.biz_id"); + .rightJoin(CrmPermissionDO.class, CrmPermissionDO::getBizId, CrmCustomerDO::getId) + .eq(CrmPermissionDO::getBizType, CrmBizTypeEnum.CRM_CUSTOMER.getType()) + .eq(CrmPermissionDO::getUserId, userId); + /** TODO @芋艿: + -- 常规连表-查询正常 + | ==> Preparing: + SELECT t.id, t.name, t.follow_up_status, t.lock_status, t.deal_status, + t.industry_id, t.level, t.source, t.mobile, t.telephone, t.website, + t.qq, t.wechat, t.email, t.description, t.remark, t.owner_user_id, + t.area_id, t.detail_address, t.contact_last_time, t.contact_next_time, + t.create_time, t.update_time, t.creator, t.updater, t.deleted + FROM crm_customer t RIGHT JOIN crm_permission t1 ON (t1.biz_id = t.id) AND t.tenant_id = 1 + WHERE t.deleted = 0 AND t1.deleted = 0 + AND (t1.biz_type = ? AND t1.user_id = ? + AND t.owner_user_id IS NOT NULL AND t.level = ?) AND t1.tenant_id = 1 LIMIT ? + | ==> Parameters: 2(Integer), 1(Long), 3(Integer), 10(Long) + + -- 连接子查询-报错,但是复制到 navicat 是可以正常执行的 + -- 区别点:常规连表会自动拼接租户 AND t.tenant_id = 1 + SELECT + t.id,t.name,t.follow_up_status,t.lock_status,t.deal_status,t.industry_id,t.level, + t.source,t.mobile,t.telephone,t.website,t.qq,t.wechat,t.email,t.description,t.remark, + t.owner_user_id,t.area_id,t.detail_address,t.contact_last_time,t.contact_next_time, + t.create_time,t.update_time,t.creator,t.updater,t.deleted + FROM crm_customer t + RIGHT JOIN (SELECT t1.biz_id FROM crm_permission t1 WHERE (t1.biz_type = 2 AND t1.user_id = 1)) t2 on t.id = t2.biz_id + WHERE t.deleted=0 + AND (t.owner_user_id IS NOT NULL) + */ + if (pageReqVO.getPool()) { // 情况一:公海 + mpjLambdaWrapperX.isNull(CrmCustomerDO::getOwnerUserId); + } else {// 情况一:不是公海 + mpjLambdaWrapperX.isNotNull(CrmCustomerDO::getOwnerUserId); + } + // TODO 场景数据过滤 + if (CrmSceneEnum.isOwner(pageReqVO.getSceneType())) { // 场景一:我负责的数据 + mpjLambdaWrapperX.eq(CrmCustomerDO::getOwnerUserId, userId); + } mpPage = selectJoinPage(mpPage, CrmCustomerDO.class, mpjLambdaWrapperX .selectAll(CrmCustomerDO.class) .likeIfPresent(CrmCustomerDO::getName, pageReqVO.getName()) diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java index 131a03ce4..e555896f1 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java @@ -39,6 +39,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { private CrmPermissionService crmPermissionService; @Override + @Transactional(rollbackFor = Exception.class) public Long createCustomer(CrmCustomerCreateReqVO createReqVO, Long userId) { // 插入 CrmCustomerDO customer = CrmCustomerConvert.INSTANCE.convert(createReqVO); @@ -89,32 +90,19 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @Override public PageResult getCustomerPage(CrmCustomerPageReqVO pageReqVO, Long userId) { - //// 1.1 TODO 如果是超级管理员 - //boolean admin = false; - //if (admin && ObjUtil.notEqual(userId, CrmPermissionDO.POOL_USER_ID)) { - // return customerMapper.selectPage(pageReqVO, Collections.emptyList()); - //} - //// 1.2 获取当前用户能看的分页数据 - //// TODO @puhui999:如果业务的数据量比较大,in 太多可能有性能问题噢;看看是不是搞成 join 连表了;可以微信讨论下; - //List permissions = crmPermissionService.getPermissionListByBizTypeAndUserId( - // CrmBizTypeEnum.CRM_CUSTOMER.getType(), userId); - //// 1.3 TODO 场景数据过滤 - //if (CrmCustomerSceneEnum.isOwner(pageReqVO.getSceneType())) { // 场景一:我负责的数据 - // permissions = CollectionUtils.filterList(permissions, item -> CrmPermissionLevelEnum.isOwner(item.getLevel())); - //} - //Set ids = convertSet(permissions, CrmPermissionDO::getBizId); - //if (CollUtil.isEmpty(ids)) { // 没得说明没有什么给他看的 - // return PageResult.empty(); - //} - // - //// 2. 获取客户分页数据 - //return customerMapper.selectPage(pageReqVO, ids); + // 1.1. TODO 如果是超级管理员 + boolean admin = false; + if (admin) { + return customerMapper.selectPage(pageReqVO); + } + // 1.2. 获取当前用户能看的分页数据 return customerMapper.selectPage(pageReqVO, userId); } @Override public List getCustomerList(CrmCustomerExportReqVO exportReqVO) { //return customerMapper.selectList(exportReqVO); + // TODO puhui999: 等数据权限完善后再实现 return Collections.emptyList(); } @@ -161,13 +149,13 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @Override @Transactional(rollbackFor = Exception.class) - public void receive(List ids) { - transferCustomerOwner(ids,SecurityFrameworkUtils.getLoginUserId()); + public void receive(List ids) { + transferCustomerOwner(ids, SecurityFrameworkUtils.getLoginUserId()); } @Override - public void distributeByIds(List cIds, Long ownerId) { - transferCustomerOwner(cIds,ownerId); + public void distributeByIds(List cIds, Long ownerId) { + transferCustomerOwner(cIds, ownerId); } @Override @@ -191,6 +179,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { } @Override + @Transactional(rollbackFor = Exception.class) @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) public void putPool(Long id) { // 1. 校验存在 @@ -214,7 +203,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { CrmPermissionLevelEnum.OWNER.getLevel()); } - private void transferCustomerOwner(List cIds, Long ownerId){ + private void transferCustomerOwner(List cIds, Long ownerId) { // 先一次性校验完成客户是否可用 // TODO @xiaqing:批量一次性加载客户列表,然后去逐个校验; for (Long cId : cIds) { @@ -228,8 +217,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { validCustomerDeal(cId); } // TODO @xiaqing:每个客户更新的时候,where 条件,加上 owner_user_id is null,防止并发问题; - List updateDos = new ArrayList <>(); - for (Long cId : cIds){ + List updateDos = new ArrayList<>(); + for (Long cId : cIds) { CrmCustomerDO customerDO = new CrmCustomerDO(); customerDO.setId(cId); customerDO.setOwnerUserId(SecurityFrameworkUtils.getLoginUserId()); @@ -239,19 +228,19 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { } private void validCustomerOwnerExist(Long id) { - if (customerMapper.selectById(id).getOwnerUserId()!=null) { + if (customerMapper.selectById(id).getOwnerUserId() != null) { throw exception(CUSTOMER_OWNER_EXISTS); } } private void validCustomerIsLocked(Long id) { - if (customerMapper.selectById(id).getLockStatus() ==true) { + if (customerMapper.selectById(id).getLockStatus() == true) { throw exception(CUSTOMER_LOCKED); } } private void validCustomerDeal(Long id) { - if (customerMapper.selectById(id).getDealStatus() ==true) { + if (customerMapper.selectById(id).getDealStatus() == true) { throw exception(CUSTOMER_ALREADY_DEAL); } } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmPermissionUtils.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmPermissionUtils.java index d2ec9e04c..675c507be 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmPermissionUtils.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/util/CrmPermissionUtils.java @@ -17,13 +17,10 @@ public class CrmPermissionUtils { * @param bizTyp 模块类型 * @param userId 用户 */ - public static void builderLeftJoinQuery(MPJLambdaWrapper mpjLambdaWrapper, Integer bizTyp, Long userId) { + public static void builderRightJoinQuery(MPJLambdaWrapper mpjLambdaWrapper, Integer bizTyp, Long userId) { + String querySql = "(SELECT t1.biz_id FROM crm_permission t1 WHERE (t1.biz_type = {} AND t1.user_id = {})) t2 on t.id = t2.biz_id"; // 默认主表别名是 t - mpjLambdaWrapper.leftJoin(StrUtil.format("(" + - "select p.biz_id from crm_permission p" + - " where p.biz_type = {} and p.user_id = {}" + - ") t2" + - " on t.id = t2.biz_id", bizTyp, userId)); + mpjLambdaWrapper.rightJoin(StrUtil.format(querySql, bizTyp, userId)); } }