Commit 6850c7c71d236f73a99658a0af04b98e9bdb2e6b

Authored by puhui999
1 parent 7bb17151

CRM: 完善跟进日志 TODO

yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/CrmOperateLogController.java
@@ -2,14 +2,14 @@ package cn.iocoder.yudao.module.crm.controller.admin.operatelog; @@ -2,14 +2,14 @@ package cn.iocoder.yudao.module.crm.controller.admin.operatelog;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  5 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
5 import cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo.CrmOperateLogPageReqVO; 6 import cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo.CrmOperateLogPageReqVO;
  7 +import cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo.CrmOperateLogV2RespVO;
6 import cn.iocoder.yudao.module.crm.enums.LogRecordConstants; 8 import cn.iocoder.yudao.module.crm.enums.LogRecordConstants;
7 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 9 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
8 import cn.iocoder.yudao.module.system.api.logger.OperateLogApi; 10 import cn.iocoder.yudao.module.system.api.logger.OperateLogApi;
9 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2PageReqDTO; 11 import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2PageReqDTO;
10 -import cn.iocoder.yudao.module.system.api.logger.dto.OperateLogV2RespDTO;  
11 import io.swagger.v3.oas.annotations.Operation; 12 import io.swagger.v3.oas.annotations.Operation;
12 -import io.swagger.v3.oas.annotations.Parameter;  
13 import io.swagger.v3.oas.annotations.tags.Tag; 13 import io.swagger.v3.oas.annotations.tags.Tag;
14 import jakarta.annotation.Resource; 14 import jakarta.annotation.Resource;
15 import jakarta.validation.Valid; 15 import jakarta.validation.Valid;
@@ -51,16 +51,14 @@ public class CrmOperateLogController { @@ -51,16 +51,14 @@ public class CrmOperateLogController {
51 BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType(), CRM_RECEIVABLE_PLAN_TYPE); 51 BIZ_TYPE_MAP.put(CrmBizTypeEnum.CRM_RECEIVABLE_PLAN.getType(), CRM_RECEIVABLE_PLAN_TYPE);
52 } 52 }
53 53
54 - // TODO @puhui999:还是搞个 VO 出来哈  
55 @GetMapping("/page") 54 @GetMapping("/page")
56 @Operation(summary = "获得操作日志") 55 @Operation(summary = "获得操作日志")
57 - @Parameter(name = "id", description = "客户编号", required = true)  
58 - @PreAuthorize("@ss.hasPermission('crm:customer:query')")  
59 - public CommonResult<PageResult<OperateLogV2RespDTO>> getCustomerOperateLog(@Valid CrmOperateLogPageReqVO pageReqVO) { 56 + @PreAuthorize("@ss.hasPermission('crm:operate-log:query')")
  57 + public CommonResult<PageResult<CrmOperateLogV2RespVO>> getCustomerOperateLog(@Valid CrmOperateLogPageReqVO pageReqVO) {
60 OperateLogV2PageReqDTO reqDTO = new OperateLogV2PageReqDTO(); 58 OperateLogV2PageReqDTO reqDTO = new OperateLogV2PageReqDTO();
61 reqDTO.setPageSize(PAGE_SIZE_NONE); // 默认不分页,需要分页需注释 59 reqDTO.setPageSize(PAGE_SIZE_NONE); // 默认不分页,需要分页需注释
62 reqDTO.setBizType(BIZ_TYPE_MAP.get(pageReqVO.getBizType())).setBizId(pageReqVO.getBizId()); 60 reqDTO.setBizType(BIZ_TYPE_MAP.get(pageReqVO.getBizType())).setBizId(pageReqVO.getBizId());
63 - return success(operateLogApi.getOperateLogPage(reqDTO)); 61 + return success(BeanUtils.toBean(operateLogApi.getOperateLogPage(reqDTO), CrmOperateLogV2RespVO.class));
64 } 62 }
65 63
66 } 64 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/operatelog/vo/CrmOperateLogV2RespVO.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.operatelog.vo;
  2 +
  3 +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +import lombok.Data;
  6 +
  7 +import java.time.LocalDateTime;
  8 +
  9 +@Schema(description = "管理后台 - CRM 跟进 Response VO")
  10 +@Data
  11 +@ExcelIgnoreUnannotated
  12 +public class CrmOperateLogV2RespVO {
  13 +
  14 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")
  15 + private Long id;
  16 + /**
  17 + * 链路追踪编号
  18 + */
  19 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")
  20 + private String traceId;
  21 + /**
  22 + * 用户编号
  23 + */
  24 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
  25 + private Long userId;
  26 + /**
  27 + * 用户名称
  28 + */
  29 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿")
  30 + private String userName;
  31 + /**
  32 + * 用户类型
  33 + */
  34 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  35 + private Integer userType;
  36 + /**
  37 + * 操作模块类型
  38 + */
  39 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")
  40 + private String type;
  41 + /**
  42 + * 操作名
  43 + */
  44 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "修改客户")
  45 + private String subType;
  46 + /**
  47 + * 操作模块业务编号
  48 + */
  49 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")
  50 + private Long bizId;
  51 + /**
  52 + * 操作内容
  53 + */
  54 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "将什么从什么改为了什么")
  55 + private String action;
  56 + /**
  57 + * 拓展字段
  58 + */
  59 + @Schema(description = "编号", example = "{orderId: 1}")
  60 + private String extra;
  61 +
  62 + /**
  63 + * 请求方法名
  64 + */
  65 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")
  66 + private String requestMethod;
  67 + /**
  68 + * 请求地址
  69 + */
  70 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")
  71 + private String requestUrl;
  72 + /**
  73 + * 用户 IP
  74 + */
  75 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")
  76 + private String userIp;
  77 + /**
  78 + * 浏览器 UA
  79 + */
  80 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")
  81 + private String userAgent;
  82 +
  83 + /**
  84 + * 创建时间
  85 + */
  86 + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-01-01")
  87 + private LocalDateTime createTime;
  88 +
  89 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/permission/CrmPermissionConvert.java
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.crm.convert.permission; @@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.crm.convert.permission;
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 4 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
5 import cn.iocoder.yudao.framework.common.util.collection.MapUtils; 5 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
  6 +import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
