Commit ca9f3c522d9c0d89a9168e5c630d5b583c81057c

Authored by puhui999
1 parent 8675d135

CRM-线索:完善操作日志

yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java
... ... @@ -14,9 +14,13 @@ public interface LogRecordConstants {
14 14 String CRM_LEADS_CREATE_SUB_TYPE = "创建线索";
15 15 String CRM_LEADS_CREATE_SUCCESS = "创建了线索{{#clue.name}}";
16 16 String CRM_LEADS_UPDATE_SUB_TYPE = "更新线索";
17   - String CRM_LEADS_UPDATE_SUCCESS = "更新了线索【{{#clueName}}】: {_DIFF{#updateReqVO}}";
  17 + String CRM_LEADS_UPDATE_SUCCESS = "更新了线索【{{#clueName}}】: {_DIFF{#updateReq}}";
18 18 String CRM_LEADS_DELETE_SUB_TYPE = "删除线索";
19 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}}】转化为客户";
20 24  
21 25 // ======================= CRM_CUSTOMER 客户 =======================
22 26  
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/clue/vo/CrmClueSaveReqVO.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.crm.controller.admin.clue.vo;
2 2  
3 3 import cn.iocoder.yudao.framework.common.validation.Mobile;
4 4 import cn.iocoder.yudao.framework.common.validation.Telephone;
  5 +import com.mzt.logapi.starter.annotation.DiffLogField;
