Commit 4b41c3c69230cc49913c7612f29bc5abfab69dc0
1 parent
b444312e
✨ CRM:优化客户的 backlog 逻辑
Showing
4 changed files
with
65 additions
and
71 deletions
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java
| ... | ... | @@ -174,15 +174,15 @@ public class CrmCustomerController { |
| 174 | 174 | return success(customerService.getPutPoolRemindCustomerCount(getLoginUserId())); |
| 175 | 175 | } |
| 176 | 176 | |
| 177 | - @GetMapping("/today-customer-count") | |
| 177 | + @GetMapping("/today-contact-count") | |
| 178 | 178 | @Operation(summary = "获得今日需联系客户数量") |
| 179 | 179 | @PreAuthorize("@ss.hasPermission('crm:customer:query')") |
| 180 | - public CommonResult<Long> getTodayCustomerCount() { | |
| 181 | - return success(customerService.getTodayCustomerCount(getLoginUserId())); | |
| 180 | + public CommonResult<Long> getTodayContactCustomerCount() { | |
| 181 | + return success(customerService.getTodayContactCustomerCount(getLoginUserId())); | |
| 182 | 182 | } |
| 183 | 183 | |
| 184 | - @GetMapping("/follow-customer-count") | |
| 185 | - @Operation(summary = "获得分配给我的客户数量") | |
| 184 | + @GetMapping("/follow-count") | |
| 185 | + @Operation(summary = "获得分配给我、待跟进的线索数量的客户数量") | |
| 186 | 186 | @PreAuthorize("@ss.hasPermission('crm:customer:query')") |
| 187 | 187 | public CommonResult<Long> getFollowCustomerCount() { |
| 188 | 188 | return success(customerService.getFollowCustomerCount(getLoginUserId())); |
| ... | ... | @@ -222,9 +222,9 @@ public class CrmCustomerController { |
| 222 | 222 | }); |
| 223 | 223 | } |
| 224 | 224 | |
| 225 | - @GetMapping(value = "/list-all-simple") | |
| 225 | + @GetMapping(value = "/simple-list") | |
| 226 | 226 | @Operation(summary = "获取客户精简信息列表", description = "只包含有读权限的客户,主要用于前端的下拉选项") |
| 227 | - public CommonResult<List<CrmCustomerRespVO>> getSimpleDeptList() { | |
| 227 | + public CommonResult<List<CrmCustomerRespVO>> getCustomerSimpleList() { | |
| 228 | 228 | CrmCustomerPageReqVO reqVO = new CrmCustomerPageReqVO(); |
| 229 | 229 | reqVO.setPageSize(PAGE_SIZE_NONE); // 不分页 |
| 230 | 230 | List<CrmCustomerDO> list = customerService.getCustomerPage(reqVO, getLoginUserId()).getList(); |
| ... | ... | @@ -232,7 +232,6 @@ public class CrmCustomerController { |
| 232 | 232 | new CrmCustomerRespVO().setId(customer.getId()).setName(customer.getName()))); |
| 233 | 233 | } |
| 234 | 234 | |
| 235 | - // TODO @puhui999:公海的导出,前端可以接下 | |
| 236 | 235 | @GetMapping("/export-excel") |
| 237 | 236 | @Operation(summary = "导出客户 Excel") |
| 238 | 237 | @PreAuthorize("@ss.hasPermission('crm:customer:export')") | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java
| ... | ... | @@ -49,11 +49,11 @@ public interface CrmCustomerMapper extends BaseMapperX<CrmCustomerDO> { |
| 49 | 49 | .set(CrmCustomerDO::getOwnerUserId, ownerUserId)); |
| 50 | 50 | } |
| 51 | 51 | |
| 52 | - default PageResult<CrmCustomerDO> selectPage(CrmCustomerPageReqVO pageReqVO, Long userId) { | |
| 52 | + default PageResult<CrmCustomerDO> selectPage(CrmCustomerPageReqVO pageReqVO, Long ownerUserId) { | |
| 53 | 53 | MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>(); |
| 54 | 54 | // 拼接数据权限的查询条件 |
| 55 | 55 | CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(), |
| 56 | - CrmCustomerDO::getId, userId, pageReqVO.getSceneType(), pageReqVO.getPool()); | |
| 56 | + CrmCustomerDO::getId, ownerUserId, pageReqVO.getSceneType(), pageReqVO.getPool()); | |
| 57 | 57 | // 拼接自身的查询条件 |
| 58 | 58 | query.selectAll(CrmCustomerDO.class) |
| 59 | 59 | .likeIfPresent(CrmCustomerDO::getName, pageReqVO.getName()) |
| ... | ... | @@ -81,10 +81,10 @@ public interface CrmCustomerMapper extends BaseMapperX<CrmCustomerDO> { |
| 81 | 81 | return selectJoinPage(pageReqVO, CrmCustomerDO.class, query); |
| 82 | 82 | } |
| 83 | 83 | |
| 84 | - default List<CrmCustomerDO> selectBatchIds(Collection<Long> ids, Long userId) { | |
| 84 | + default List<CrmCustomerDO> selectBatchIds(Collection<Long> ids, Long ownerUserId) { | |
| 85 | 85 | MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>(); |
| 86 | 86 | // 拼接数据权限的查询条件 |
| 87 | - CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(), ids, userId); | |
| 87 | + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(), ids, ownerUserId); | |
| 88 | 88 | // 拼接自身的查询条件 |
| 89 | 89 | query.selectAll(CrmCustomerDO.class).in(CrmCustomerDO::getId, ids).orderByDesc(CrmCustomerDO::getId); |
| 90 | 90 | return selectJoinList(CrmCustomerDO.class, query); |
| ... | ... | @@ -96,8 +96,8 @@ public interface CrmCustomerMapper extends BaseMapperX<CrmCustomerDO> { |
| 96 | 96 | |
| 97 | 97 | default PageResult<CrmCustomerDO> selectPutPoolRemindCustomerPage(CrmCustomerPageReqVO pageReqVO, |
| 98 | 98 | CrmCustomerPoolConfigDO poolConfig, |
| 99 | - Long userId) { | |
| 100 | - final MPJLambdaWrapperX<CrmCustomerDO> query = buildPutPoolRemindCustomerQuery(pageReqVO, poolConfig, userId); | |
| 99 | + Long ownerUserId) { | |
| 100 | + final MPJLambdaWrapperX<CrmCustomerDO> query = buildPutPoolRemindCustomerQuery(pageReqVO, poolConfig, ownerUserId); | |
| 101 | 101 | return selectJoinPage(pageReqVO, CrmCustomerDO.class, query.selectAll(CrmCustomerDO.class)); |
| 102 | 102 | } |
| 103 | 103 | |
| ... | ... | @@ -110,11 +110,11 @@ public interface CrmCustomerMapper extends BaseMapperX<CrmCustomerDO> { |
| 110 | 110 | |
| 111 | 111 | private static MPJLambdaWrapperX<CrmCustomerDO> buildPutPoolRemindCustomerQuery(CrmCustomerPageReqVO pageReqVO, |
| 112 | 112 | CrmCustomerPoolConfigDO poolConfig, |
| 113 | - Long userId) { | |
| 113 | + Long ownerUserId) { | |
| 114 | 114 | MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>(); |
| 115 | 115 | // 拼接数据权限的查询条件 |
| 116 | 116 | CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(), |
| 117 | - CrmCustomerDO::getId, userId, pageReqVO.getSceneType(), null); | |
| 117 | + CrmCustomerDO::getId, ownerUserId, pageReqVO.getSceneType(), null); | |
| 118 | 118 | |
| 119 | 119 | // 未锁定 + 未成交 |
| 120 | 120 | query.eq(CrmCustomerDO::getLockStatus, false).eq(CrmCustomerDO::getDealStatus, false); |
| ... | ... | @@ -164,11 +164,11 @@ public interface CrmCustomerMapper extends BaseMapperX<CrmCustomerDO> { |
| 164 | 164 | return selectList(query); |
| 165 | 165 | } |
| 166 | 166 | |
| 167 | - default Long selectTodayCustomerCount(Long userId) { | |
| 167 | + default Long selectCountByTodayContact(Long ownerUserId) { | |
| 168 | 168 | MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>(); |
| 169 | 169 | // 我负责的 + 非公海 |
| 170 | 170 | CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(), |
| 171 | - CrmCustomerDO::getId, userId, CrmSceneTypeEnum.OWNER.getType(), Boolean.FALSE); | |
| 171 | + CrmCustomerDO::getId, ownerUserId, CrmSceneTypeEnum.OWNER.getType(), Boolean.FALSE); | |
| 172 | 172 | // 今天需联系 |
| 173 | 173 | LocalDateTime beginOfToday = LocalDateTimeUtil.beginOfDay(LocalDateTime.now()); |
| 174 | 174 | LocalDateTime endOfToday = LocalDateTimeUtil.endOfDay(LocalDateTime.now()); |
| ... | ... | @@ -176,11 +176,11 @@ public interface CrmCustomerMapper extends BaseMapperX<CrmCustomerDO> { |
| 176 | 176 | return selectCount(query); |
| 177 | 177 | } |
| 178 | 178 | |
| 179 | - default Long selectFollowCustomerCount(Long userId) { | |
| 179 | + default Long selectCountByFollow(Long ownerUserId) { | |
| 180 | 180 | MPJLambdaWrapperX<CrmCustomerDO> query = new MPJLambdaWrapperX<>(); |
| 181 | 181 | // 我负责的 + 非公海 |
| 182 | 182 | CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CUSTOMER.getType(), |
| 183 | - CrmCustomerDO::getId, userId, CrmSceneTypeEnum.OWNER.getType(), Boolean.FALSE); | |
| 183 | + CrmCustomerDO::getId, ownerUserId, CrmSceneTypeEnum.OWNER.getType(), Boolean.FALSE); | |
| 184 | 184 | // 未跟进 |
| 185 | 185 | query.eq(CrmClueDO::getFollowUpStatus, false); |
| 186 | 186 | return selectCount(query); | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java
| ... | ... | @@ -114,6 +114,22 @@ public interface CrmCustomerService { |
| 114 | 114 | Long getPutPoolRemindCustomerCount(Long userId); |
| 115 | 115 | |
| 116 | 116 | /** |
| 117 | + * 获得今日需联系客户数量 | |
| 118 | + * | |
| 119 | + * @param userId 用户编号 | |
| 120 | + * @return 提醒数量 | |
| 121 | + */ | |
| 122 | + Long getTodayContactCustomerCount(Long userId); | |
| 123 | + | |
| 124 | + /** | |
| 125 | + * 获得分配给我的客户数量 | |
| 126 | + * | |
| 127 | + * @param userId 用户编号 | |
| 128 | + * @return 提醒数量 | |
| 129 | + */ | |
| 130 | + Long getFollowCustomerCount(Long userId); | |
| 131 | + | |
| 132 | + /** | |
| 117 | 133 | * 校验客户是否存在 |
| 118 | 134 | * |
| 119 | 135 | * @param customerId 客户 id |
| ... | ... | @@ -168,7 +184,7 @@ public interface CrmCustomerService { |
| 168 | 184 | * |
| 169 | 185 | * @param ids 要领取的客户编号数组 |
| 170 | 186 | * @param ownerUserId 负责人 |
| 171 | - * @param isReceive 是/否领取 | |
| 187 | + * @param isReceive 是/否领取;true - 领取;false - 分配 | |
| 172 | 188 | */ |
| 173 | 189 | void receiveCustomer(List<Long> ids, Long ownerUserId, Boolean isReceive); |
| 174 | 190 | |
| ... | ... | @@ -179,20 +195,4 @@ public interface CrmCustomerService { |
| 179 | 195 | */ |
| 180 | 196 | int autoPutCustomerPool(); |
| 181 | 197 | |
| 182 | - /** | |
| 183 | - * 获得今日需联系客户数量 | |
| 184 | - * | |
| 185 | - * @param userId 用户编号 | |
| 186 | - * @return 提醒数量 | |
| 187 | - */ | |
| 188 | - Long getTodayCustomerCount(Long userId); | |
| 189 | - | |
| 190 | - /** | |
| 191 | - * 获得分配给我的客户数量 | |
| 192 | - * | |
| 193 | - * @param userId 用户编号 | |
| 194 | - * @return 提醒数量 | |
| 195 | - */ | |
| 196 | - Long getFollowCustomerCount(Long userId); | |
| 197 | - | |
| 198 | 198 | } | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java
| ... | ... | @@ -18,7 +18,6 @@ import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; |
| 18 | 18 | import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum; |
| 19 | 19 | import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; |
| 20 | 20 | import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission; |
| 21 | -import cn.iocoder.yudao.module.crm.framework.permission.core.util.CrmPermissionUtils; | |
| 22 | 21 | import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; |
| 23 | 22 | import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; |
| 24 | 23 | import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; |
| ... | ... | @@ -191,23 +190,6 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { |
| 191 | 190 | LogRecordContext.putVariable("customerName", customer.getName()); |
| 192 | 191 | } |
| 193 | 192 | |
| 194 | - /** | |
| 195 | - * 校验客户是否被引用 | |
| 196 | - * | |
| 197 | - * @param id 客户编号 | |
| 198 | - */ | |
| 199 | - private void validateCustomerReference(Long id) { | |
| 200 | - if (contactService.getContactCountByCustomerId(id) > 0) { | |
| 201 | - throw exception(CUSTOMER_DELETE_FAIL_HAVE_REFERENCE, CrmBizTypeEnum.CRM_CONTACT.getName()); | |
| 202 | - } | |
| 203 | - if (businessService.getBusinessCountByCustomerId(id) > 0) { | |
| 204 | - throw exception(CUSTOMER_DELETE_FAIL_HAVE_REFERENCE, CrmBizTypeEnum.CRM_BUSINESS.getName()); | |
| 205 | - } | |
| 206 | - if (contractService.getContractCountByCustomerId(id) > 0) { | |
| 207 | - throw exception(CUSTOMER_DELETE_FAIL_HAVE_REFERENCE, CrmBizTypeEnum.CRM_CONTRACT.getName()); | |
| 208 | - } | |
| 209 | - } | |
| 210 | - | |
| 211 | 193 | @Override |
| 212 | 194 | @Transactional(rollbackFor = Exception.class) |
| 213 | 195 | @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.id}}", |
| ... | ... | @@ -336,13 +318,6 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { |
| 336 | 318 | LogRecordContext.putVariable("isUpdate", isUpdate); |
| 337 | 319 | } |
| 338 | 320 | |
| 339 | - private void validateCustomerForCreate(CrmCustomerImportExcelVO importCustomer) { | |
| 340 | - // 校验客户名称不能为空 | |
| 341 | - if (StrUtil.isEmptyIfStr(importCustomer.getName())) { | |
| 342 | - throw exception(CUSTOMER_CREATE_NAME_NOT_NULL); | |
| 343 | - } | |
| 344 | - } | |
| 345 | - | |
| 346 | 321 | // ==================== 公海相关操作 ==================== |
| 347 | 322 | |
| 348 | 323 | @Override |
| ... | ... | @@ -371,18 +346,14 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { |
| 371 | 346 | @Override |
| 372 | 347 | @Transactional(rollbackFor = Exception.class) |
| 373 | 348 | public void receiveCustomer(List<Long> ids, Long ownerUserId, Boolean isReceive) { |
| 374 | - if (!isReceive && !CrmPermissionUtils.isCrmAdmin()) { // 只有管理员可以分配 | |
| 375 | - throw exception(CRM_PERMISSION_DENIED, CrmBizTypeEnum.CRM_CUSTOMER.getName()); | |
| 376 | - } | |
| 377 | - | |
| 378 | 349 | // 1.1 校验存在 |
| 379 | 350 | List<CrmCustomerDO> customers = customerMapper.selectBatchIds(ids); |
| 380 | 351 | if (customers.size() != ids.size()) { |
| 381 | 352 | throw exception(CUSTOMER_NOT_EXISTS); |
| 382 | 353 | } |
| 383 | - // 1.2. 校验负责人是否存在 | |
| 354 | + // 1.2 校验负责人是否存在 | |
| 384 | 355 | adminUserApi.validateUserList(singletonList(ownerUserId)); |
| 385 | - // 1.3. 校验状态 | |
| 356 | + // 1.3 校验状态 | |
| 386 | 357 | customers.forEach(customer -> { |
| 387 | 358 | // 校验是否已有负责人 |
| 388 | 359 | validateCustomerOwnerExists(customer, false); |
| ... | ... | @@ -513,17 +484,41 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { |
| 513 | 484 | } |
| 514 | 485 | |
| 515 | 486 | @Override |
| 516 | - public Long getTodayCustomerCount(Long userId) { | |
| 517 | - return customerMapper.selectTodayCustomerCount(userId); | |
| 487 | + public Long getTodayContactCustomerCount(Long userId) { | |
| 488 | + return customerMapper.selectCountByTodayContact(userId); | |
| 518 | 489 | } |
| 519 | 490 | |
| 520 | 491 | @Override |
| 521 | 492 | public Long getFollowCustomerCount(Long userId) { |
| 522 | - return customerMapper.selectFollowCustomerCount(userId); | |
| 493 | + return customerMapper.selectCountByFollow(userId); | |
| 523 | 494 | } |
| 524 | 495 | |
| 525 | 496 | // ======================= 校验相关 ======================= |
| 526 | 497 | |
| 498 | + private void validateCustomerForCreate(CrmCustomerImportExcelVO importCustomer) { | |
| 499 | + // 校验客户名称不能为空 | |
| 500 | + if (StrUtil.isEmptyIfStr(importCustomer.getName())) { | |
| 501 | + throw exception(CUSTOMER_CREATE_NAME_NOT_NULL); | |
| 502 | + } | |
| 503 | + } | |
| 504 | + | |
| 505 | + /** | |
| 506 | + * 校验客户是否被引用 | |
| 507 | + * | |
| 508 | + * @param id 客户编号 | |
| 509 | + */ | |
| 510 | + private void validateCustomerReference(Long id) { | |
| 511 | + if (contactService.getContactCountByCustomerId(id) > 0) { | |
| 512 | + throw exception(CUSTOMER_DELETE_FAIL_HAVE_REFERENCE, CrmBizTypeEnum.CRM_CONTACT.getName()); | |
| 513 | + } | |
| 514 | + if (businessService.getBusinessCountByCustomerId(id) > 0) { | |
| 515 | + throw exception(CUSTOMER_DELETE_FAIL_HAVE_REFERENCE, CrmBizTypeEnum.CRM_BUSINESS.getName()); | |
| 516 | + } | |
| 517 | + if (contractService.getContractCountByCustomerId(id) > 0) { | |
| 518 | + throw exception(CUSTOMER_DELETE_FAIL_HAVE_REFERENCE, CrmBizTypeEnum.CRM_CONTRACT.getName()); | |
| 519 | + } | |
| 520 | + } | |
| 521 | + | |
| 527 | 522 | /** |
| 528 | 523 | * 校验客户是否存在 |
| 529 | 524 | * | ... | ... |