Commit 69a974ef02f1ce829dbf20acb9fca72199c12862
1 parent
bac476c1
📖 CRM:线索的跟进逻辑
Showing
11 changed files
with
110 additions
and
164 deletions
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
| ... | ... | @@ -16,8 +16,7 @@ public interface ErrorCodeConstants { |
| 16 | 16 | |
| 17 | 17 | // ========== 线索管理 1-020-001-000 ========== |
| 18 | 18 | ErrorCode CLUE_NOT_EXISTS = new ErrorCode(1_020_001_000, "线索不存在"); |
| 19 | - ErrorCode CLUE_NOT_EXISTS_ANY = new ErrorCode(1_020_001_001, "线索【{}】不存在"); | |
| 20 | - ErrorCode CLUE_TRANSFORM_FAIL_ALREADY = new ErrorCode(1_020_001_002, "线索【{}】已经转化过了,请勿重复转化"); | |
| 19 | + ErrorCode CLUE_TRANSFORM_FAIL_ALREADY = new ErrorCode(1_020_001_001, "线索已经转化过了,请勿重复转化"); | |
| 21 | 20 | |
| 22 | 21 | // ========== 商机管理 1-020-002-000 ========== |
| 23 | 22 | ErrorCode BUSINESS_NOT_EXISTS = new ErrorCode(1_020_002_000, "商机不存在"); | ... | ... |
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java
| ... | ... | @@ -8,19 +8,21 @@ package cn.iocoder.yudao.module.crm.enums; |
| 8 | 8 | */ |
| 9 | 9 | public interface LogRecordConstants { |
| 10 | 10 | |
| 11 | - // ======================= CRM_LEADS 线索 ======================= | |
| 12 | - | |
| 13 | - String CRM_LEADS_TYPE = "CRM 线索"; | |
| 14 | - String CRM_LEADS_CREATE_SUB_TYPE = "创建线索"; | |
| 15 | - String CRM_LEADS_CREATE_SUCCESS = "创建了线索{{#clue.name}}"; | |
| 16 | - String CRM_LEADS_UPDATE_SUB_TYPE = "更新线索"; | |
| 17 | - String CRM_LEADS_UPDATE_SUCCESS = "更新了线索【{{#clueName}}】: {_DIFF{#updateReq}}"; | |
| 18 | - String CRM_LEADS_DELETE_SUB_TYPE = "删除线索"; | |
| 19 | - String CRM_LEADS_DELETE_SUCCESS = "删除了线索【{{#clueName}}】"; | |
| 20 | - String CRM_LEADS_TRANSFER_SUB_TYPE = "转移线索"; | |
| 21 | - String CRM_LEADS_TRANSFER_SUCCESS = "将线索【{{#clue.name}}】的负责人从【{getAdminUserById{#clue.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】"; | |
| 22 | - String CRM_LEADS_TRANSLATE_SUB_TYPE = "线索转化为客户"; | |
| 23 | - String CRM_LEADS_TRANSLATE_SUCCESS = "将线索【{{#clue.name}}】转化为客户"; | |
| 11 | + // ======================= CRM_CLUE 线索 ======================= | |
| 12 | + | |
| 13 | + String CRM_CLUE_TYPE = "CRM 线索"; | |
| 14 | + String CRM_CLUE_CREATE_SUB_TYPE = "创建线索"; | |
| 15 | + String CRM_CLUE_CREATE_SUCCESS = "创建了线索{{#clue.name}}"; | |
| 16 | + String CRM_CLUE_UPDATE_SUB_TYPE = "更新线索"; | |
| 17 | + String CRM_CLUE_UPDATE_SUCCESS = "更新了线索【{{#clueName}}】: {_DIFF{#updateReq}}"; | |
| 18 | + String CRM_CLUE_DELETE_SUB_TYPE = "删除线索"; | |
| 19 | + String CRM_CLUE_DELETE_SUCCESS = "删除了线索【{{#clueName}}】"; | |
| 20 | + String CRM_CLUE_TRANSFER_SUB_TYPE = "转移线索"; | |
| 21 | + String CRM_CLUE_TRANSFER_SUCCESS = "将线索【{{#clue.name}}】的负责人从【{getAdminUserById{#clue.ownerUserId}}】变更为了【{getAdminUserById{#reqVO.newOwnerUserId}}】"; | |
| 22 | + String CRM_CLUE_TRANSLATE_SUB_TYPE = "线索转化为客户"; | |
| 23 | + String CRM_CLUE_TRANSLATE_SUCCESS = "将线索【{{#clueName}}】转化为客户"; | |
| 24 | + String CRM_CLUE_FOLLOW_UP_SUB_TYPE = "线索跟进"; | |
| 25 | + String CRM_CLUE_FOLLOW_UP_SUCCESS = "线索跟进【{{#clueName}}】"; | |
| 24 | 26 | |
| 25 | 27 | // ======================= CRM_CUSTOMER 客户 ======================= |
| 26 | 28 | ... | ... |
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/common/CrmBizTypeEnum.java
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java
| ... | ... | @@ -9,7 +9,10 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 9 | 9 | import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; |
| 10 | 10 | import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; |
| 11 | 11 | import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; |
| 12 | -import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.*; | |
| 12 | +import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO; | |
| 13 | +import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueRespVO; | |
| 14 | +import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueSaveReqVO; | |
| 15 | +import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransferReqVO; | |
| 13 | 16 | import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO; |
| 14 | 17 | import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; |
| 15 | 18 | import cn.iocoder.yudao.module.crm.service.clue.CrmClueService; |
| ... | ... | @@ -36,7 +39,8 @@ import java.util.stream.Stream; |
| 36 | 39 | |
| 37 | 40 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 38 | 41 | import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; |
| 39 | -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; | |
| 42 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap; | |
| 43 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | |
| 40 | 44 | import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; |
| 41 | 45 | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; |
| 42 | 46 | |
| ... | ... | @@ -151,18 +155,18 @@ public class CrmClueController { |
| 151 | 155 | |
| 152 | 156 | @PutMapping("/transform") |
| 153 | 157 | @Operation(summary = "线索转化为客户") |
| 154 | - @Parameter(name = "ids", description = "线索编号数组", required = true) | |
| 158 | + @Parameter(name = "id", description = "编号", required = true) | |
| 155 | 159 | @PreAuthorize("@ss.hasPermission('crm:clue:update')") |
| 156 | - public CommonResult<Boolean> transformClue(@RequestParam("ids") List<Long> ids) { | |
| 157 | - clueService.transformClue(ids, getLoginUserId()); | |
| 160 | + public CommonResult<Boolean> transformClue(@RequestParam("id") Long id) { | |
| 161 | + clueService.transformClue(id, getLoginUserId()); | |
| 158 | 162 | return success(Boolean.TRUE); |
| 159 | 163 | } |
| 160 | 164 | |
| 161 | - @GetMapping("/follow-leads-count") | |
| 162 | - @Operation(summary = "获得分配给我的线索数量") | |
| 165 | + @GetMapping("/follow-count") | |
| 166 | + @Operation(summary = "获得分配给我的、待跟进的线索数量") | |
| 163 | 167 | @PreAuthorize("@ss.hasPermission('crm:clue:query')") |
| 164 | - public CommonResult<Long> getFollowLeadsCount() { | |
| 165 | - return success(clueService.getFollowLeadsCount(getLoginUserId())); | |
| 168 | + public CommonResult<Long> getFollowClueCount() { | |
| 169 | + return success(clueService.getFollowClueCount(getLoginUserId())); | |
| 166 | 170 | } |
| 167 | 171 | |
| 168 | 172 | } | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/CrmOperateLogController.java
| ... | ... | @@ -41,7 +41,7 @@ public class CrmOperateLogController { |
| 41 | 41 | private static final Map<Integer, String> BIZ_TYPE_MAP = new HashMap<>(); |
| 42 | 42 | |
| 43 | 43 | static { |
| 44 | - BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_LEADS.getType(), CRM_LEADS_TYPE); | |
| 44 | + BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_CLUE.getType(), CRM_CLUE_TYPE); | |
| 45 | 45 | BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_CUSTOMER.getType(), CRM_CUSTOMER_TYPE); |
| 46 | 46 | BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_CONTACT.getType(), CRM_CONTACT_TYPE); |
| 47 | 47 | BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_BUSINESS.getType(), CRM_BUSINESS_TYPE); | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/clue/CrmClueConvert.java deleted
| 1 | -package cn.iocoder.yudao.module.crm.convert.clue; | |
| 2 | - | |
| 3 | -import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransferReqVO; | |
| 4 | -import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO; | |
| 5 | -import org.mapstruct.Mapper; | |
| 6 | -import org.mapstruct.Mapping; | |
| 7 | -import org.mapstruct.factory.Mappers; | |
| 8 | - | |
| 9 | -/** | |
| 10 | - * 线索 Convert | |
| 11 | - * | |
| 12 | - * @author Wanwan | |
| 13 | - */ | |
| 14 | -@Mapper | |
| 15 | -public interface CrmClueConvert { | |
| 16 | - | |
| 17 | - CrmClueConvert INSTANCE = Mappers.getMapper(CrmClueConvert.class); | |
| 18 | - | |
| 19 | - @Mapping(target = "bizId", source = "reqVO.id") | |
| 20 | - CrmPermissionTransferReqBO convert(CrmClueTransferReqVO reqVO, Long userId); | |
| 21 | - | |
| 22 | -} |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/clue/CrmClueMapper.java
| ... | ... | @@ -8,7 +8,6 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO; |
| 8 | 8 | import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; |
| 9 | 9 | import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum; |
| 10 | 10 | import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils; |
| 11 | -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; | |
| 12 | 11 | import org.apache.ibatis.annotations.Mapper; |
| 13 | 12 | |
| 14 | 13 | import java.util.Collection; |
| ... | ... | @@ -22,16 +21,10 @@ import java.util.List; |
| 22 | 21 | @Mapper |
| 23 | 22 | public interface CrmClueMapper extends BaseMapperX<CrmClueDO> { |
| 24 | 23 | |
| 25 | - default int updateOwnerUserIdById(Long id, Long ownerUserId) { | |
| 26 | - return update(new LambdaUpdateWrapper<CrmClueDO>() | |
| 27 | - .eq(CrmClueDO::getId, id) | |
| 28 | - .set(CrmClueDO::getOwnerUserId, ownerUserId)); | |
| 29 | - } | |
| 30 | - | |
| 31 | 24 | default PageResult<CrmClueDO> selectPage(CrmCluePageReqVO pageReqVO, Long userId) { |
| 32 | 25 | MPJLambdaWrapperX<CrmClueDO> query = new MPJLambdaWrapperX<>(); |
| 33 | 26 | // 拼接数据权限的查询条件 |
| 34 | - CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_LEADS.getType(), | |
| 27 | + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CLUE.getType(), | |
| 35 | 28 | CrmClueDO::getId, userId, pageReqVO.getSceneType(), pageReqVO.getPool()); |
| 36 | 29 | // 拼接自身的查询条件 |
| 37 | 30 | query.selectAll(CrmClueDO.class) |
| ... | ... | @@ -50,16 +43,16 @@ public interface CrmClueMapper extends BaseMapperX<CrmClueDO> { |
| 50 | 43 | default List<CrmClueDO> selectBatchIds(Collection<Long> ids, Long userId) { |
| 51 | 44 | MPJLambdaWrapperX<CrmClueDO> query = new MPJLambdaWrapperX<>(); |
| 52 | 45 | // 拼接数据权限的查询条件 |
| 53 | - CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_LEADS.getType(), ids, userId); | |
| 46 | + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CLUE.getType(), ids, userId); | |
| 54 | 47 | query.selectAll(CrmClueDO.class).in(CrmClueDO::getId, ids).orderByDesc(CrmClueDO::getId); |
| 55 | 48 | // 拼接自身的查询条件 |
| 56 | 49 | return selectJoinList(CrmClueDO.class, query); |
| 57 | 50 | } |
| 58 | 51 | |
| 59 | - default Long selectFollowLeadsCount(Long userId) { | |
| 52 | + default Long selectCountByFollow(Long userId) { | |
| 60 | 53 | MPJLambdaWrapperX<CrmClueDO> query = new MPJLambdaWrapperX<>(); |
| 61 | 54 | // 我负责的 + 非公海 |
| 62 | - CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_LEADS.getType(), | |
| 55 | + CrmQueryWrapperUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CLUE.getType(), | |
| 63 | 56 | CrmClueDO::getId, userId, CrmSceneTypeEnum.OWNER.getType(), Boolean.FALSE); |
| 64 | 57 | // 未跟进 + 未转化 |
| 65 | 58 | query.eq(CrmClueDO::getFollowUpStatus, false) | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueService.java
| ... | ... | @@ -5,9 +5,9 @@ import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO; |
| 5 | 5 | import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueSaveReqVO; |
| 6 | 6 | import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransferReqVO; |
| 7 | 7 | import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO; |
| 8 | -import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; | |
| 9 | 8 | import jakarta.validation.Valid; |
| 10 | 9 | |
| 10 | +import java.time.LocalDateTime; | |
| 11 | 11 | import java.util.Collection; |
| 12 | 12 | import java.util.List; |
| 13 | 13 | |
| ... | ... | @@ -36,9 +36,11 @@ public interface CrmClueService { |
| 36 | 36 | /** |
| 37 | 37 | * 更新线索相关的跟进信息 |
| 38 | 38 | * |
| 39 | - * @param clueUpdateFollowUpReqBO 信息 | |
| 39 | + * @param id 编号 | |
| 40 | + * @param contactNextTime 下次联系时间 | |
| 41 | + * @param contactLastContent 最后联系内容 | |
| 40 | 42 | */ |
| 41 | - void updateClueFollowUp(CrmUpdateFollowUpReqBO clueUpdateFollowUpReqBO); | |
| 43 | + void updateClueFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent); | |
| 42 | 44 | |
| 43 | 45 | /** |
| 44 | 46 | * 删除线索 |
| ... | ... | @@ -83,17 +85,17 @@ public interface CrmClueService { |
| 83 | 85 | /** |
| 84 | 86 | * 线索转化为客户 |
| 85 | 87 | * |
| 86 | - * @param ids 线索编号数组 | |
| 88 | + * @param id 线索编号 | |
| 87 | 89 | * @param userId 用户编号 |
| 88 | 90 | */ |
| 89 | - void transformClue(List<Long> ids, Long userId); | |
| 91 | + void transformClue(Long id, Long userId); | |
| 90 | 92 | |
| 91 | 93 | /** |
| 92 | - * 获得分配给我的线索数量 | |
| 94 | + * 获得分配给我的、待跟进的线索数量 | |
| 93 | 95 | * |
| 94 | 96 | * @param userId 用户编号 |
| 95 | - * @return 提醒数量 | |
| 97 | + * @return 数量 | |
| 96 | 98 | */ |
| 97 | - Long getFollowLeadsCount(Long userId); | |
| 99 | + Long getFollowClueCount(Long userId); | |
| 98 | 100 | |
| 99 | 101 | } | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java
| ... | ... | @@ -3,15 +3,12 @@ package cn.iocoder.yudao.module.crm.service.clue; |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.collection.ListUtil; |
| 5 | 5 | import cn.hutool.core.lang.Assert; |
| 6 | -import cn.hutool.core.util.ObjectUtil; | |
| 7 | -import cn.hutool.extra.spring.SpringUtil; | |
| 8 | 6 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 9 | 7 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 10 | 8 | import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO; |
| 11 | 9 | import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueSaveReqVO; |
| 12 | 10 | import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransferReqVO; |
| 13 | 11 | import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerSaveReqVO; |
| 14 | -import cn.iocoder.yudao.module.crm.convert.clue.CrmClueConvert; | |
| 15 | 12 | import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO; |
| 16 | 13 | import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO; |
| 17 | 14 | import cn.iocoder.yudao.module.crm.dal.mysql.clue.CrmClueMapper; |
| ... | ... | @@ -25,6 +22,7 @@ import cn.iocoder.yudao.module.crm.service.followup.bo.CrmFollowUpCreateReqBO; |
| 25 | 22 | import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; |
| 26 | 23 | import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; |
| 27 | 24 | import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; |
| 25 | +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO; | |
| 28 | 26 | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| 29 | 27 | import com.mzt.logapi.context.LogRecordContext; |
| 30 | 28 | import com.mzt.logapi.service.impl.DiffParseFunction; |
| ... | ... | @@ -37,12 +35,13 @@ import org.springframework.validation.annotation.Validated; |
| 37 | 35 | import java.time.LocalDateTime; |
| 38 | 36 | import java.util.Collection; |
| 39 | 37 | import java.util.List; |
| 40 | -import java.util.Map; | |
| 41 | 38 | import java.util.Objects; |
| 42 | 39 | |
| 43 | 40 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 44 | -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; | |
| 45 | -import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; | |
| 41 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | |
| 42 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.singleton; | |
| 43 | +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CLUE_NOT_EXISTS; | |
| 44 | +import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CLUE_TRANSFORM_FAIL_ALREADY; | |
| 46 | 45 | import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; |
| 47 | 46 | import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_NOT_EXISTS; |
| 48 | 47 | |
| ... | ... | @@ -60,18 +59,18 @@ public class CrmClueServiceImpl implements CrmClueService { |
| 60 | 59 | |
| 61 | 60 | @Resource |
| 62 | 61 | private CrmCustomerService customerService; |
| 63 | - | |
| 64 | 62 | @Resource |
| 65 | 63 | private CrmPermissionService crmPermissionService; |
| 66 | 64 | @Resource |
| 67 | 65 | private CrmFollowUpRecordService followUpRecordService; |
| 66 | + | |
| 68 | 67 | @Resource |
| 69 | 68 | private AdminUserApi adminUserApi; |
| 70 | 69 | |
| 71 | 70 | @Override |
| 72 | 71 | @Transactional(rollbackFor = Exception.class) |
| 73 | - @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_CREATE_SUB_TYPE, bizNo = "{{#clue.id}}", | |
| 74 | - success = CRM_LEADS_CREATE_SUCCESS) | |
| 72 | + @LogRecord(type = CRM_CLUE_TYPE, subType = CRM_CLUE_CREATE_SUB_TYPE, bizNo = "{{#clue.id}}", | |
| 73 | + success = CRM_CLUE_CREATE_SUCCESS) | |
| 75 | 74 | public Long createClue(CrmClueSaveReqVO createReqVO) { |
| 76 | 75 | // 1.1 校验关联数据 |
| 77 | 76 | validateRelationDataExists(createReqVO); |
| ... | ... | @@ -84,7 +83,7 @@ public class CrmClueServiceImpl implements CrmClueService { |
| 84 | 83 | clueMapper.insert(clue); |
| 85 | 84 | |
| 86 | 85 | // 3. 创建数据权限 |
| 87 | - CrmPermissionCreateReqBO createReqBO = new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_LEADS.getType()) | |
| 86 | + CrmPermissionCreateReqBO createReqBO = new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CLUE.getType()) | |
| 88 | 87 | .setBizId(clue.getId()).setUserId(clue.getOwnerUserId()).setLevel(CrmPermissionLevelEnum.OWNER.getLevel()); |
| 89 | 88 | crmPermissionService.createPermission(createReqBO); |
| 90 | 89 | |
| ... | ... | @@ -95,9 +94,9 @@ public class CrmClueServiceImpl implements CrmClueService { |
| 95 | 94 | |
| 96 | 95 | @Override |
| 97 | 96 | @Transactional(rollbackFor = Exception.class) |
| 98 | - @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", | |
| 99 | - success = CRM_LEADS_UPDATE_SUCCESS) | |
| 100 | - @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#updateReq.id", level = CrmPermissionLevelEnum.OWNER) | |
| 97 | + @LogRecord(type = CRM_CLUE_TYPE, subType = CRM_CLUE_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}", | |
| 98 | + success = CRM_CLUE_UPDATE_SUCCESS) | |
| 99 | + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CLUE, bizId = "#updateReq.id", level = CrmPermissionLevelEnum.OWNER) | |
| 101 | 100 | public void updateClue(CrmClueSaveReqVO updateReq) { |
| 102 | 101 | Assert.notNull(updateReq.getId(), "线索编号不能为空"); |
| 103 | 102 | // 1.1 校验线索是否存在 |
| ... | ... | @@ -114,15 +113,25 @@ public class CrmClueServiceImpl implements CrmClueService { |
| 114 | 113 | LogRecordContext.putVariable("clueName", oldClue.getName()); |
| 115 | 114 | } |
| 116 | 115 | |
| 116 | + private void validateRelationDataExists(CrmClueSaveReqVO reqVO) { | |
| 117 | + // 校验负责人 | |
| 118 | + if (Objects.nonNull(reqVO.getOwnerUserId()) && | |
| 119 | + Objects.isNull(adminUserApi.getUser(reqVO.getOwnerUserId()))) { | |
| 120 | + throw exception(USER_NOT_EXISTS); | |
| 121 | + } | |
| 122 | + } | |
| 123 | + | |
| 117 | 124 | @Override |
| 118 | - @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_UPDATE_SUB_TYPE, bizNo = "{{#updateReq.bizId}", | |
| 119 | - success = CRM_LEADS_UPDATE_SUCCESS) | |
| 120 | - public void updateClueFollowUp(CrmUpdateFollowUpReqBO updateReq) { | |
| 125 | + @LogRecord(type = CRM_CLUE_TYPE, subType = CRM_CLUE_FOLLOW_UP_SUB_TYPE, bizNo = "{{#id}", | |
| 126 | + success = CRM_CLUE_FOLLOW_UP_SUCCESS) | |
| 127 | + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CLUE, bizId = "#id", level = CrmPermissionLevelEnum.WRITE) | |
| 128 | + public void updateClueFollowUp(Long id, LocalDateTime contactNextTime, String contactLastContent) { | |
| 121 | 129 | // 校验线索是否存在 |
| 122 | - CrmClueDO oldClue = validateClueExists(updateReq.getBizId()); | |
| 130 | + CrmClueDO oldClue = validateClueExists(id); | |
| 123 | 131 | |
| 124 | 132 | // 更新 |
| 125 | - clueMapper.updateById(BeanUtils.toBean(updateReq, CrmClueDO.class).setId(updateReq.getBizId())); | |
| 133 | + clueMapper.updateById(new CrmClueDO().setId(id).setFollowUpStatus(true).setContactNextTime(contactNextTime) | |
| 134 | + .setContactLastTime(LocalDateTime.now()).setContactLastContent(contactLastContent)); | |
| 126 | 135 | |
| 127 | 136 | // 3. 记录操作日志上下文 |
| 128 | 137 | LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldClue, CrmUpdateFollowUpReqBO.class)); |
| ... | ... | @@ -131,9 +140,9 @@ public class CrmClueServiceImpl implements CrmClueService { |
| 131 | 140 | |
| 132 | 141 | @Override |
| 133 | 142 | @Transactional(rollbackFor = Exception.class) |
| 134 | - @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_DELETE_SUB_TYPE, bizNo = "{{#id}}", | |
| 135 | - success = CRM_LEADS_DELETE_SUCCESS) | |
| 136 | - @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) | |
| 143 | + @LogRecord(type = CRM_CLUE_TYPE, subType = CRM_CLUE_DELETE_SUB_TYPE, bizNo = "{{#id}}", | |
| 144 | + success = CRM_CLUE_DELETE_SUCCESS) | |
| 145 | + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CLUE, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) | |
| 137 | 146 | public void deleteClue(Long id) { |
| 138 | 147 | // 1. 校验存在 |
| 139 | 148 | CrmClueDO clue = validateClueExists(id); |
| ... | ... | @@ -142,28 +151,29 @@ public class CrmClueServiceImpl implements CrmClueService { |
| 142 | 151 | clueMapper.deleteById(id); |
| 143 | 152 | |
| 144 | 153 | // 3. 删除数据权限 |
| 145 | - crmPermissionService.deletePermission(CrmBizTypeEnum.CRM_LEADS.getType(), id); | |
| 154 | + crmPermissionService.deletePermission(CrmBizTypeEnum.CRM_CLUE.getType(), id); | |
| 146 | 155 | |
| 147 | 156 | // 4. 删除跟进 |
| 148 | - followUpRecordService.deleteFollowUpRecordByBiz(CrmBizTypeEnum.CRM_LEADS.getType(), id); | |
| 157 | + followUpRecordService.deleteFollowUpRecordByBiz(CrmBizTypeEnum.CRM_CLUE.getType(), id); | |
| 149 | 158 | |
| 150 | - // 记录操作日志上下文 | |
| 159 | + // 5. 记录操作日志上下文 | |
| 151 | 160 | LogRecordContext.putVariable("clueName", clue.getName()); |
| 152 | 161 | } |
| 153 | 162 | |
| 154 | 163 | @Override |
| 155 | 164 | @Transactional(rollbackFor = Exception.class) |
| 156 | - @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.id}}", | |
| 157 | - success = CRM_LEADS_TRANSFER_SUCCESS) | |
| 158 | - @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER) | |
| 165 | + @LogRecord(type = CRM_CLUE_TYPE, subType = CRM_CLUE_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.id}}", | |
| 166 | + success = CRM_CLUE_TRANSFER_SUCCESS) | |
| 167 | + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CLUE, bizId = "#reqVO.id", level = CrmPermissionLevelEnum.OWNER) | |
| 159 | 168 | public void transferClue(CrmClueTransferReqVO reqVO, Long userId) { |
| 160 | 169 | // 1 校验线索是否存在 |
| 161 | 170 | CrmClueDO clue = validateClueExists(reqVO.getId()); |
| 162 | 171 | |
| 163 | 172 | // 2.1 数据权限转移 |
| 164 | - crmPermissionService.transferPermission(CrmClueConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_LEADS.getType())); | |
| 173 | + crmPermissionService.transferPermission(new CrmPermissionTransferReqBO(userId, CrmBizTypeEnum.CRM_CLUE.getType(), | |
| 174 | + reqVO.getId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel())); | |
| 165 | 175 | // 2.2 设置新的负责人 |
| 166 | - clueMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId()); | |
| 176 | + clueMapper.updateById(new CrmClueDO().setId(reqVO.getId()).setOwnerUserId(reqVO.getNewOwnerUserId())); | |
| 167 | 177 | |
| 168 | 178 | // 3. 记录转移日志 |
| 169 | 179 | LogRecordContext.putVariable("clue", clue); |
| ... | ... | @@ -171,69 +181,32 @@ public class CrmClueServiceImpl implements CrmClueService { |
| 171 | 181 | |
| 172 | 182 | @Override |
| 173 | 183 | @Transactional(rollbackFor = Exception.class) |
| 174 | - @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#ids", level = CrmPermissionLevelEnum.OWNER) | |
| 175 | - public void transformClue(List<Long> ids, Long userId) { | |
| 184 | + @LogRecord(type = CRM_CLUE_TYPE, subType = CRM_CLUE_TRANSLATE_SUB_TYPE, bizNo = "{{#id}}", | |
| 185 | + success = CRM_CLUE_TRANSLATE_SUCCESS) | |
| 186 | + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CLUE, bizId = "#id", level = CrmPermissionLevelEnum.OWNER) | |
| 187 | + public void transformClue(Long id, Long userId) { | |
| 176 | 188 | // 1.1 校验线索都存在 |
| 177 | - List<CrmClueDO> clues = getClueList(ids, userId); | |
| 178 | - if (CollUtil.isEmpty(clues) || ObjectUtil.notEqual(clues.size(), ids.size())) { | |
| 179 | - ids.removeAll(convertSet(clues, CrmClueDO::getId)); | |
| 180 | - throw exception(CLUE_NOT_EXISTS_ANY, ids); | |
| 181 | - } | |
| 182 | - // 1.2 存在已经转化的,直接提示哈。避免操作的用户,以为都转化成功了 | |
| 183 | - List<CrmClueDO> translatedClues = filterList(clues, | |
| 184 | - clue -> ObjectUtil.equal(Boolean.TRUE, clue.getTransformStatus())); | |
| 185 | - if (CollUtil.isNotEmpty(translatedClues)) { | |
| 186 | - throw exception(CLUE_TRANSFORM_FAIL_ALREADY, convertSet(translatedClues, CrmClueDO::getId)); | |
| 189 | + CrmClueDO clue = validateClueExists(id); | |
| 190 | + // 1.2 存在已经转化的 | |
| 191 | + if (clue.getTransformStatus()) { | |
| 192 | + throw exception(CLUE_TRANSFORM_FAIL_ALREADY); | |
| 187 | 193 | } |
| 188 | 194 | |
| 189 | 195 | // 2.1 遍历线索(未转化的线索),创建对应的客户 |
| 190 | - clues.forEach(clue -> { | |
| 191 | - Long customerId = customerService.createCustomer(BeanUtils.toBean(clue, CrmCustomerCreateReqBO.class), userId); | |
| 192 | - clue.setCustomerId(customerId); | |
| 193 | - }); | |
| 196 | + Long customerId = customerService.createCustomer(BeanUtils.toBean(clue, CrmCustomerCreateReqBO.class), userId); | |
| 194 | 197 | // 2.2 更新线索 |
| 195 | - clueMapper.updateBatch(convertList(clues, clue -> new CrmClueDO().setId(clue.getId()) | |
| 196 | - .setTransformStatus(Boolean.TRUE).setCustomerId(clue.getCustomerId()))); | |
| 198 | + clueMapper.updateById(new CrmClueDO().setId(id).setTransformStatus(Boolean.TRUE).setCustomerId(customerId)); | |
| 197 | 199 | // 2.3 复制跟进记录 |
| 198 | - copyFollowUpRecords(clues); | |
| 199 | - | |
| 200 | - // 3. 记录操作日志 | |
| 201 | - for (CrmClueDO clue : clues) { | |
| 202 | - getSelf().translateCustomerLog(clue); | |
| 203 | - } | |
| 204 | - } | |
| 205 | - | |
| 206 | - /** | |
| 207 | - * 线索被转换客户后,需要将线索的跟进记录,复制到客户上 | |
| 208 | - * | |
| 209 | - * @param clues 被转化的线索 | |
| 210 | - */ | |
| 211 | - private void copyFollowUpRecords(List<CrmClueDO> clues) { | |
| 212 | 200 | List<CrmFollowUpRecordDO> followUpRecords = followUpRecordService.getFollowUpRecordByBiz( |
| 213 | - CrmBizTypeEnum.CRM_LEADS.getType(), convertSet(clues, CrmClueDO::getId)); | |
| 214 | - if (CollUtil.isEmpty(followUpRecords)) { | |
| 215 | - return; | |
| 201 | + CrmBizTypeEnum.CRM_CLUE.getType(), singleton(clue.getId())); | |
| 202 | + if (CollUtil.isNotEmpty(followUpRecords)) { | |
| 203 | + followUpRecordService.createFollowUpRecordBatch(convertList(followUpRecords, record -> | |
| 204 | + BeanUtils.toBean(record, CrmFollowUpCreateReqBO.class) | |
| 205 | + .setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()).setBizId(customerId))); | |
| 216 | 206 | } |
| 217 | - // 创建跟进 | |
| 218 | - Map<Long, CrmClueDO> clueMap = convertMap(clues, CrmClueDO::getId); | |
| 219 | - followUpRecordService.createFollowUpRecordBatch(convertList(followUpRecords, followUpRecord -> | |
| 220 | - BeanUtils.toBean(followUpRecord, CrmFollowUpCreateReqBO.class).setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()) | |
| 221 | - .setBizId(clueMap.get(followUpRecord.getBizId()).getCustomerId()))); | |
| 222 | - } | |
| 223 | - | |
| 224 | - @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_TRANSLATE_SUB_TYPE, bizNo = "{{#clue.id}}", | |
| 225 | - success = CRM_LEADS_TRANSLATE_SUCCESS) | |
| 226 | - public void translateCustomerLog(CrmClueDO clue) { | |
| 227 | - // 记录操作日志上下文 | |
| 228 | - LogRecordContext.putVariable("clue", clue); | |
| 229 | - } | |
| 230 | 207 | |
| 231 | - private void validateRelationDataExists(CrmClueSaveReqVO reqVO) { | |
| 232 | - // 校验负责人 | |
| 233 | - if (Objects.nonNull(reqVO.getOwnerUserId()) && | |
| 234 | - Objects.isNull(adminUserApi.getUser(reqVO.getOwnerUserId()))) { | |
| 235 | - throw exception(USER_NOT_EXISTS); | |
| 236 | - } | |
| 208 | + // 3. 记录操作日志上下文 | |
| 209 | + LogRecordContext.putVariable("clueName", clue.getName()); | |
| 237 | 210 | } |
| 238 | 211 | |
| 239 | 212 | private CrmClueDO validateClueExists(Long id) { |
| ... | ... | @@ -245,7 +218,7 @@ public class CrmClueServiceImpl implements CrmClueService { |
| 245 | 218 | } |
| 246 | 219 | |
| 247 | 220 | @Override |
| 248 | - @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#id", level = CrmPermissionLevelEnum.READ) | |
| 221 | + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CLUE, bizId = "#id", level = CrmPermissionLevelEnum.READ) | |
| 249 | 222 | public CrmClueDO getClue(Long id) { |
| 250 | 223 | return clueMapper.selectById(id); |
| 251 | 224 | } |
| ... | ... | @@ -263,18 +236,9 @@ public class CrmClueServiceImpl implements CrmClueService { |
| 263 | 236 | return clueMapper.selectPage(pageReqVO, userId); |
| 264 | 237 | } |
| 265 | 238 | |
| 266 | - /** | |
| 267 | - * 获得自身的代理对象,解决 AOP 生效问题 | |
| 268 | - * | |
| 269 | - * @return 自己 | |
| 270 | - */ | |
| 271 | - private CrmClueServiceImpl getSelf() { | |
| 272 | - return SpringUtil.getBean(getClass()); | |
| 273 | - } | |
| 274 | - | |
| 275 | 239 | @Override |
| 276 | - public Long getFollowLeadsCount(Long userId) { | |
| 277 | - return clueMapper.selectFollowLeadsCount(userId); | |
| 240 | + public Long getFollowClueCount(Long userId) { | |
| 241 | + return clueMapper.selectCountByFollow(userId); | |
| 278 | 242 | } |
| 279 | 243 | |
| 280 | 244 | } | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordServiceImpl.java
| ... | ... | @@ -79,8 +79,8 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService { |
| 79 | 79 | if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_BUSINESS.getType(), followUpRecord.getBizType())) { // 更新商机跟进信息 |
| 80 | 80 | businessService.updateBusinessFollowUpBatch(Collections.singletonList(updateFollowUpReqBO)); |
| 81 | 81 | } |
| 82 | - if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_LEADS.getType(), followUpRecord.getBizType())) { // 更新线索跟进信息 | |
| 83 | - clueService.updateClueFollowUp(updateFollowUpReqBO); | |
| 82 | + if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_CLUE.getType(), followUpRecord.getBizType())) { // 更新线索跟进信息 | |
| 83 | + clueService.updateClueFollowUp(followUpRecord.getId(), followUpRecord.getNextTime(), followUpRecord.getContent()); | |
| 84 | 84 | } |
| 85 | 85 | if (ObjUtil.notEqual(CrmBizTypeEnum.CRM_CONTACT.getType(), followUpRecord.getBizType())) { // 更新联系人跟进信息 |
| 86 | 86 | contactService.updateContactFollowUpBatch(Collections.singletonList(updateFollowUpReqBO)); | ... | ... |
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/permission/bo/CrmPermissionTransferReqBO.java
| ... | ... | @@ -3,9 +3,11 @@ package cn.iocoder.yudao.module.crm.service.permission.bo; |
| 3 | 3 | import cn.iocoder.yudao.framework.common.validation.InEnum; |
| 4 | 4 | import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; |
| 5 | 5 | import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; |
| 6 | +import lombok.AllArgsConstructor; | |
| 6 | 7 | import lombok.Data; |
| 7 | 8 | |
| 8 | 9 | import jakarta.validation.constraints.NotNull; |
| 10 | +import lombok.NoArgsConstructor; | |
| 9 | 11 | |
| 10 | 12 | /** |
| 11 | 13 | * 数据权限转移 Request BO |
| ... | ... | @@ -13,6 +15,8 @@ import jakarta.validation.constraints.NotNull; |
| 13 | 15 | * @author HUIHUI |
| 14 | 16 | */ |
| 15 | 17 | @Data |
| 18 | +@NoArgsConstructor | |
| 19 | +@AllArgsConstructor | |
| 16 | 20 | public class CrmPermissionTransferReqBO { |
| 17 | 21 | |
| 18 | 22 | /** | ... | ... |