Commit 8675d135484a6f30de32075ba57f686dd258859f

Authored by puhui999
1 parent f7d41186

CRM-线索:完善线索转客户

yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/CrmClueController.java
... ... @@ -39,7 +39,7 @@ public class CrmClueController {
39 39 @Operation(summary = "创建线索")
40 40 @PreAuthorize("@ss.hasPermission('crm:clue:create')")
41 41 public CommonResult<Long> createClue(@Valid @RequestBody CrmClueSaveReqVO createReqVO) {
42   - return success(clueService.createClue(createReqVO));
  42 + return success(clueService.createClue(createReqVO, getLoginUserId()));
43 43 }
44 44  
45 45 @PutMapping("/update")
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/followup/CrmFollowUpRecordMapper.java
... ... @@ -7,6 +7,9 @@ import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecor
7 7 import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;
8 8 import org.apache.ibatis.annotations.Mapper;
9 9  
  10 +import java.util.Collection;
  11 +import java.util.List;
  12 +
10 13 /**
11 14 * 跟进记录 Mapper
12 15 *
... ... @@ -22,4 +25,14 @@ public interface CrmFollowUpRecordMapper extends BaseMapperX&lt;CrmFollowUpRecordDO
22 25 .orderByDesc(CrmFollowUpRecordDO::getId));
23 26 }
24 27  
  28 + default void deleteByBiz(Integer bizType, Long bizId) {
  29 + delete(new LambdaQueryWrapperX<CrmFollowUpRecordDO>().eq(CrmFollowUpRecordDO::getBizType, bizType)
  30 + .eq(CrmFollowUpRecordDO::getBizId, bizId));
  31 + }
  32 +
  33 + default List<CrmFollowUpRecordDO> selectListByBiz(Integer bizType, Collection<Long> bizIds) {
  34 + return selectList(new LambdaQueryWrapperX<CrmFollowUpRecordDO>().eq(CrmFollowUpRecordDO::getBizType, bizType)
  35 + .in(CrmFollowUpRecordDO::getBizId, bizIds));
  36 + }
  37 +
25 38 }
26 39 \ No newline at end of file
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueService.java
... ... @@ -23,9 +23,10 @@ public interface CrmClueService {
23 23 * 创建线索
24 24 *
25 25 * @param createReqVO 创建信息
  26 + * @param userId 用户编号
26 27 * @return 编号
27 28 */
28   - Long createClue(@Valid CrmClueSaveReqVO createReqVO);
  29 + Long createClue(@Valid CrmClueSaveReqVO createReqVO, Long userId);
29 30  
30 31 /**
31 32 * 更新线索
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java
... ... @@ -3,10 +3,9 @@ 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.ObjUtil;
6 7 import cn.hutool.core.util.ObjectUtil;
7   -import cn.hutool.core.util.StrUtil;
8 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
9   -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
10 9 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
11 10 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO;
12 11 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueSaveReqVO;
... ... @@ -15,11 +14,16 @@ import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransformReqV
15 14 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerSaveReqVO;
16 15 import cn.iocoder.yudao.module.crm.convert.clue.CrmClueConvert;
17 16 import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;
  17 +import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
  18 +import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;
18 19 import cn.iocoder.yudao.module.crm.dal.mysql.clue.CrmClueMapper;
19 20 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
20 21 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
21 22 import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
22 23 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
  24 +import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO;
  25 +import cn.iocoder.yudao.module.crm.service.followup.CrmFollowUpRecordService;
  26 +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmFollowUpCreateReqBO;
23 27 import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
24 28 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
25 29 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
... ... @@ -32,17 +36,14 @@ import org.springframework.stereotype.Service;
32 36 import org.springframework.transaction.annotation.Transactional;
33 37 import org.springframework.validation.annotation.Validated;
34 38  
35   -import java.util.Collection;
36   -import java.util.List;
37   -import java.util.Objects;
38   -import java.util.Set;
  39 +import java.util.*;
39 40  
40 41 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
41   -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
42   -import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
  42 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
43 43 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
44 44 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
45 45 import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.USER_NOT_EXISTS;
  46 +import static java.util.Collections.singletonList;
46 47  
47 48 /**
48 49 * 线索 Service 实现类
... ... @@ -61,7 +62,8 @@ public class CrmClueServiceImpl implements CrmClueService {
61 62  
62 63 @Resource
63 64 private CrmPermissionService crmPermissionService;
64   -
  65 + @Resource
  66 + private CrmFollowUpRecordService followUpRecordService;
65 67 @Resource
66 68 private AdminUserApi adminUserApi;
67 69  
... ... @@ -69,21 +71,23 @@ public class CrmClueServiceImpl implements CrmClueService {
69 71 @Transactional(rollbackFor = Exception.class)
70 72 @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_CREATE_SUB_TYPE, bizNo = "{{#clue.id}}",
71 73 success = CRM_LEADS_CREATE_SUCCESS)
72   - public Long createClue(CrmClueSaveReqVO createReqVO) {
  74 + public Long createClue(CrmClueSaveReqVO createReqVO, Long userId) {
73 75 // 1. 校验关联数据
74 76 validateRelationDataExists(createReqVO);
75 77  
76 78 // 2. 插入
77 79 CrmClueDO clue = BeanUtils.toBean(createReqVO, CrmClueDO.class);
  80 + if (ObjUtil.isNull(createReqVO.getOwnerUserId())) {
  81 + clue.setOwnerUserId(userId); // 如果没有设置负责人那么默认操作人为负责人
  82 + } else {
  83 + // 校验负责人是否存在
  84 + adminUserApi.validateUserList(singletonList(createReqVO.getOwnerUserId()));
  85 + }
78 86 clueMapper.insert(clue);
79 87  
80 88 // 3. 创建数据权限
81   - CrmPermissionCreateReqBO createReqBO = new CrmPermissionCreateReqBO()
82   - .setBizType(CrmBizTypeEnum.CRM_LEADS.getType())
83   - .setBizId(clue.getId())
84   - // 设置当前操作的人为负责人
85   - .setUserId(getLoginUserId())
86   - .setLevel(CrmPermissionLevelEnum.OWNER.getLevel());
  89 + CrmPermissionCreateReqBO createReqBO = new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_LEADS.getType())
  90 + .setBizId(clue.getId()).setUserId(clue.getOwnerUserId()).setLevel(CrmPermissionLevelEnum.OWNER.getLevel());
87 91 crmPermissionService.createPermission(createReqBO);
88 92  
89 93 // 4. 记录操作日志上下文
... ... @@ -132,7 +136,10 @@ public class CrmClueServiceImpl implements CrmClueService {
132 136 // 3. 删除数据权限
133 137 crmPermissionService.deletePermission(CrmBizTypeEnum.CRM_LEADS.getType(), id);
134 138  
135   - // 4. 记录操作日志上下文
  139 + // 4. 删除跟进
  140 + followUpRecordService.deleteFollowUpRecordByBiz(CrmBizTypeEnum.CRM_LEADS.getType(), id);
  141 +
  142 + // 记录操作日志上下文
136 143 LogRecordContext.putVariable("clueName", clue.getName());
137 144 }
138 145  
... ... @@ -179,31 +186,47 @@ public class CrmClueServiceImpl implements CrmClueService {
179 186 @Override
180 187 @Transactional(rollbackFor = Exception.class)
181 188 public void translateCustomer(CrmClueTransformReqVO reqVO, Long userId) {
182   - // 校验线索都存在
  189 + // 1.1 校验线索都存在
183 190 Set<Long> clueIds = reqVO.getIds();
184 191 List<CrmClueDO> clues = getClueList(clueIds, userId);
185 192 if (CollUtil.isEmpty(clues) || ObjectUtil.notEqual(clues.size(), clueIds.size())) {
186 193 clueIds.removeAll(convertSet(clues, CrmClueDO::getId));
187   - throw exception(CLUE_ANY_CLUE_NOT_EXISTS, StrUtil.join(",", clueIds));
  194 + throw exception(CLUE_ANY_CLUE_NOT_EXISTS, clueIds);
188 195 }
189   -
190   - // 存在已经转化的,直接提示哈。避免操作的用户,以为都转化成功了
191   - List<CrmClueDO> translatedClues = CollectionUtils.filterList(clues,
  196 + // 1.2 存在已经转化的,直接提示哈。避免操作的用户,以为都转化成功了
  197 + List<CrmClueDO> translatedClues = filterList(clues,
192 198 clue -> ObjectUtil.equal(Boolean.TRUE, clue.getTransformStatus()));
193 199 if (CollUtil.isNotEmpty(translatedClues)) {
194   - throw exception(CLUE_ANY_CLUE_ALREADY_TRANSLATED, StrUtil.join(",", convertSet(translatedClues, CrmClueDO::getId)));
  200 + throw exception(CLUE_ANY_CLUE_ALREADY_TRANSLATED, convertSet(translatedClues, CrmClueDO::getId));
  201 + }
  202 +
  203 + // 2. 遍历线索(未转化的线索),创建对应的客户
  204 + List<CrmClueDO> translateClues = filterList(clues, clue -> ObjUtil.equal(Boolean.FALSE, clue.getTransformStatus()));
  205 + List<CrmCustomerDO> customers = customerService.createCustomerBatch(convertList(translateClues, clue ->
  206 + BeanUtils.toBean(clue, CrmCustomerCreateReqBO.class)), userId);
  207 +
  208 + Map<Long, Long> clueCustomerIdMap = new HashMap<>(translateClues.size());
  209 + // 2.1 更新线索
  210 + clueMapper.updateBatch(convertList(customers, customer -> {
  211 + CrmClueDO firstClue = findFirst(translateClues, clue -> ObjUtil.equal(clue.getName(), customer.getName()));
  212 + clueCustomerIdMap.put(firstClue.getId(), customer.getId());
  213 + return new CrmClueDO().setId(firstClue.getId()).setTransformStatus(Boolean.TRUE).setCustomerId(customer.getId());
  214 + }));
  215 + // 2.3 复制跟进
  216 + updateFollowUpRecords(clueCustomerIdMap);
  217 + }
  218 +
  219 + private void updateFollowUpRecords(Map<Long, Long> clueCustomerIdMap) {
  220 + List<CrmFollowUpRecordDO> followUpRecords = followUpRecordService.getFollowUpRecordByBiz(
  221 + CrmBizTypeEnum.CRM_LEADS.getType(), clueCustomerIdMap.keySet());
  222 + if (CollUtil.isEmpty(followUpRecords)) {
  223 + return;
195 224 }
196 225  
197   - // 遍历线索(未转化的线索),创建对应的客户
198   - reqVO.getIds().forEach(id -> {
199   - // 1. 创建客户
200   - CrmCustomerSaveReqVO customerSaveReqVO = BeanUtils.toBean(id, CrmCustomerSaveReqVO.class).setId(null);
201   - Long customerId = customerService.createCustomer(customerSaveReqVO, userId);
202   - // TODO @puhui999:如果有跟进记录,需要一起转过去;提问:艿艿这里是复制线索所有的跟进吗?还是直接把线索相关的跟进 bizType、bizId 全改为关联客户?
203   - // 2. 更新线索
204   - clueMapper.updateById(new CrmClueDO().setId(id)
205   - .setTransformStatus(Boolean.TRUE).setCustomerId(customerId));
206   - });
  226 + // 创建跟进
  227 + followUpRecordService.createFollowUpRecordBatch(convertList(followUpRecords, followUpRecord ->
  228 + BeanUtils.toBean(followUpRecord, CrmFollowUpCreateReqBO.class).setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())
  229 + .setBizId(clueCustomerIdMap.get(followUpRecord.getBizId()))));
207 230 }
208 231  
209 232 private void validateRelationDataExists(CrmClueSaveReqVO reqVO) {
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java
... ... @@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageR
6 6 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerSaveReqVO;
7 7 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerTransferReqVO;
8 8 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
  9 +import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO;
9 10 import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
10 11 import jakarta.validation.Valid;
11 12  
... ... @@ -98,6 +99,15 @@ public interface CrmCustomerService {
98 99 */
99 100 void updateCustomerFollowUp(CrmUpdateFollowUpReqBO customerUpdateFollowUpReqBO);
100 101  
  102 + /**
  103 + * 批量创建客户
  104 + *
  105 + * @param customerCreateReqBOs 请求
  106 + * @param userId 用户编号
  107 + * @return 客户列表
  108 + */
  109 + List<CrmCustomerDO> createCustomerBatch(List<CrmCustomerCreateReqBO> customerCreateReqBOs, Long userId);
  110 +
101 111 // ==================== 公海相关操作 ====================
102 112  
103 113 /**
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java
... ... @@ -21,6 +21,7 @@ import cn.iocoder.yudao.module.crm.framework.permission.core.util.CrmPermissionU
21 21 import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
22 22 import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
23 23 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
  24 +import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO;
24 25 import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
25 26 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
26 27 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
... ... @@ -42,10 +43,12 @@ import java.util.Collections;
42 43 import java.util.List;
43 44  
44 45 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
  46 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
45 47 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
46 48 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
47 49 import static cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum.CUSTOMER_LOCK_LIMIT;
48 50 import static cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum.CUSTOMER_OWNER_LIMIT;
  51 +import static java.util.Collections.emptyList;
49 52 import static java.util.Collections.singletonList;
50 53  
51 54 /**
... ... @@ -209,6 +212,25 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
209 212 customerMapper.updateById(BeanUtils.toBean(customerUpdateFollowUpReqBO, CrmCustomerDO.class).setId(customerUpdateFollowUpReqBO.getBizId()));
210 213 }
211 214  
  215 + @Override
  216 + @Transactional(rollbackFor = Exception.class)
  217 + public List<CrmCustomerDO> createCustomerBatch(List<CrmCustomerCreateReqBO> customerCreateReqBOs, Long userId) {
  218 + if (CollUtil.isEmpty(customerCreateReqBOs)) {
  219 + return emptyList();
  220 + }
  221 +
  222 + // 创建客户
  223 + List<CrmCustomerDO> customers = convertList(customerCreateReqBOs, customerBO ->
  224 + BeanUtils.toBean(customerBO, CrmCustomerDO.class).setOwnerUserId(userId));
  225 + customerMapper.insertBatch(customers);
  226 +
  227 + // 创建负责人数据权限
  228 + permissionService.createPermissionBatch(convertList(customers, customer -> new CrmPermissionCreateReqBO()
  229 + .setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()).setBizId(customer.getId()).setUserId(userId)
  230 + .setLevel(CrmPermissionLevelEnum.OWNER.getLevel())));
  231 + return customers;
  232 + }
  233 +
212 234 // ==================== 公海相关操作 ====================
213 235  
214 236 @Override
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/bo/CrmCustomerCreateReqBO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.service.customer.bo;
  2 +
  3 +import cn.iocoder.yudao.framework.common.validation.Mobile;
  4 +import cn.iocoder.yudao.framework.common.validation.Telephone;
  5 +import cn.iocoder.yudao.module.crm.enums.DictTypeConstants;
  6 +import jakarta.validation.constraints.Email;
  7 +import jakarta.validation.constraints.NotEmpty;
  8 +import jakarta.validation.constraints.Size;
  9 +import lombok.Data;
  10 +
  11 +import java.time.LocalDateTime;
  12 +
  13 +/**
  14 + * 客户创建 Create Req BO
  15 + *
  16 + * @author HUIHUI
  17 + */
  18 +@Data
  19 +public class CrmCustomerCreateReqBO {
  20 +
  21 + /**
  22 + * 客户名称
  23 + */
  24 + @NotEmpty(message = "客户名称不能为空")
  25 + private String name;
  26 + /**
  27 + * 跟进状态
  28 + */
  29 + private Boolean followUpStatus;
  30 + /**
  31 + * 锁定状态
  32 + */
  33 + private Boolean lockStatus;
  34 + /**
  35 + * 成交状态
  36 + */
  37 + private Boolean dealStatus;
  38 + /**
  39 + * 所属行业
  40 + *
  41 + * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_INDUSTRY}
  42 + */
  43 + private Integer industryId;
  44 + /**
  45 + * 客户等级
  46 + *
  47 + * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_LEVEL}
  48 + */
  49 + private Integer level;
  50 + /**
  51 + * 客户来源
  52 + *
  53 + * 对应字典 {@link DictTypeConstants#CRM_CUSTOMER_SOURCE}
  54 + */
  55 + private Integer source;
  56 +
  57 + /**
  58 + * 手机
  59 + */
  60 + @Mobile
  61 + private String mobile;
  62 + /**
  63 + * 电话
  64 + */
  65 + @Telephone
  66 + private String telephone;
  67 + /**
  68 + * 网址
  69 + */
  70 + private String website;
  71 + /**
  72 + * QQ
  73 + */
  74 + private String qq;
  75 + /**
  76 + * wechat
  77 + */
  78 + private String wechat;
  79 +
  80 + /**
  81 + * 邮箱
  82 + */
  83 + @Email(message = "邮箱格式不正确")
  84 + private String email;
  85 +
  86 + /**
  87 + * 客户描述
  88 + */
  89 + @Size(max = 4096, message = "客户描述长度不能超过 4096 个字符")
  90 + private String description;
  91 + /**
  92 + * 备注
  93 + */
  94 + private String remark;
  95 + /**
  96 + * 负责人的用户编号
  97 + *
  98 + * 关联 AdminUserDO 的 id 字段
  99 + */
  100 + private Long ownerUserId;
  101 + /**
  102 + * 所在地
  103 + *
  104 + * 关联 {@link cn.iocoder.yudao.framework.ip.core.Area#getId()} 字段
  105 + */
  106 + private Integer areaId;
  107 + /**
  108 + * 详细地址
  109 + */
  110 + private String detailAddress;
  111 +
  112 + /**
  113 + * 最后跟进时间
  114 + */
  115 + private LocalDateTime contactLastTime;
  116 + /**
  117 + * 最后跟进内容
  118 + */
  119 + private String contactLastContent;
  120 + /**
  121 + * 下次联系时间
  122 + */
  123 + private LocalDateTime contactNextTime;
  124 +
  125 +}
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordService.java
... ... @@ -4,8 +4,12 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 4 import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordPageReqVO;
5 5 import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordSaveReqVO;
6 6 import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;
  7 +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmFollowUpCreateReqBO;
7 8 import jakarta.validation.Valid;
8 9  
  10 +import java.util.Collection;
  11 +import java.util.List;
  12 +
9 13 /**
10 14 * 跟进记录 Service 接口
11 15 *
... ... @@ -22,6 +26,13 @@ public interface CrmFollowUpRecordService {
22 26 Long createFollowUpRecord(@Valid CrmFollowUpRecordSaveReqVO createReqVO);
23 27  
24 28 /**
  29 + * 创建更进
  30 + *
  31 + * @param followUpCreateReqBOs 请求
  32 + */
  33 + void createFollowUpRecordBatch(List<CrmFollowUpCreateReqBO> followUpCreateReqBOs);
  34 +
  35 + /**
25 36 * 删除跟进记录 (数据权限基于 bizType、 bizId)
26 37 *
27 38 * @param id 编号
... ... @@ -30,6 +41,14 @@ public interface CrmFollowUpRecordService {
30 41 void deleteFollowUpRecord(Long id, Long userId);
31 42  
32 43 /**
  44 + * 删除跟进
  45 + *
  46 + * @param bizType 模块类型
  47 + * @param bizId 模块数据编号
  48 + */
  49 + void deleteFollowUpRecordByBiz(Integer bizType, Long bizId);
  50 +
  51 + /**
33 52 * 获得跟进记录
34 53 *
35 54 * @param id 编号
... ... @@ -45,4 +64,13 @@ public interface CrmFollowUpRecordService {
45 64 */
46 65 PageResult<CrmFollowUpRecordDO> getFollowUpRecordPage(CrmFollowUpRecordPageReqVO pageReqVO);
47 66  
  67 + /**
  68 + * 获取跟进记录
  69 + *
  70 + * @param bizType 模块类型
  71 + * @param bizIds 模块数据编号
  72 + * @return 跟进列表
  73 + */
  74 + List<CrmFollowUpRecordDO> getFollowUpRecordByBiz(Integer bizType, Collection<Long> bizIds);
  75 +
48 76 }
49 77 \ No newline at end of file
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordServiceImpl.java
... ... @@ -17,6 +17,7 @@ import cn.iocoder.yudao.module.crm.service.clue.CrmClueService;
17 17 import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
18 18 import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
19 19 import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
  20 +import cn.iocoder.yudao.module.crm.service.followup.bo.CrmFollowUpCreateReqBO;