5 6 import io.swagger.v3.oas.annotations.media.Schema;
6 7 import jakarta.validation.constraints.NotEmpty;
7 8 import lombok.Data;
... ... @@ -19,32 +20,39 @@ public class CrmClueSaveReqVO {
19 20 private Long id;
20 21  
21 22 @Schema(description = "线索名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "线索xxx")
  23 + @DiffLogField(name = "线索名称")
22 24 @NotEmpty(message = "线索名称不能为空")
23 25 private String name;
24 26  
25 27 @Schema(description = "下次联系时间", example = "2023-10-18 01:00:00")
  28 + @DiffLogField(name = "下次联系时间")
26 29 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
27 30 private LocalDateTime contactNextTime;
28 31  
29 32 @Schema(description = "电话", example = "18000000000")
  33 + @DiffLogField(name = "电话")
30 34 @Telephone
31 35 private String telephone;
32 36  
33 37 @Schema(description = "手机号", example = "18000000000")
  38 + @DiffLogField(name = "手机号")
34 39 @Mobile
35 40 private String mobile;
36 41  
37 42 @Schema(description = "地址", example = "北京市海淀区")
  43 + @DiffLogField(name = "地址")
38 44 private String address;
39 45  
40 46 @Schema(description = "最后跟进时间")
41 47 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  48 + @DiffLogField(name = "最后跟进时间")
42 49 private LocalDateTime contactLastTime;
43 50  
44 51 @Schema(description = "负责人编号", example = "2048")
45 52 private Long ownerUserId;
46 53  
47 54 @Schema(description = "备注", example = "随便")
  55 + @DiffLogField(name = "备注")
48 56 private String remark;
49 57  
50 58 }
... ...
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java
... ... @@ -5,6 +5,7 @@ import cn.hutool.core.collection.ListUtil;
5 5 import cn.hutool.core.lang.Assert;
6 6 import cn.hutool.core.util.ObjUtil;
7 7 import cn.hutool.core.util.ObjectUtil;
  8 +import cn.hutool.extra.spring.SpringUtil;
8 9 import cn.iocoder.yudao.framework.common.pojo.PageResult;
9 10 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
10 11 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO;
... ... @@ -76,7 +77,7 @@ public class CrmClueServiceImpl implements CrmClueService {
76 77 validateRelationDataExists(createReqVO);
77 78  
78 79 // 2. 插入
79   - CrmClueDO clue = BeanUtils.toBean(createReqVO, CrmClueDO.class);
  80 + CrmClueDO clue = BeanUtils.toBean(createReqVO, CrmClueDO.class).setId(null);
80 81 if (ObjUtil.isNull(createReqVO.getOwnerUserId())) {
81 82 clue.setOwnerUserId(userId); // 如果没有设置负责人那么默认操作人为负责人
82 83 } else {
... ... @@ -99,16 +100,16 @@ public class CrmClueServiceImpl implements CrmClueService {
99 100 @Transactional(rollbackFor = Exception.class)
100 101 @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
101 102 success = CRM_LEADS_UPDATE_SUCCESS)
102   - @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE)
103   - public void updateClue(CrmClueSaveReqVO updateReqVO) {
104   - Assert.notNull(updateReqVO.getId(), "线索编号不能为空");
  103 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#updateReq.id", level = CrmPermissionLevelEnum.OWNER)
  104 + public void updateClue(CrmClueSaveReqVO updateReq) {
  105 + Assert.notNull(updateReq.getId(), "线索编号不能为空");
105 106 // 1. 校验线索是否存在
106   - CrmClueDO oldClue = validateClueExists(updateReqVO.getId());
  107 + CrmClueDO oldClue = validateClueExists(updateReq.getId());
107 108 // 2. 校验关联数据
108   - validateRelationDataExists(updateReqVO);
  109 + validateRelationDataExists(updateReq);
109 110  
110 111 // 3. 更新
111   - CrmClueDO updateObj = BeanUtils.toBean(updateReqVO, CrmClueDO.class);
  112 + CrmClueDO updateObj = BeanUtils.toBean(updateReq, CrmClueDO.class);
112 113 clueMapper.updateById(updateObj);
113 114  
114 115 // 3. 记录操作日志上下文
... ... @@ -117,8 +118,19 @@ public class CrmClueServiceImpl implements CrmClueService {
117 118 }
118 119  
119 120 @Override
120   - public void updateClueFollowUp(CrmUpdateFollowUpReqBO clueUpdateFollowUpReqBO) {
121   - clueMapper.updateById(BeanUtils.toBean(clueUpdateFollowUpReqBO, CrmClueDO.class).setId(clueUpdateFollowUpReqBO.getBizId()));
  121 + @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_UPDATE_SUB_TYPE, bizNo = "{{#updateReq.bizId}",
  122 + success = CRM_LEADS_UPDATE_SUCCESS)
  123 + public void updateClueFollowUp(CrmUpdateFollowUpReqBO updateReq) {
  124 + // 校验线索是否存在
  125 + CrmClueDO oldClue = validateClueExists(updateReq.getBizId());
  126 +
  127 + // 更新
  128 + clueMapper.updateById(BeanUtils.toBean(updateReq, CrmClueDO.class).setId(updateReq.getBizId()));
  129 +
  130 + // 3. 记录操作日志上下文
  131 + LogRecordContext.putVariable(DiffParseFunction.OLD_OBJECT, BeanUtils.toBean(oldClue, CrmUpdateFollowUpReqBO.class));
  132 + LogRecordContext.putVariable("clueName", oldClue.getName());
  133 +
122 134 }
123 135  
124 136 @Override
... ... @@ -143,48 +155,28 @@ public class CrmClueServiceImpl implements CrmClueService {
143 155 LogRecordContext.putVariable("clueName", clue.getName());
144 156 }
145 157  
146   - private CrmClueDO validateClueExists(Long id) {
147   - CrmClueDO crmClueDO = clueMapper.selectById(id);
148   - if (crmClueDO == null) {
149   - throw exception(CLUE_NOT_EXISTS);
150   - }
151   - return crmClueDO;
152   - }
153   -
154   - @Override
155   - @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#id", level = CrmPermissionLevelEnum.READ)
156   - public CrmClueDO getClue(Long id) {
157   - return clueMapper.selectById(id);
158   - }
159   -
160   - @Override
161   - public List<CrmClueDO> getClueList(Collection<Long> ids, Long userId) {
162   - if (CollUtil.isEmpty(ids)) {
163   - return ListUtil.empty();
164   - }
165   - return clueMapper.selectBatchIds(ids, userId);
166   - }
167   -
168   - @Override
169   - public PageResult<CrmClueDO> getCluePage(CrmCluePageReqVO pageReqVO, Long userId) {
170   - return clueMapper.selectPage(pageReqVO, userId);
171   - }
172 158  
173 159 @Override
  160 + @Transactional(rollbackFor = Exception.class)
  161 + @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_TRANSFER_SUB_TYPE, bizNo = "{{#reqVO.id}}",
  162 + success = CRM_LEADS_TRANSFER_SUCCESS)
  163 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
174 164 public void transferClue(CrmClueTransferReqVO reqVO, Long userId) {
175 165 // 1 校验线索是否存在
176   - validateClueExists(reqVO.getId());
  166 + CrmClueDO clue = validateClueExists(reqVO.getId());
177 167  
178 168 // 2.1 数据权限转移
179 169 crmPermissionService.transferPermission(CrmClueConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_LEADS.getType()));
180 170 // 2.2 设置新的负责人
181 171 clueMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
182 172  
183   - // 3. TODO 记录转移日志
  173 + // 3. 记录转移日志
  174 + LogRecordContext.putVariable("clue", clue);
184 175 }
185 176  
186 177 @Override
187 178 @Transactional(rollbackFor = Exception.class)
  179 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#id", level = CrmPermissionLevelEnum.OWNER)
188 180 public void translateCustomer(CrmClueTransformReqVO reqVO, Long userId) {
189 181 // 1.1 校验线索都存在
190 182 Set<Long> clueIds = reqVO.getIds();
... ... @@ -214,6 +206,11 @@ public class CrmClueServiceImpl implements CrmClueService {
214 206 }));
215 207 // 2.3 复制跟进
216 208 updateFollowUpRecords(clueCustomerIdMap);
  209 +
  210 + // 3. 记录操作日志
  211 + for (CrmClueDO clue : translateClues) {
  212 + getSelf().receiveClueLog(clue);
  213 + }
217 214 }
218 215  
219 216 private void updateFollowUpRecords(Map<Long, Long> clueCustomerIdMap) {
... ... @@ -229,6 +226,13 @@ public class CrmClueServiceImpl implements CrmClueService {
229 226 .setBizId(clueCustomerIdMap.get(followUpRecord.getBizId()))));
230 227 }
231 228  
  229 + @LogRecord(type = CRM_LEADS_TYPE, subType = CRM_LEADS_TRANSLATE_SUB_TYPE, bizNo = "{{#clue.id}}",
  230 + success = CRM_LEADS_TRANSLATE_SUCCESS)
  231 + public void receiveClueLog(CrmClueDO clue) {
  232 + // 记录操作日志上下文
  233 + LogRecordContext.putVariable("clue", clue);
  234 + }
  235 +