6 import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionRespVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionRespVO;
7 import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO; 8 import cn.iocoder.yudao.module.crm.controller.admin.permission.vo.CrmPermissionUpdateReqVO;
8 import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO; 9 import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
@@ -29,13 +30,10 @@ public interface CrmPermissionConvert { @@ -29,13 +30,10 @@ public interface CrmPermissionConvert {
29 30
30 CrmPermissionConvert INSTANCE = Mappers.getMapper(CrmPermissionConvert.class); 31 CrmPermissionConvert INSTANCE = Mappers.getMapper(CrmPermissionConvert.class);
31 32
32 - // TODO @puhui999:这个要不也搞到 copy 里  
33 - List<CrmPermissionRespVO> convert(List<CrmPermissionDO> permission);  
34 -  
35 - default List<CrmPermissionRespVO> convert(List<CrmPermissionDO> permission, List<AdminUserRespDTO> userList, 33 + default List<CrmPermissionRespVO> convert(List<CrmPermissionDO> permissions, List<AdminUserRespDTO> userList,
36 Map<Long, DeptRespDTO> deptMap, Map<Long, PostRespDTO> postMap) { 34 Map<Long, DeptRespDTO> deptMap, Map<Long, PostRespDTO> postMap) {
37 Map<Long, AdminUserRespDTO> userMap = CollectionUtils.convertMap(userList, AdminUserRespDTO::getId); 35 Map<Long, AdminUserRespDTO> userMap = CollectionUtils.convertMap(userList, AdminUserRespDTO::getId);
38 - return CollectionUtils.convertList(convert(permission), item -> { 36 + return CollectionUtils.convertList(BeanUtils.toBean(permissions, CrmPermissionRespVO.class), item -> {
39 findAndThen(userMap, item.getUserId(), user -> { 37 findAndThen(userMap, item.getUserId(), user -> {
40 item.setNickname(user.getNickname()); 38 item.setNickname(user.getNickname());
41 findAndThen(deptMap, user.getDeptId(), deptRespDTO -> item.setDeptName(deptRespDTO.getName())); 39 findAndThen(deptMap, user.getDeptId(), deptRespDTO -> item.setDeptName(deptRespDTO.getName()));
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerDO.java
@@ -115,9 +115,14 @@ public class CrmCustomerDO extends BaseDO { @@ -115,9 +115,14 @@ public class CrmCustomerDO extends BaseDO {
115 private String detailAddress; 115 private String detailAddress;
116 116
117 /** 117 /**
  118 + * 最后接收时间
  119 + */
  120 + private LocalDateTime receiveTime;
  121 + /**
118 * 最后跟进时间 122 * 最后跟进时间
119 */ 123 */
120 private LocalDateTime contactLastTime; 124 private LocalDateTime contactLastTime;
  125 +
121 /** 126 /**
122 * 最后跟进内容 127 * 最后跟进内容
123 */ 128 */
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/framework/permission/core/util/CrmPermissionUtils.java
1 package cn.iocoder.yudao.module.crm.framework.permission.core.util; 1 package cn.iocoder.yudao.module.crm.framework.permission.core.util;
2 2
  3 +import cn.hutool.core.util.ObjUtil;
3 import cn.hutool.extra.spring.SpringUtil; 4 import cn.hutool.extra.spring.SpringUtil;
  5 +import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
  6 +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
  7 +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
4 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionRoleCodeEnum; 8 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionRoleCodeEnum;
  9 +import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
5 import cn.iocoder.yudao.module.system.api.permission.PermissionApi; 10 import cn.iocoder.yudao.module.system.api.permission.PermissionApi;
6 11
  12 +import java.util.List;
  13 +
  14 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.anyMatch;
7 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; 15 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
8 16
9 /** 17 /**
@@ -23,6 +31,21 @@ public class CrmPermissionUtils { @@ -23,6 +31,21 @@ public class CrmPermissionUtils {
23 } 31 }
24 32
25 /** 33 /**
  34 + * 校验权限
  35 + *
  36 + * @param bizType 数据类型,关联 {@link CrmBizTypeEnum}
  37 + * @param bizId 数据编号,关联 {@link CrmBizTypeEnum} 对应模块 DO#getId()
  38 + * @param userId 用户编号
  39 + * @param levelEnum 权限级别
  40 + * @return boolean
  41 + */
  42 + public static boolean hasPermission(Integer bizType, Long bizId, Long userId, CrmPermissionLevelEnum levelEnum) {
  43 + List<CrmPermissionDO> permissionList = SingletonManager.getCrmPermissionService().getPermissionListByBiz(bizType, bizId);
  44 + return anyMatch(permissionList, permission ->
  45 + ObjUtil.equal(permission.getUserId(), userId) && ObjUtil.equal(permission.getLevel(), levelEnum.getLevel()));
  46 + }
  47 +
  48 + /**
26 * 静态内部类实现单例获取 49 * 静态内部类实现单例获取
27 * 50 *
28 * @author HUIHUI 51 * @author HUIHUI
@@ -30,11 +53,16 @@ public class CrmPermissionUtils { @@ -30,11 +53,16 @@ public class CrmPermissionUtils {
30 private static class SingletonManager { 53 private static class SingletonManager {
31 54
32 private static final PermissionApi PERMISSION_API = SpringUtil.getBean(PermissionApi.class); 55 private static final PermissionApi PERMISSION_API = SpringUtil.getBean(PermissionApi.class);
  56 + private static final CrmPermissionService CRM_PERMISSION_SERVICE = SpringUtil.getBean(CrmPermissionService.class);
33 57
34 public static PermissionApi getPermissionApi() { 58 public static PermissionApi getPermissionApi() {
35 return PERMISSION_API; 59 return PERMISSION_API;
36 } 60 }
37 61
  62 + public static CrmPermissionService getCrmPermissionService() {
  63 + return CRM_PERMISSION_SERVICE;
  64 + }
  65 +
38 } 66 }
39 67
40 } 68 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java
@@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.crm.service.clue; @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.crm.service.clue;
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.collection.ListUtil; 4 import cn.hutool.core.collection.ListUtil;
5 import cn.hutool.core.lang.Assert; 5 import cn.hutool.core.lang.Assert;
6 -import cn.hutool.core.util.ObjUtil;  
7 import cn.hutool.core.util.ObjectUtil; 6 import cn.hutool.core.util.ObjectUtil;
8 import cn.hutool.extra.spring.SpringUtil; 7 import cn.hutool.extra.spring.SpringUtil;
9 import cn.iocoder.yudao.framework.common.pojo.PageResult; 8 import cn.iocoder.yudao.framework.common.pojo.PageResult;
@@ -15,7 +14,6 @@ import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransformReqV @@ -15,7 +14,6 @@ import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransformReqV
15 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerSaveReqVO; 14 import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerSaveReqVO;
16 import cn.iocoder.yudao.module.crm.convert.clue.CrmClueConvert; 15 import cn.iocoder.yudao.module.crm.convert.clue.CrmClueConvert;
17 import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO; 16 import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;
18 -import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;  
19 import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO; 17 import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;
20 import cn.iocoder.yudao.module.crm.dal.mysql.clue.CrmClueMapper; 18 import cn.iocoder.yudao.module.crm.dal.mysql.clue.CrmClueMapper;
21 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 19 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
@@ -193,46 +191,39 @@ public class CrmClueServiceImpl implements CrmClueService { @@ -193,46 +191,39 @@ public class CrmClueServiceImpl implements CrmClueService {
193 } 191 }
194 192
195 // 2. 遍历线索(未转化的线索),创建对应的客户 193 // 2. 遍历线索(未转化的线索),创建对应的客户
196 - // TODO @puhui999:这里不用过滤了;  
197 - List<CrmClueDO> translateClues = filterList(clues, clue -> ObjUtil.equal(Boolean.FALSE, clue.getTransformStatus()));  
198 - List<CrmCustomerDO> customers = customerService.createCustomerBatch(convertList(translateClues, clue ->  
199 - BeanUtils.toBean(clue, CrmCustomerCreateReqBO.class)), userId);  
200 -  
201 - // TODO @puhui999:这里不用搞一个 clueCustomerIdMap 出来;可以考虑逐个创建,然后把 customerId 设置回 CrmClueDO;避免 name 匹配,极端会有问题哈;  
202 - // TODO 是不是就直接 foreach 处理好了;因为本身量不大,for 处理性能 ok,可阅读性好  
203 - Map<Long, Long> clueCustomerIdMap = new HashMap<>(translateClues.size()); 194 + clues.forEach(clue -> {
  195 + Long customerId = customerService.createCustomer(BeanUtils.toBean(clue, CrmCustomerCreateReqBO.class), userId);
  196 + clue.setCustomerId(customerId);
  197 + });
  198 +
204 // 2.1 更新线索 199 // 2.1 更新线索
205 - clueMapper.updateBatch(convertList(customers, customer -> {  
206 - CrmClueDO firstClue = findFirst(translateClues, clue -> ObjUtil.equal(clue.getName(), customer.getName()));  
207 - clueCustomerIdMap.put(firstClue.getId(), customer.getId());  
208 - return new CrmClueDO().setId(firstClue.getId()).setTransformStatus(Boolean.TRUE).setCustomerId(customer.getId());  
209 - })); 200 + clueMapper.updateBatch(convertList(clues, clue -> new CrmClueDO().setId(clue.getId()).setTransformStatus(Boolean.TRUE)
  201 + .setCustomerId(clue.getCustomerId())));
210 // 2.3 复制跟进 202 // 2.3 复制跟进
211 - updateFollowUpRecords(clueCustomerIdMap);  
212 - 203 + updateFollowUpRecords(clues);
213 // 3. 记录操作日志 204 // 3. 记录操作日志
214 - for (CrmClueDO clue : translateClues) {  
215 - // TODO @puhui999:这里优化下,translate 操作日志  
216 - getSelf().receiveClueLog(clue); 205 + for (CrmClueDO clue : clues) {
  206 + getSelf().translateCustomerLog(clue);
217 } 207 }
218 } 208 }
219 209
220 - private void updateFollowUpRecords(Map<Long, Long> clueCustomerIdMap) { 210 + private void updateFollowUpRecords(List<CrmClueDO> clues) {
221 List<CrmFollowUpRecordDO> followUpRecords = followUpRecordService.getFollowUpRecordByBiz( 211 List<CrmFollowUpRecordDO> followUpRecords = followUpRecordService.getFollowUpRecordByBiz(
222 - CrmBizTypeEnum.CRM_LEADS.getType(), clueCustomerIdMap.keySet()); 212 + CrmBizTypeEnum.CRM_LEADS.getType(), convertSet(clues, CrmClueDO::getId));
223 if (CollUtil.isEmpty(followUpRecords)) { 213 if (CollUtil.isEmpty(followUpRecords)) {
224 return; 214 return;
225 } 215 }
226 216
  217 + Map<Long, CrmClueDO> clueMap = convertMap(clues, CrmClueDO::getId);
227 // 创建跟进 218 // 创建跟进
228 followUpRecordService.createFollowUpRecordBatch(convertList(followUpRecords, followUpRecord -> 219 followUpRecordService.createFollowUpRecordBatch(convertList(followUpRecords, followUpRecord ->
229 BeanUtils.toBean(followUpRecord, CrmFollowUpCreateReqBO.class).setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()) 220 BeanUtils.toBean(followUpRecord, CrmFollowUpCreateReqBO.class).setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())
230 - .setBizId(clueCustomerIdMap.get(followUpRecord.getBizId())))); 221 + .setBizId(clueMap.get(followUpRecord.getBizId()).getCustomerId())));
231 } 222 }
232 223
233 @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_TRANSLATE_SUB_TYPE, bizNo = "{{#clue.id}}", 224 @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_TRANSLATE_SUB_TYPE, bizNo = "{{#clue.id}}",
234 success = CRM_LEADS_TRANSLATE_SUCCESS) 225 success = CRM_LEADS_TRANSLATE_SUCCESS)
235 - public void receiveClueLog(CrmClueDO clue) { 226 + public void translateCustomerLog(CrmClueDO clue) {
236 // 记录操作日志上下文 227 // 记录操作日志上下文
237 LogRecordContext.putVariable("clue", clue); 228 LogRecordContext.putVariable("clue", clue);
238 } 229 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactBusinessServiceImpl.java
@@ -6,6 +6,9 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; @@ -6,6 +6,9 @@ import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
6 import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO; 6 import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO;
7 import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO; 7 import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
8 import cn.iocoder.yudao.module.crm.dal.mysql.contactbusinesslink.CrmContactBusinessMapper; 8 import cn.iocoder.yudao.module.crm.dal.mysql.contactbusinesslink.CrmContactBusinessMapper;
  9 +import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
  10 +import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
  11 +import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
9 import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; 12 import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
10 import jakarta.annotation.Resource; 13 import jakarta.annotation.Resource;
11 import org.springframework.context.annotation.Lazy; 14 import org.springframework.context.annotation.Lazy;
@@ -19,7 +22,6 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU @@ -19,7 +22,6 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
19 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_NOT_EXISTS; 22 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_NOT_EXISTS;
20 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CONTACT_NOT_EXISTS; 23 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CONTACT_NOT_EXISTS;
21 24
22 -// TODO @puhui999:数据权限的校验;每个操作;  
23 /** 25 /**
24 * 联系人与商机的关联 Service 实现类 26 * 联系人与商机的关联 Service 实现类
25 * 27 *
@@ -40,6 +42,7 @@ public class CrmContactBusinessServiceImpl implements CrmContactBusinessService @@ -40,6 +42,7 @@ public class CrmContactBusinessServiceImpl implements CrmContactBusinessService
40 private CrmContactService contactService; 42 private CrmContactService contactService;
41 43
42 @Override 44 @Override
  45 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#createReqVO.contactId", level = CrmPermissionLevelEnum.WRITE)
43 public void createContactBusinessList(CrmContactBusinessReqVO createReqVO) { 46 public void createContactBusinessList(CrmContactBusinessReqVO createReqVO) {
44 CrmContactDO contact = contactService.getContact(createReqVO.getContactId()); 47 CrmContactDO contact = contactService.getContact(createReqVO.getContactId());
45 if (contact == null) { 48 if (contact == null) {
@@ -65,6 +68,7 @@ public class CrmContactBusinessServiceImpl implements CrmContactBusinessService @@ -65,6 +68,7 @@ public class CrmContactBusinessServiceImpl implements CrmContactBusinessService
65 } 68 }
66 69
67 @Override 70 @Override
  71 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#deleteReqVO.contactId", level = CrmPermissionLevelEnum.WRITE)
68 public void deleteContactBusinessList(CrmContactBusinessReqVO deleteReqVO) { 72 public void deleteContactBusinessList(CrmContactBusinessReqVO deleteReqVO) {
69 CrmContactDO contact = contactService.getContact(deleteReqVO.getContactId()); 73 CrmContactDO contact = contactService.getContact(deleteReqVO.getContactId());
70 if (contact == null) { 74 if (contact == null) {
@@ -76,11 +80,13 @@ public class CrmContactBusinessServiceImpl implements CrmContactBusinessService @@ -76,11 +80,13 @@ public class CrmContactBusinessServiceImpl implements CrmContactBusinessService
76 } 80 }
77 81
78 @Override 82 @Override
  83 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#contactId", level = CrmPermissionLevelEnum.WRITE)
79 public void deleteContactBusinessByContactId(Long contactId) { 84 public void deleteContactBusinessByContactId(Long contactId) {
80 - contactBusinessMapper.delete(CrmContactBusinessDO::getContactId,contactId); 85 + contactBusinessMapper.delete(CrmContactBusinessDO::getContactId, contactId);
81 } 86 }
82 87
83 @Override 88 @Override
  89 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#contactId", level = CrmPermissionLevelEnum.READ)
84 public List<CrmContactBusinessDO> getContactBusinessListByContactId(Long contactId) { 90 public List<CrmContactBusinessDO> getContactBusinessListByContactId(Long contactId) {
85 return contactBusinessMapper.selectListByContactId(contactId); 91 return contactBusinessMapper.selectListByContactId(contactId);
86 } 92 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java
@@ -100,13 +100,13 @@ public interface CrmCustomerService { @@ -100,13 +100,13 @@ public interface CrmCustomerService {
100 void updateCustomerFollowUp(CrmUpdateFollowUpReqBO customerUpdateFollowUpReqBO); 100 void updateCustomerFollowUp(CrmUpdateFollowUpReqBO customerUpdateFollowUpReqBO);
101 101
102 /** 102 /**
103 - * 批量创建客户 103 + * 创建客户
104 * 104 *
105 - * @param customerCreateReqBOs 请求  
106 - * @param userId 用户编号 105 + * @param customerCreateReq 请求信息
  106 + * @param userId 用户编号
107 * @return 客户列表 107 * @return 客户列表
108 */ 108 */
109 - List<CrmCustomerDO> createCustomerBatch(List<CrmCustomerCreateReqBO> customerCreateReqBOs, Long userId); 109 + Long createCustomer(CrmCustomerCreateReqBO customerCreateReq, Long userId);
110 110
111 // ==================== 公海相关操作 ==================== 111 // ==================== 公海相关操作 ====================
112 112
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java
@@ -43,12 +43,10 @@ import java.util.Collections; @@ -43,12 +43,10 @@ import java.util.Collections;
43 import java.util.List; 43 import java.util.List;
44 44
45 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 45 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
46 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;  
47 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*; 46 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
48 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*; 47 import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
49 import static cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum.CUSTOMER_LOCK_LIMIT; 48 import static cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum.CUSTOMER_LOCK_LIMIT;
50 import static cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum.CUSTOMER_OWNER_LIMIT; 49 import static cn.iocoder.yudao.module.crm.enums.customer.CrmCustomerLimitConfigTypeEnum.CUSTOMER_OWNER_LIMIT;
51 -import static java.util.Collections.emptyList;  
52 import static java.util.Collections.singletonList; 50 import static java.util.Collections.singletonList;
53 51
54 /** 52 /**
@@ -91,9 +89,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -91,9 +89,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
91 89
92 // 2. 插入客户 90 // 2. 插入客户
93 CrmCustomerDO customer = BeanUtils.toBean(createReqVO, CrmCustomerDO.class) 91 CrmCustomerDO customer = BeanUtils.toBean(createReqVO, CrmCustomerDO.class)
94 - .setLockStatus(false).setDealStatus(false)  
95 - .setContactLastTime(LocalDateTime.now());  
96 - // TODO @puhui999:可能要加个 receiveTime 字段,记录最后接收时间 92 + .setLockStatus(false).setDealStatus(false).setContactLastTime(LocalDateTime.now());
97 customerMapper.insert(customer); 93 customerMapper.insert(customer);
98 94
99 // 3. 创建数据权限 95 // 3. 创建数据权限
@@ -214,24 +210,24 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -214,24 +210,24 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
214 210
215 @Override 211 @Override
216 @Transactional(rollbackFor = Exception.class) 212 @Transactional(rollbackFor = Exception.class)
217 - public List<CrmCustomerDO> createCustomerBatch(List<CrmCustomerCreateReqBO> customerCreateReqBOs, Long userId) {  
218 - if (CollUtil.isEmpty(customerCreateReqBOs)) {  
219 - return emptyList();  
220 - } 213 + @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_CREATE_SUB_TYPE, bizNo = "{{#customer.id}}",
  214 + success = CRM_CUSTOMER_CREATE_SUCCESS)
  215 + public Long createCustomer(CrmCustomerCreateReqBO customerCreateReq, Long userId) {
  216 + // 1. 插入客户
  217 + CrmCustomerDO customer = BeanUtils.toBean(customerCreateReq, CrmCustomerDO.class).setOwnerUserId(userId)
  218 + .setLockStatus(false).setDealStatus(false).setReceiveTime(LocalDateTime.now());
  219 + customerMapper.insert(customer);
221 220
222 - // 创建客户  
223 - List<CrmCustomerDO> customers = convertList(customerCreateReqBOs, customerBO ->  
224 - BeanUtils.toBean(customerBO, CrmCustomerDO.class).setOwnerUserId(userId));  
225 - customerMapper.insertBatch(customers); 221 + // 2. 创建数据权限
  222 + permissionService.createPermission(new CrmPermissionCreateReqBO().setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())
  223 + .setBizId(customer.getId()).setUserId(userId).setLevel(CrmPermissionLevelEnum.OWNER.getLevel())); // 设置当前操作的人为负责人
226 224
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; 225 + // 3. 记录操作日志上下文
  226 + LogRecordContext.putVariable("customer", customer);
  227 + return customer.getId();
232 } 228 }
233 229
234 - // ==================== 公海相关操作 ==================== 230 +// ==================== 公海相关操作 ====================
235 231
236 @Override 232 @Override
237 @Transactional(rollbackFor = Exception.class) 233 @Transactional(rollbackFor = Exception.class)
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/CrmFollowUpRecordServiceImpl.java
@@ -7,7 +7,6 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; @@ -7,7 +7,6 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
7 import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordPageReqVO; 7 import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordPageReqVO;
8 import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordSaveReqVO; 8 import cn.iocoder.yudao.module.crm.controller.admin.followup.vo.CrmFollowUpRecordSaveReqVO;
9 import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO; 9 import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;
10 -import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;  
11 import cn.iocoder.yudao.module.crm.dal.mysql.followup.CrmFollowUpRecordMapper; 10 import cn.iocoder.yudao.module.crm.dal.mysql.followup.CrmFollowUpRecordMapper;
12 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; 11 import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
13 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; 12 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
@@ -31,10 +30,10 @@ import java.util.Collections; @@ -31,10 +30,10 @@ import java.util.Collections;
31 import java.util.List; 30 import java.util.List;
32 31
33 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 32 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
34 -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.anyMatch;  
35 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; 33 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
36 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.FOLLOW_UP_RECORD_DELETE_DENIED; 34 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.FOLLOW_UP_RECORD_DELETE_DENIED;
37 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.FOLLOW_UP_RECORD_NOT_EXISTS; 35 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.FOLLOW_UP_RECORD_NOT_EXISTS;
  36 +import static cn.iocoder.yudao.module.crm.framework.permission.core.util.CrmPermissionUtils.hasPermission;
38 37
39 /** 38 /**
40 * 跟进记录 Service 实现类 39 * 跟进记录 Service 实现类
@@ -94,14 +93,15 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService { @@ -94,14 +93,15 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService {
94 customerService.updateCustomerFollowUp(updateFollowUpReqBO); 93 customerService.updateCustomerFollowUp(updateFollowUpReqBO);
95 } 94 }
96 95
97 - // TODO @puhui999:这两个,不更新 contactLastTime、contactLastContent,只更新 nextTime  
98 // 3.1 更新 contactIds 对应的记录 96 // 3.1 更新 contactIds 对应的记录
99 if (CollUtil.isNotEmpty(createReqVO.getContactIds())) { 97 if (CollUtil.isNotEmpty(createReqVO.getContactIds())) {
100 - contactService.updateContactFollowUpBatch(convertList(createReqVO.getContactIds(), updateFollowUpReqBO::setBizId)); 98 + contactService.updateContactFollowUpBatch(convertList(createReqVO.getContactIds(),
  99 + contactId -> updateFollowUpReqBO.setBizId(contactId).setContactLastTime(null).setContactLastContent(null)));
101 } 100 }
102 // 3.2 需要更新 businessIds、contactIds 对应的记录 101 // 3.2 需要更新 businessIds、contactIds 对应的记录
103 if (CollUtil.isNotEmpty(createReqVO.getBusinessIds())) { 102 if (CollUtil.isNotEmpty(createReqVO.getBusinessIds())) {
104 - businessService.updateBusinessFollowUpBatch(convertList(createReqVO.getBusinessIds(), updateFollowUpReqBO::setBizId)); 103 + businessService.updateBusinessFollowUpBatch(convertList(createReqVO.getBusinessIds(),
  104 + businessId -> updateFollowUpReqBO.setBizId(businessId).setContactLastTime(null).setContactLastContent(null)));
105 } 105 }
106 return followUpRecord.getId(); 106 return followUpRecord.getId();
107 } 107 }
@@ -112,8 +112,7 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService { @@ -112,8 +112,7 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService {
112 return; 112 return;
113 } 113 }
114 114
115 - List<CrmFollowUpRecordDO> followUpRecords = BeanUtils.toBean(followUpCreateReqBOs, CrmFollowUpRecordDO.class);  
116 - crmFollowUpRecordMapper.insertBatch(followUpRecords); 115 + crmFollowUpRecordMapper.insertBatch(BeanUtils.toBean(followUpCreateReqBOs, CrmFollowUpRecordDO.class));
117 } 116 }
118 117
119 @Override 118 @Override
@@ -121,12 +120,7 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService { @@ -121,12 +120,7 @@ public class CrmFollowUpRecordServiceImpl implements CrmFollowUpRecordService {
121 // 校验存在 120 // 校验存在
122 CrmFollowUpRecordDO followUpRecord = validateFollowUpRecordExists(id); 121 CrmFollowUpRecordDO followUpRecord = validateFollowUpRecordExists(id);
123 // 校验权限 122 // 校验权限
124 - // TODO @puhui999:是不是封装一个 hasPermission,更简介一点;  
125 - List<CrmPermissionDO> permissionList = permissionService.getPermissionListByBiz(  
126 - followUpRecord.getBizType(), followUpRecord.getBizId());  
127 - boolean hasPermission = anyMatch(permissionList, permission ->  
128 - ObjUtil.equal(permission.getUserId(), userId) && ObjUtil.equal(permission.getLevel(), CrmPermissionLevelEnum.OWNER.getLevel()));  
129 - if (!hasPermission) { 123 + if (!hasPermission(followUpRecord.getBizType(), followUpRecord.getBizId(), userId, CrmPermissionLevelEnum.OWNER)) {
130 throw exception(FOLLOW_UP_RECORD_DELETE_DENIED); 124 throw exception(FOLLOW_UP_RECORD_DELETE_DENIED);
131 } 125 }
132 126
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/followup/bo/CrmUpdateFollowUpReqBO.java
@@ -21,7 +21,6 @@ public class CrmUpdateFollowUpReqBO { @@ -21,7 +21,6 @@ public class CrmUpdateFollowUpReqBO {
21 21
22 @Schema(description = "最后跟进时间") 22 @Schema(description = "最后跟进时间")
23 @DiffLogField(name = "最后跟进时间") 23 @DiffLogField(name = "最后跟进时间")
24 - @NotNull(message = "最后跟进时间不能为空")  
25 private LocalDateTime contactLastTime; 24 private LocalDateTime contactLastTime;
26 25
27 @Schema(description = "下次联系时间") 26 @Schema(description = "下次联系时间")
@@ -30,7 +29,6 @@ public class CrmUpdateFollowUpReqBO { @@ -30,7 +29,6 @@ public class CrmUpdateFollowUpReqBO {
30 29
31 @Schema(description = "最后更进内容") 30 @Schema(description = "最后更进内容")
32 @DiffLogField(name = "最后更进内容") 31 @DiffLogField(name = "最后更进内容")
33 - @NotNull(message = "最后更进内容不能为空")  
34 private String contactLastContent; 32 private String contactLastContent;
35 33
36 } 34 }