20 21 import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
21 22 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
22 23 import jakarta.annotation.Resource;
... ... @@ -24,6 +25,7 @@ import org.springframework.stereotype.Service;
24 25 import org.springframework.validation.annotation.Validated;
25 26  
26 27 import java.time.LocalDateTime;
  28 +import java.util.Collection;
27 29 import java.util.Collections;
28 30 import java.util.List;
29 31  
... ... @@ -97,6 +99,16 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService {
97 99 }
98 100  
99 101 @Override
  102 + public void createFollowUpRecordBatch(List<CrmFollowUpCreateReqBO> followUpCreateReqBOs) {
  103 + if (CollUtil.isEmpty(followUpCreateReqBOs)) {
  104 + return;
  105 + }
  106 +
  107 + List<CrmFollowUpRecordDO> followUpRecords = BeanUtils.toBean(followUpCreateReqBOs, CrmFollowUpRecordDO.class);
  108 + crmFollowUpRecordMapper.insertBatch(followUpRecords);
  109 + }
  110 +
  111 + @Override
100 112 public void deleteFollowUpRecord(Long id, Long userId) {
101 113 // 校验存在
102 114 CrmFollowUpRecordDO followUpRecord = validateFollowUpRecordExists(id);
... ... @@ -113,6 +125,12 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService {
113 125 crmFollowUpRecordMapper.deleteById(id);
114 126 }
115 127  
  128 + @Override
  129 + public void deleteFollowUpRecordByBiz(Integer bizType, Long bizId) {
  130 + // 删除
  131 + crmFollowUpRecordMapper.deleteByBiz(bizType, bizId);
  132 + }
  133 +
116 134 private CrmFollowUpRecordDO validateFollowUpRecordExists(Long id) {
117 135 CrmFollowUpRecordDO followUpRecord = crmFollowUpRecordMapper.selectById(id);
118 136 if (followUpRecord == null) {
... ... @@ -132,4 +150,9 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService {
132 150 return crmFollowUpRecordMapper.selectPage(pageReqVO);
133 151 }
134 152  
  153 + @Override
  154 + public List<CrmFollowUpRecordDO> getFollowUpRecordByBiz(Integer bizType, Collection<Long> bizIds) {
  155 + return crmFollowUpRecordMapper.selectListByBiz(bizType, bizIds);
  156 + }
  157 +
135 158 }
136 159 \ No newline at end of file
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/bo/CrmFollowUpCreateReqBO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.service.followup.bo;
  2 +
  3 +import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
  4 +import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
  5 +import cn.iocoder.yudao.module.crm.enums.DictTypeConstants;
  6 +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
  7 +import jakarta.validation.constraints.NotEmpty;
  8 +import jakarta.validation.constraints.NotNull;
  9 +import lombok.Data;
  10 +
  11 +import java.time.LocalDateTime;
  12 +import java.util.List;
  13 +
  14 +/**
  15 + * 跟进信息 Create Req BO
  16 + *
  17 + * @author HUIHUI
  18 + */
  19 +@Data
  20 +public class CrmFollowUpCreateReqBO {
  21 +
  22 + /**
  23 + * 数据类型
  24 + *
  25 + * 枚举 {@link CrmBizTypeEnum}
  26 + */
  27 + @NotNull(message = "数据类型不能为空")
  28 + private Integer bizType;
  29 + /**
  30 + * 数据编号
  31 + *
  32 + * 关联 {@link CrmBizTypeEnum} 对应模块 DO 的 id 字段
  33 + */
  34 + @NotNull(message = "数据编号不能为空")
  35 + private Long bizId;
  36 +
  37 + /**
  38 + * 跟进类型
  39 + *
  40 + * 关联 {@link DictTypeConstants#CRM_FOLLOW_UP_TYPE} 字典
  41 + */
  42 + @NotNull(message = "跟进类型不能为空")
  43 + private Integer type;
  44 + /**
  45 + * 跟进内容
  46 + */
  47 + @NotEmpty(message = "跟进内容不能为空")
  48 + private String content;
  49 + /**
  50 + * 下次联系时间
  51 + */
  52 + @NotNull(message = "下次联系时间不能为空")
  53 + private LocalDateTime nextTime;
  54 +
  55 + /**
  56 + * 图片
  57 + */
  58 + private List<String> picUrls;
  59 + /**
  60 + * 附件
  61 + */
  62 + private List<String> fileUrls;
  63 +
  64 + /**
  65 + * 关联的商机编号数组
  66 + *
  67 + * 关联 {@link CrmBusinessDO#getId()}
  68 + */
  69 + private List<Long> businessIds;
  70 +
  71 + /**
  72 + * 关联的联系人编号数组
  73 + *
  74 + * 关联 {@link CrmContactDO#getId()}
  75 + */
  76 + private List<Long> contactIds;
  77 +
  78 +}
... ...
yudao-module-crm/yudao-module-crm-biz/src/test/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImplTest.java
... ... @@ -15,6 +15,7 @@ import java.util.List;
15 15  
16 16 import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE;
17 17 import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
  18 +import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
18 19 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
19 20 import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
20 21 import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
... ... @@ -45,7 +46,7 @@ public class CrmClueServiceImplTest extends BaseDbUnitTest {
45 46 CrmClueSaveReqVO reqVO = randomPojo(CrmClueSaveReqVO.class);
46 47  
47 48 // 调用
48   - Long clueId = clueService.createClue(reqVO);
  49 + Long clueId = clueService.createClue(reqVO, getLoginUserId());
49 50 // 断言
50 51 assertNotNull(clueId);
51 52 // 校验记录的属性是否正确
... ...