232 236 private void validateRelationDataExists(CrmClueSaveReqVO reqVO) {
233 237 // 校验负责人
234 238 if (Objects.nonNull(reqVO.getOwnerUserId()) &&
... ... @@ -237,4 +241,40 @@ public class CrmClueServiceImpl implements CrmClueService {
237 241 }
238 242 }
239 243  
  244 + private CrmClueDO validateClueExists(Long id) {
  245 + CrmClueDO crmClueDO = clueMapper.selectById(id);
  246 + if (crmClueDO == null) {
  247 + throw exception(CLUE_NOT_EXISTS);
  248 + }
  249 + return crmClueDO;
  250 + }
  251 +
  252 + @Override
  253 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_LEADS, bizId = "#id", level = CrmPermissionLevelEnum.READ)
  254 + public CrmClueDO getClue(Long id) {
  255 + return clueMapper.selectById(id);
  256 + }
  257 +
  258 + @Override
  259 + public List<CrmClueDO> getClueList(Collection<Long> ids, Long userId) {
  260 + if (CollUtil.isEmpty(ids)) {
  261 + return ListUtil.empty();
  262 + }
  263 + return clueMapper.selectBatchIds(ids, userId);
  264 + }
  265 +
  266 + @Override
  267 + public PageResult<CrmClueDO> getCluePage(CrmCluePageReqVO pageReqVO, Long userId) {
  268 + return clueMapper.selectPage(pageReqVO, userId);
  269 + }
  270 +
  271 + /**
  272 + * 获得自身的代理对象,解决 AOP 生效问题
  273 + *
  274 + * @return 自己
  275 + */
  276 + private CrmClueServiceImpl getSelf() {
  277 + return SpringUtil.getBean(getClass());
  278 + }
  279 +
240 280 }
... ...