Commit 3e6524932b06cea91cff375ea31cb9a88e34c680

Authored by YunaiV
1 parent 69a974ef

✨ CRM:优化客户的详情、更新成交状态

Showing 19 changed files with 204 additions and 229 deletions
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
@@ -51,6 +51,7 @@ public interface ErrorCodeConstants { @@ -51,6 +51,7 @@ public interface ErrorCodeConstants {
51 ErrorCode CUSTOMER_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_020_006_012, "导入客户数据不能为空!"); 51 ErrorCode CUSTOMER_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_020_006_012, "导入客户数据不能为空!");
52 ErrorCode CUSTOMER_CREATE_NAME_NOT_NULL = new ErrorCode(1_020_006_013, "客户名称不能为空!"); 52 ErrorCode CUSTOMER_CREATE_NAME_NOT_NULL = new ErrorCode(1_020_006_013, "客户名称不能为空!");
53 ErrorCode CUSTOMER_NAME_EXISTS = new ErrorCode(1_020_006_014, "已存在名为【{}】的客户!"); 53 ErrorCode CUSTOMER_NAME_EXISTS = new ErrorCode(1_020_006_014, "已存在名为【{}】的客户!");
  54 + ErrorCode CUSTOMER_UPDATE_DEAL_STATUS_FAIL = new ErrorCode(1_020_006_015, "更新客户的成交状态失败,原因:已经是该状态,无需更新");
54 55
55 // ========== 权限管理 1_020_007_000 ========== 56 // ========== 权限管理 1_020_007_000 ==========
56 ErrorCode CRM_PERMISSION_NOT_EXISTS = new ErrorCode(1_020_007_000, "数据权限不存在"); 57 ErrorCode CRM_PERMISSION_NOT_EXISTS = new ErrorCode(1_020_007_000, "数据权限不存在");
yudao-module-crm/yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/enums/LogRecordConstants.java
@@ -43,6 +43,8 @@ public interface LogRecordConstants { @@ -43,6 +43,8 @@ public interface LogRecordConstants {
43 String CRM_CUSTOMER_RECEIVE_SUCCESS = "{{#ownerUserName != null ? '将客户【' + #customer.name + '】分配给【' + #ownerUserName + '】' : '领取客户【' + #customer.name + '】'}}"; 43 String CRM_CUSTOMER_RECEIVE_SUCCESS = "{{#ownerUserName != null ? '将客户【' + #customer.name + '】分配给【' + #ownerUserName + '】' : '领取客户【' + #customer.name + '】'}}";
44 String CRM_CUSTOMER_IMPORT_SUB_TYPE = "{{#isUpdate ? '导入并更新客户' : '导入客户'}}"; 44 String CRM_CUSTOMER_IMPORT_SUB_TYPE = "{{#isUpdate ? '导入并更新客户' : '导入客户'}}";
45 String CRM_CUSTOMER_IMPORT_SUCCESS = "{{#isUpdate ? '导入并更新了客户【'+ #customer.name +'】' : '导入了客户【'+ #customer.name +'】'}}"; 45 String CRM_CUSTOMER_IMPORT_SUCCESS = "{{#isUpdate ? '导入并更新了客户【'+ #customer.name +'】' : '导入了客户【'+ #customer.name +'】'}}";
  46 + String CRM_CUSTOMER_UPDATE_DEAL_STATUS_SUB_TYPE = "更新客户成交状态";
  47 + String CRM_CUSTOMER_UPDATE_DEAL_STATUS_SUCCESS = "更新了客户【{{#customerName}}】的成交状态为【{{#dealStatus ? '已成交' : '未成交'}}】";
46 48
47 // ======================= CRM_CUSTOMER_LIMIT_CONFIG 客户限制配置 ======================= 49 // ======================= CRM_CUSTOMER_LIMIT_CONFIG 客户限制配置 =======================
48 50
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.http deleted
1 -### 请求 /transfer  
2 -PUT {{baseUrl}}/crm/customer/transfer  
3 -Content-Type: application/-id: {{adminTenentId}}json  
4 -Authorization: Bearer {{token}}  
5 -tenant  
6 -  
7 -{  
8 - "id": 10,  
9 - "newOwnerUserId": 127  
10 -}  
11 -  
12 -### 自定义日志记录结果  
13 -### 操作日志 ===> OperateLogV2CreateReqBO(traceId=, userId=1, userType=2, module=CRM-客户, name=客户转移, bizId=10, content=把客户【张三】的负责人从【芋道源码(15612345678)】变更为了【tttt】, requestMethod=PUT, requestUrl=/admin-api/crm/customer/transfer, userIp=127.0.0.1, userAgent=Apache-HttpClient/4.5.14 (Java/17.0.9))  
14 -  
15 -### diff 日志  
16 -### | 操作日志 ===> OperateLogV2CreateReqBO(traceId=, userId=1, userType=2, module=CRM-客户, name=更新客户, bizId=11, content=更新了客户【所属行业】从【H 住宿和餐饮业】修改为【D 电力、热力、燃气及水生产和供应业】;【客户等级】从【C (非优先客户)】修改为【A (重点客户)】;【客户来源】从【线上咨询】修改为【预约上门】, requestMethod=PUT, requestUrl=/admin-api/crm/customer/update, userIp=0:0:0:0:0:0:0:1, userAgent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36)  
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/CrmCustomerController.java
@@ -5,12 +5,14 @@ import cn.hutool.core.map.MapUtil; @@ -5,12 +5,14 @@ import cn.hutool.core.map.MapUtil;
5 import cn.hutool.core.util.ObjUtil; 5 import cn.hutool.core.util.ObjUtil;
6 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 6 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
7 import cn.iocoder.yudao.framework.common.pojo.PageResult; 7 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  8 +import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
8 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; 9 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
  10 +import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
9 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 11 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
10 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; 12 import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
  13 +import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
11 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; 14 import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
12 -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.*;  
13 -import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert; 15 +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer.*;
14 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 16 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
15 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO; 17 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;
16 import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum; 18 import cn.iocoder.yudao.module.crm.enums.common.CrmSceneTypeEnum;
@@ -22,11 +24,11 @@ import cn.iocoder.yudao.module.system.api.user.AdminUserApi; @@ -22,11 +24,11 @@ import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
22 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 24 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
23 import io.swagger.v3.oas.annotations.Operation; 25 import io.swagger.v3.oas.annotations.Operation;
24 import io.swagger.v3.oas.annotations.Parameter; 26 import io.swagger.v3.oas.annotations.Parameter;
  27 +import io.swagger.v3.oas.annotations.Parameters;
25 import io.swagger.v3.oas.annotations.tags.Tag; 28 import io.swagger.v3.oas.annotations.tags.Tag;
26 import jakarta.annotation.Resource; 29 import jakarta.annotation.Resource;
27 import jakarta.servlet.http.HttpServletResponse; 30 import jakarta.servlet.http.HttpServletResponse;
28 import jakarta.validation.Valid; 31 import jakarta.validation.Valid;
29 -import org.mapstruct.ap.internal.util.Collections;  
30 import org.springframework.security.access.prepost.PreAuthorize; 32 import org.springframework.security.access.prepost.PreAuthorize;
31 import org.springframework.validation.annotation.Validated; 33 import org.springframework.validation.annotation.Validated;
32 import org.springframework.web.bind.annotation.*; 34 import org.springframework.web.bind.annotation.*;
@@ -45,6 +47,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils. @@ -45,6 +47,7 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
45 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; 47 import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
46 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; 48 import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
47 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CUSTOMER_POOL_CONFIG_NOT_EXISTS_OR_DISABLED; 49 import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CUSTOMER_POOL_CONFIG_NOT_EXISTS_OR_DISABLED;
  50 +import static java.util.Collections.singletonList;
48 51
49 @Tag(name = "管理后台 - CRM 客户") 52 @Tag(name = "管理后台 - CRM 客户")
50 @RestController 53 @RestController
@@ -77,6 +80,18 @@ public class CrmCustomerController { @@ -77,6 +80,18 @@ public class CrmCustomerController {
77 return success(true); 80 return success(true);
78 } 81 }
79 82
  83 + @PutMapping("/update-deal-status")
  84 + @Operation(summary = "更新客户的成交状态")
  85 + @Parameters({
  86 + @Parameter(name = "id", description = "客户编号", required = true),
  87 + @Parameter(name = "dealStatus", description = "成交状态", required = true)
  88 + })
  89 + public CommonResult<Boolean> updateCustomerDealStatus(@RequestParam("id") Long id,
  90 + @RequestParam("dealStatus") Boolean dealStatus) {
  91 + customerService.updateCustomerDealStatus(id, dealStatus);
  92 + return success(true);
  93 + }
  94 +
80 @DeleteMapping("/delete") 95 @DeleteMapping("/delete")
81 @Operation(summary = "删除客户") 96 @Operation(summary = "删除客户")
82 @Parameter(name = "id", description = "客户编号", required = true) 97 @Parameter(name = "id", description = "客户编号", required = true)
@@ -93,14 +108,15 @@ public class CrmCustomerController { @@ -93,14 +108,15 @@ public class CrmCustomerController {
93 public CommonResult<CrmCustomerRespVO> getCustomer(@RequestParam("id") Long id) { 108 public CommonResult<CrmCustomerRespVO> getCustomer(@RequestParam("id") Long id) {
94 // 1. 获取客户 109 // 1. 获取客户
95 CrmCustomerDO customer = customerService.getCustomer(id); 110 CrmCustomerDO customer = customerService.getCustomer(id);
  111 + // 2. 拼接数据
  112 + return success(buildCustomerDetail(customer));
  113 + }
  114 +
  115 + public CrmCustomerRespVO buildCustomerDetail(CrmCustomerDO customer) {
96 if (customer == null) { 116 if (customer == null) {
97 - return success(null); 117 + return null;
98 } 118 }
99 - // 2. 拼接数据  
100 - Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(  
101 - Collections.asSet(Long.valueOf(customer.getCreator()), customer.getOwnerUserId()));  
102 - Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));  
103 - return success(CrmCustomerConvert.INSTANCE.convert(customer, userMap, deptMap)); 119 + return buildCustomerDetailList(singletonList(customer)).get(0);
104 } 120 }
105 121
106 @GetMapping("/page") 122 @GetMapping("/page")
@@ -112,16 +128,38 @@ public class CrmCustomerController { @@ -112,16 +128,38 @@ public class CrmCustomerController {
112 if (CollUtil.isEmpty(pageResult.getList())) { 128 if (CollUtil.isEmpty(pageResult.getList())) {
113 return success(PageResult.empty(pageResult.getTotal())); 129 return success(PageResult.empty(pageResult.getTotal()));
114 } 130 }
115 -  
116 // 2. 拼接数据 131 // 2. 拼接数据
117 - Map<Long, Long> poolDayMap = Boolean.TRUE.equals(pageVO.getPool()) ? null :  
118 - getPoolDayMap(pageResult.getList()); // 客户界面,需要查看距离进入公海的时间  
119 - Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(  
120 - convertSetByFlatMap(pageResult.getList(), user -> Stream.of(Long.parseLong(user.getCreator()), user.getOwnerUserId()))); 132 + return success(new PageResult<>(buildCustomerDetailList(pageResult.getList()), pageResult.getTotal()));
  133 + }
  134 +
  135 + public List<CrmCustomerRespVO> buildCustomerDetailList(List<CrmCustomerDO> list) {
  136 + if (CollUtil.isEmpty(list)) {
  137 + return java.util.Collections.emptyList();
  138 + }
  139 + // 1.1 获取创建人、负责人列表
  140 + Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(list,
  141 + contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getOwnerUserId())));
121 Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); 142 Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
122 - return success(CrmCustomerConvert.INSTANCE.convertPage(pageResult, userMap, deptMap, poolDayMap)); 143 + // 1.2 获取距离进入公海的时间
  144 + Map<Long, Long> poolDayMap = getPoolDayMap(list);
  145 + // 2. 转换成 VO
  146 + return BeanUtils.toBean(list, CrmCustomerRespVO.class, customerVO -> {
  147 + customerVO.setAreaName(AreaUtils.format(customerVO.getAreaId()));
  148 + // 2.1 设置创建人、负责人名称
  149 + MapUtils.findAndThen(userMap, NumberUtils.parseLong(customerVO.getCreator()),
  150 + user -> customerVO.setCreatorName(user.getNickname()));
  151 + MapUtils.findAndThen(userMap, customerVO.getOwnerUserId(), user -> {
  152 + customerVO.setOwnerUserName(user.getNickname());
  153 + MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> customerVO.setOwnerUserDeptName(dept.getName()));
  154 + });
  155 + // 2.2 设置距离进入公海的时间
  156 + if (customerVO.getOwnerUserId() != null) {
  157 + customerVO.setPoolDay(poolDayMap.get(customerVO.getId()));
  158 + }
  159 + });
123 } 160 }
124 161
  162 + // TODO @芋艿:需要 review 下
125 @GetMapping("/put-in-pool-remind-page") 163 @GetMapping("/put-in-pool-remind-page")
126 @Operation(summary = "获得待进入公海客户分页") 164 @Operation(summary = "获得待进入公海客户分页")
127 @PreAuthorize("@ss.hasPermission('crm:customer:query')") 165 @PreAuthorize("@ss.hasPermission('crm:customer:query')")
@@ -141,12 +179,7 @@ public class CrmCustomerController { @@ -141,12 +179,7 @@ public class CrmCustomerController {
141 } 179 }
142 180
143 // 2. 拼接数据 181 // 2. 拼接数据
144 - // TODO @芋艿:合并 getCustomerPage 和 getPutInPoolRemindCustomerPage 的后置处理;  
145 - Map<Long, Long> poolDayMap = getPoolDayMap(pageResult.getList()); // 客户界面,需要查看距离进入公海的时间  
146 - Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(  
147 - convertSetByFlatMap(pageResult.getList(), user -> Stream.of(Long.parseLong(user.getCreator()), user.getOwnerUserId())));  
148 - Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));  
149 - return success(CrmCustomerConvert.INSTANCE.convertPage(pageResult, userMap, deptMap, poolDayMap)); 182 + return success(new PageResult<>(buildCustomerDetailList(pageResult.getList()), pageResult.getTotal()));
150 } 183 }
151 184
152 @GetMapping("/put-in-pool-remind-count") 185 @GetMapping("/put-in-pool-remind-count")
@@ -182,22 +215,26 @@ public class CrmCustomerController { @@ -182,22 +215,26 @@ public class CrmCustomerController {
182 } 215 }
183 216
184 /** 217 /**
185 - * 获取距离进入公海的时间 218 + * 获取距离进入公海的时间 Map
186 * 219 *
187 - * @param customerList 客户列表  
188 - * @return Map<key 客户编号, value 距离进入公海的时间> 220 + * @param list 客户列表
  221 + * @return key 客户编号, value 距离进入公海的时间
189 */ 222 */
190 - private Map<Long, Long> getPoolDayMap(List<CrmCustomerDO> customerList) { 223 + private Map<Long, Long> getPoolDayMap(List<CrmCustomerDO> list) {
191 CrmCustomerPoolConfigDO poolConfig = customerPoolConfigService.getCustomerPoolConfig(); 224 CrmCustomerPoolConfigDO poolConfig = customerPoolConfigService.getCustomerPoolConfig();
192 if (poolConfig == null || !poolConfig.getEnabled()) { 225 if (poolConfig == null || !poolConfig.getEnabled()) {
193 return MapUtil.empty(); 226 return MapUtil.empty();
194 } 227 }
195 - return convertMap(customerList, CrmCustomerDO::getId, customer -> { 228 + return convertMap(list, CrmCustomerDO::getId, customer -> {
  229 + // TODO 芋艿:这样计算,貌似有点问题
196 // 1.1 未成交放入公海天数 230 // 1.1 未成交放入公海天数
197 long dealExpireDay = 0; 231 long dealExpireDay = 0;
198 if (!customer.getDealStatus()) { 232 if (!customer.getDealStatus()) {
199 dealExpireDay = poolConfig.getDealExpireDays() - LocalDateTimeUtils.between(customer.getCreateTime()); 233 dealExpireDay = poolConfig.getDealExpireDays() - LocalDateTimeUtils.between(customer.getCreateTime());
200 } 234 }
  235 + if (dealExpireDay < 0) {
  236 + dealExpireDay = 0;
  237 + }
201 // 1.2 未跟进放入公海天数 238 // 1.2 未跟进放入公海天数
202 LocalDateTime lastTime = ObjUtil.defaultIfNull(customer.getContactLastTime(), customer.getCreateTime()); 239 LocalDateTime lastTime = ObjUtil.defaultIfNull(customer.getContactLastTime(), customer.getCreateTime());
203 long contactExpireDay = poolConfig.getContactExpireDays() - LocalDateTimeUtils.between(lastTime); 240 long contactExpireDay = poolConfig.getContactExpireDays() - LocalDateTimeUtils.between(lastTime);
@@ -230,7 +267,7 @@ public class CrmCustomerController { @@ -230,7 +267,7 @@ public class CrmCustomerController {
230 List<CrmCustomerDO> list = customerService.getCustomerPage(pageVO, getLoginUserId()).getList(); 267 List<CrmCustomerDO> list = customerService.getCustomerPage(pageVO, getLoginUserId()).getList();
231 // 导出 Excel 268 // 导出 Excel
232 ExcelUtils.write(response, "客户.xls", "数据", CrmCustomerRespVO.class, 269 ExcelUtils.write(response, "客户.xls", "数据", CrmCustomerRespVO.class,
233 - BeanUtils.toBean(list, CrmCustomerRespVO.class)); 270 + buildCustomerDetailList(list));
234 } 271 }
235 272
236 @GetMapping("/get-import-template") 273 @GetMapping("/get-import-template")
@@ -238,12 +275,12 @@ public class CrmCustomerController { @@ -238,12 +275,12 @@ public class CrmCustomerController {
238 public void importTemplate(HttpServletResponse response) throws IOException { 275 public void importTemplate(HttpServletResponse response) throws IOException {
239 // 手动创建导出 demo 276 // 手动创建导出 demo
240 List<CrmCustomerImportExcelVO> list = Arrays.asList( 277 List<CrmCustomerImportExcelVO> list = Arrays.asList(
241 - CrmCustomerImportExcelVO.builder().name("芋道").industryId(1).level(1).source(1).mobile("15601691300").telephone("")  
242 - .qq("").wechat("").email("yunai@iocoder.cn").description("").remark("")  
243 - .areaId(null).detailAddress("").build(),  
244 - CrmCustomerImportExcelVO.builder().name("源码").industryId(1).level(1).source(1).mobile("15601691300").telephone("")  
245 - .qq("").wechat("").email("yunai@iocoder.cn").description("").remark("")  
246 - .areaId(null).detailAddress("").build() 278 + CrmCustomerImportExcelVO.builder().name("芋道").industryId(1).level(1).source(1)
  279 + .mobile("15601691300").telephone("").qq("").wechat("").email("yunai@iocoder.cn")
  280 + .areaId(null).detailAddress("").remark("").build(),
  281 + CrmCustomerImportExcelVO.builder().name("源码").industryId(1).level(1).source(1)
  282 + .mobile("15601691300").telephone("").qq("").wechat("").email("yunai@iocoder.cn")
  283 + .areaId(null).detailAddress("").remark("").build()
247 ); 284 );
248 // 输出 285 // 输出
249 ExcelUtils.write(response, "客户导入模板.xls", "客户列表", CrmCustomerImportExcelVO.class, list); 286 ExcelUtils.write(response, "客户导入模板.xls", "客户列表", CrmCustomerImportExcelVO.class, list);
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerDistributeReqVO.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerDistributeReqVO.java
1 -package cn.iocoder.yudao.module.crm.controller.admin.customer.vo; 1 +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer;
2 2
3 import io.swagger.v3.oas.annotations.media.Schema; 3 import io.swagger.v3.oas.annotations.media.Schema;
4 import jakarta.validation.constraints.NotEmpty; 4 import jakarta.validation.constraints.NotEmpty;
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerImportExcelVO.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportExcelVO.java
1 -package cn.iocoder.yudao.module.crm.controller.admin.customer.vo; 1 +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer;
2 2
3 import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; 3 import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
4 import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; 4 import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
@@ -24,19 +24,6 @@ public class CrmCustomerImportExcelVO { @@ -24,19 +24,6 @@ public class CrmCustomerImportExcelVO {
24 @ExcelProperty("客户名称") 24 @ExcelProperty("客户名称")
25 private String name; 25 private String name;
26 26
27 - // TODO @puhui999:industryId、level、source 字段,可以研究下怎么搞下拉框  
28 - @ExcelProperty(value = "所属行业", converter = DictConvert.class)  
29 - @DictFormat(CRM_CUSTOMER_INDUSTRY)  
30 - private Integer industryId;  
31 -  
32 - @ExcelProperty(value = "客户等级", converter = DictConvert.class)  
33 - @DictFormat(CRM_CUSTOMER_LEVEL)  
34 - private Integer level;  
35 -  
36 - @ExcelProperty(value = "客户来源", converter = DictConvert.class)  
37 - @DictFormat(CRM_CUSTOMER_SOURCE)  
38 - private Integer source;  
39 -  
40 @ExcelProperty("手机") 27 @ExcelProperty("手机")
41 private String mobile; 28 private String mobile;
42 29
@@ -52,12 +39,6 @@ public class CrmCustomerImportExcelVO { @@ -52,12 +39,6 @@ public class CrmCustomerImportExcelVO {
52 @ExcelProperty("邮箱") 39 @ExcelProperty("邮箱")
53 private String email; 40 private String email;
54 41
55 - @ExcelProperty("客户描述")  
56 - private String description;  
57 -  
58 - @ExcelProperty("备注")  
59 - private String remark;  
60 -  
61 // TODO @puhui999:需要选择省市区,需要研究下,怎么搞合理点; 42 // TODO @puhui999:需要选择省市区,需要研究下,怎么搞合理点;
62 @ExcelProperty("地区编号") 43 @ExcelProperty("地区编号")
63 private Integer areaId; 44 private Integer areaId;
@@ -65,4 +46,20 @@ public class CrmCustomerImportExcelVO { @@ -65,4 +46,20 @@ public class CrmCustomerImportExcelVO {
65 @ExcelProperty("详细地址") 46 @ExcelProperty("详细地址")
66 private String detailAddress; 47 private String detailAddress;
67 48
  49 + // TODO @puhui999:industryId、level、source 字段,可以研究下怎么搞下拉框
  50 + @ExcelProperty(value = "所属行业", converter = DictConvert.class)
  51 + @DictFormat(CRM_CUSTOMER_INDUSTRY)
  52 + private Integer industryId;
  53 +
  54 + @ExcelProperty(value = "客户等级", converter = DictConvert.class)
  55 + @DictFormat(CRM_CUSTOMER_LEVEL)
  56 + private Integer level;
  57 +
  58 + @ExcelProperty(value = "客户来源", converter = DictConvert.class)
  59 + @DictFormat(CRM_CUSTOMER_SOURCE)
  60 + private Integer source;
  61 +
  62 + @ExcelProperty("备注")
  63 + private String remark;
  64 +
68 } 65 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerImportReqVO.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportReqVO.java
1 -package cn.iocoder.yudao.module.crm.controller.admin.customer.vo; 1 +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer;
2 2
3 import io.swagger.v3.oas.annotations.media.Schema; 3 import io.swagger.v3.oas.annotations.media.Schema;
4 import jakarta.validation.constraints.NotNull; 4 import jakarta.validation.constraints.NotNull;
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerImportRespVO.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerImportRespVO.java
1 -package cn.iocoder.yudao.module.crm.controller.admin.customer.vo; 1 +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer;
2 2
3 import io.swagger.v3.oas.annotations.media.Schema; 3 import io.swagger.v3.oas.annotations.media.Schema;
4 import lombok.Builder; 4 import lombok.Builder;
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerLockReqVO.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerLockReqVO.java
1 -package cn.iocoder.yudao.module.crm.controller.admin.customer.vo; 1 +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer;
2 2
3 import io.swagger.v3.oas.annotations.media.Schema; 3 import io.swagger.v3.oas.annotations.media.Schema;
4 import lombok.Data; 4 import lombok.Data;
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerPageReqVO.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerPageReqVO.java
1 -package cn.iocoder.yudao.module.crm.controller.admin.customer.vo; 1 +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageParam; 3 import cn.iocoder.yudao.framework.common.pojo.PageParam;
4 import cn.iocoder.yudao.framework.common.validation.InEnum; 4 import cn.iocoder.yudao.framework.common.validation.InEnum;
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerRespVO.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerRespVO.java
1 -package cn.iocoder.yudao.module.crm.controller.admin.customer.vo; 1 +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer;
2 2
3 import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; 3 import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
4 import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; 4 import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
@@ -7,12 +7,9 @@ import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; @@ -7,12 +7,9 @@ import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
7 import com.alibaba.excel.annotation.ExcelProperty; 7 import com.alibaba.excel.annotation.ExcelProperty;
8 import io.swagger.v3.oas.annotations.media.Schema; 8 import io.swagger.v3.oas.annotations.media.Schema;
9 import lombok.Data; 9 import lombok.Data;
10 -import org.springframework.format.annotation.DateTimeFormat;  
11 10
12 import java.time.LocalDateTime; 11 import java.time.LocalDateTime;
13 12
14 -import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;  
15 -  
16 @Schema(description = "管理后台 - CRM 客户 Response VO") 13 @Schema(description = "管理后台 - CRM 客户 Response VO")
17 @Data 14 @Data
18 @ExcelIgnoreUnannotated 15 @ExcelIgnoreUnannotated
@@ -31,6 +28,28 @@ public class CrmCustomerRespVO { @@ -31,6 +28,28 @@ public class CrmCustomerRespVO {
31 @DictFormat(DictTypeConstants.BOOLEAN_STRING) 28 @DictFormat(DictTypeConstants.BOOLEAN_STRING)
32 private Boolean followUpStatus; 29 private Boolean followUpStatus;
33 30
  31 + @Schema(description = "最后跟进时间")
  32 + @ExcelProperty("最后跟进时间")
  33 + private LocalDateTime contactLastTime;
  34 +
  35 + @Schema(description = "最后跟进内容", example = "吃饭、睡觉、打逗逗")
  36 + @ExcelProperty("最后跟进内容")
  37 + private String contactLastContent;
  38 +
  39 + @Schema(description = "下次联系时间")
  40 + @ExcelProperty("下次联系时间")
  41 + private LocalDateTime contactNextTime;
  42 +
  43 + @Schema(description = "负责人的用户编号", example = "25682")
  44 + @ExcelProperty("负责人的用户编号")
  45 + private Long ownerUserId;
  46 + @Schema(description = "负责人名字", example = "25682")
  47 + @ExcelProperty("负责人名字")
  48 + private String ownerUserName;
  49 + @Schema(description = "负责人部门")
  50 + @ExcelProperty("负责人部门")
  51 + private String ownerUserDeptName;
  52 +
34 @Schema(description = "锁定状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") 53 @Schema(description = "锁定状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")
35 @ExcelProperty(value = "锁定状态", converter = DictConvert.class) 54 @ExcelProperty(value = "锁定状态", converter = DictConvert.class)
36 @DictFormat(DictTypeConstants.BOOLEAN_STRING) 55 @DictFormat(DictTypeConstants.BOOLEAN_STRING)
@@ -41,55 +60,26 @@ public class CrmCustomerRespVO { @@ -41,55 +60,26 @@ public class CrmCustomerRespVO {
41 @DictFormat(DictTypeConstants.BOOLEAN_STRING) 60 @DictFormat(DictTypeConstants.BOOLEAN_STRING)
42 private Boolean dealStatus; 61 private Boolean dealStatus;
43 62
44 - @Schema(description = "所属行业", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")  
45 - @ExcelProperty(value = "所属行业", converter = DictConvert.class)  
46 - @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY)  
47 - private Integer industryId;  
48 -  
49 - @Schema(description = "客户等级", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")  
50 - @ExcelProperty(value = "客户等级", converter = DictConvert.class)  
51 - @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_LEVEL)  
52 - private Integer level;  
53 -  
54 - @Schema(description = "客户来源", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")  
55 - @ExcelProperty(value = "客户来源", converter = DictConvert.class)  
56 - @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_SOURCE)  
57 - private Integer source;  
58 -  
59 - @Schema(description = "负责人的用户编号", example = "25682") 63 + @Schema(description = "手机", example = "25682")
60 @ExcelProperty("手机") 64 @ExcelProperty("手机")
61 private String mobile; 65 private String mobile;
62 66
63 - @Schema(description = "负责人的用户编号", example = "25682") 67 + @Schema(description = "电话", example = "25682")
64 @ExcelProperty("电话") 68 @ExcelProperty("电话")
65 private String telephone; 69 private String telephone;
66 70
67 - @Schema(description = "负责人的用户编号", example = "25682") 71 + @Schema(description = "QQ", example = "25682")
68 @ExcelProperty("QQ") 72 @ExcelProperty("QQ")
69 private String qq; 73 private String qq;
70 74
71 - @Schema(description = "负责人的用户编号", example = "25682") 75 + @Schema(description = "wechat", example = "25682")
72 @ExcelProperty("wechat") 76 @ExcelProperty("wechat")
73 private String wechat; 77 private String wechat;
74 78
75 - @Schema(description = "负责人的用户编号", example = "25682") 79 + @Schema(description = "email", example = "25682")
76 @ExcelProperty("email") 80 @ExcelProperty("email")
77 private String email; 81 private String email;
78 82
79 - @Schema(description = "负责人的用户编号", example = "25682")  
80 - @ExcelProperty("备注")  
81 - private String remark;  
82 -  
83 - @Schema(description = "负责人的用户编号", example = "25682")  
84 - @ExcelProperty("负责人的用户编号")  
85 - private Long ownerUserId;  
86 - @Schema(description = "负责人名字", example = "25682")  
87 - @ExcelProperty("负责人名字")  
88 - private String ownerUserName;  
89 - @Schema(description = "负责人部门")  
90 - @ExcelProperty("负责人部门")  
91 - private String ownerUserDeptName;  
92 -  
93 @Schema(description = "地区编号", example = "1024") 83 @Schema(description = "地区编号", example = "1024")
94 @ExcelProperty("地区编号") 84 @ExcelProperty("地区编号")
95 private Integer areaId; 85 private Integer areaId;
@@ -100,14 +90,24 @@ public class CrmCustomerRespVO { @@ -100,14 +90,24 @@ public class CrmCustomerRespVO {
100 @ExcelProperty("详细地址") 90 @ExcelProperty("详细地址")
101 private String detailAddress; 91 private String detailAddress;
102 92
103 - @Schema(description = "最后跟进时间")  
104 - @ExcelProperty("最后跟进时间")  
105 - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)  
106 - private LocalDateTime contactLastTime; 93 + @Schema(description = "所属行业", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")
  94 + @ExcelProperty(value = "所属行业", converter = DictConvert.class)
  95 + @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_INDUSTRY)
  96 + private Integer industryId;
107 97
108 - @Schema(description = "下次联系时间")  
109 - @ExcelProperty("下次联系时间")  
110 - private LocalDateTime contactNextTime; 98 + @Schema(description = "客户等级", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")
  99 + @ExcelProperty(value = "客户等级", converter = DictConvert.class)
  100 + @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_LEVEL)
  101 + private Integer level;
  102 +
  103 + @Schema(description = "客户来源", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")
  104 + @ExcelProperty(value = "客户来源", converter = DictConvert.class)
  105 + @DictFormat(cn.iocoder.yudao.module.crm.enums.DictTypeConstants.CRM_CUSTOMER_SOURCE)
  106 + private Integer source;
  107 +
  108 + @Schema(description = "负责人的用户编号", example = "25682")
  109 + @ExcelProperty("备注")
  110 + private String remark;
111 111
112 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) 112 @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
113 @ExcelProperty("创建时间") 113 @ExcelProperty("创建时间")
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerSaveReqVO.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerSaveReqVO.java
1 -package cn.iocoder.yudao.module.crm.controller.admin.customer.vo; 1 +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer;
2 2
3 import cn.iocoder.yudao.framework.common.validation.InEnum; 3 import cn.iocoder.yudao.framework.common.validation.InEnum;
4 import cn.iocoder.yudao.framework.common.validation.Mobile; 4 import cn.iocoder.yudao.framework.common.validation.Mobile;
@@ -13,6 +13,7 @@ import com.mzt.logapi.starter.annotation.DiffLogField; @@ -13,6 +13,7 @@ import com.mzt.logapi.starter.annotation.DiffLogField;
13 import io.swagger.v3.oas.annotations.media.Schema; 13 import io.swagger.v3.oas.annotations.media.Schema;
14 import jakarta.validation.constraints.Email; 14 import jakarta.validation.constraints.Email;
15 import jakarta.validation.constraints.NotEmpty; 15 import jakarta.validation.constraints.NotEmpty;
  16 +import jakarta.validation.constraints.NotNull;
16 import jakarta.validation.constraints.Size; 17 import jakarta.validation.constraints.Size;
17 import lombok.Data; 18 import lombok.Data;
18 import org.springframework.format.annotation.DateTimeFormat; 19 import org.springframework.format.annotation.DateTimeFormat;
@@ -34,19 +35,14 @@ public class CrmCustomerSaveReqVO { @@ -34,19 +35,14 @@ public class CrmCustomerSaveReqVO {
34 @NotEmpty(message = "客户名称不能为空") 35 @NotEmpty(message = "客户名称不能为空")
35 private String name; 36 private String name;
36 37
37 - @Schema(description = "所属行业", example = "1")  
38 - @DiffLogField(name = "所属行业", function = CrmCustomerIndustryParseFunction.NAME)  
39 - @DictFormat(CRM_CUSTOMER_INDUSTRY)  
40 - private Integer industryId;  
41 -  
42 - @Schema(description = "客户等级", example = "2")  
43 - @DiffLogField(name = "客户等级", function = CrmCustomerLevelParseFunction.NAME)  
44 - @InEnum(CrmCustomerLevelEnum.class)  
45 - private Integer level; 38 + @Schema(description = "下次联系时间")
  39 + @DiffLogField(name = "下次联系时间")
  40 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  41 + private LocalDateTime contactNextTime;
46 42
47 - @Schema(description = "客户来源", example = "3")  
48 - @DiffLogField(name = "客户来源", function = CrmCustomerSourceParseFunction.NAME)  
49 - private Integer source; 43 + @Schema(description = "负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")
  44 + @NotNull(message = "负责人的用户编号不能为空")
  45 + private Long ownerUserId;
50 46
51 @Schema(description = "手机", example = "18000000000") 47 @Schema(description = "手机", example = "18000000000")
52 @DiffLogField(name = "手机") 48 @DiffLogField(name = "手机")
@@ -74,15 +70,6 @@ public class CrmCustomerSaveReqVO { @@ -74,15 +70,6 @@ public class CrmCustomerSaveReqVO {
74 @Size(max = 255, message = "邮箱长度不能超过 255 个字符") 70 @Size(max = 255, message = "邮箱长度不能超过 255 个字符")
75 private String email; 71 private String email;
76 72
77 - @Schema(description = "客户描述", example = "任意文字")  
78 - @DiffLogField(name = "客户描述")  
79 - @Size(max = 4096, message = "客户描述长度不能超过 4096 个字符")  
80 - private String description;  
81 -  
82 - @Schema(description = "备注", example = "随便")  
83 - @DiffLogField(name = "备注")  
84 - private String remark;  
85 -  
86 @Schema(description = "地区编号", example = "20158") 73 @Schema(description = "地区编号", example = "20158")
87 @DiffLogField(name = "地区编号", function = SysAreaParseFunction.NAME) 74 @DiffLogField(name = "地区编号", function = SysAreaParseFunction.NAME)
88 private Integer areaId; 75 private Integer areaId;
@@ -91,12 +78,22 @@ public class CrmCustomerSaveReqVO { @@ -91,12 +78,22 @@ public class CrmCustomerSaveReqVO {
91 @DiffLogField(name = "详细地址") 78 @DiffLogField(name = "详细地址")
92 private String detailAddress; 79 private String detailAddress;
93 80
94 - @Schema(description = "下次联系时间")  
95 - @DiffLogField(name = "下次联系时间")  
96 - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)  
97 - private LocalDateTime contactNextTime; 81 + @Schema(description = "所属行业", example = "1")
  82 + @DiffLogField(name = "所属行业", function = CrmCustomerIndustryParseFunction.NAME)
  83 + @DictFormat(CRM_CUSTOMER_INDUSTRY)
  84 + private Integer industryId;
98 85
99 - @Schema(description = "负责人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563")  
100 - private Long ownerUserId; 86 + @Schema(description = "客户等级", example = "2")
  87 + @DiffLogField(name = "客户等级", function = CrmCustomerLevelParseFunction.NAME)
  88 + @InEnum(CrmCustomerLevelEnum.class)
  89 + private Integer level;
  90 +
  91 + @Schema(description = "客户来源", example = "3")
  92 + @DiffLogField(name = "客户来源", function = CrmCustomerSourceParseFunction.NAME)
  93 + private Integer source;
  94 +
  95 + @Schema(description = "备注", example = "随便")
  96 + @DiffLogField(name = "备注")
  97 + private String remark;
101 98
102 } 99 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/CrmCustomerTransferReqVO.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/customer/vo/customer/CrmCustomerTransferReqVO.java
1 -package cn.iocoder.yudao.module.crm.controller.admin.customer.vo; 1 +package cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer;
2 2
3 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum; 3 import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
4 import io.swagger.v3.oas.annotations.media.Schema; 4 import io.swagger.v3.oas.annotations.media.Schema;
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/convert/customer/CrmCustomerConvert.java deleted
1 -package cn.iocoder.yudao.module.crm.convert.customer;  
2 -  
3 -import cn.iocoder.yudao.framework.common.pojo.PageResult;  
4 -import cn.iocoder.yudao.framework.common.util.object.BeanUtils;  
5 -import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;  
6 -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerRespVO;  
7 -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerTransferReqVO;  
8 -import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;  
9 -import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;  
10 -import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;  
11 -import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;  
12 -import org.mapstruct.Mapper;  
13 -import org.mapstruct.Mapping;  
14 -import org.mapstruct.factory.Mappers;  
15 -  
16 -import java.util.Map;  
17 -  
18 -import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;  
19 -  
20 -/**  
21 - * 客户 Convert  
22 - *  
23 - * @author Wanwan  
24 - */  
25 -@Mapper  
26 -public interface CrmCustomerConvert {  
27 -  
28 - CrmCustomerConvert INSTANCE = Mappers.getMapper(CrmCustomerConvert.class);  
29 -  
30 - default CrmCustomerRespVO convert(CrmCustomerDO customer, Map<Long, AdminUserRespDTO> userMap,  
31 - Map<Long, DeptRespDTO> deptMap) {  
32 - CrmCustomerRespVO customerResp = BeanUtils.toBean(customer, CrmCustomerRespVO.class);  
33 - setUserInfo(customerResp, userMap, deptMap);  
34 - return customerResp;  
35 - }  
36 -  
37 - default PageResult<CrmCustomerRespVO> convertPage(PageResult<CrmCustomerDO> pageResult, Map<Long, AdminUserRespDTO> userMap,  
38 - Map<Long, DeptRespDTO> deptMap, Map<Long, Long> poolDayMap) {  
39 - PageResult<CrmCustomerRespVO> result = BeanUtils.toBean(pageResult, CrmCustomerRespVO.class);  
40 - result.getList().forEach(item -> {  
41 - setUserInfo(item, userMap, deptMap);  
42 - findAndThen(poolDayMap, item.getId(), item::setPoolDay);  
43 - });  
44 - return result;  
45 - }  
46 -  
47 - /**  
48 - * 设置用户信息  
49 - *  
50 - * @param customer CRM 客户 Response VO  
51 - * @param userMap 用户信息 map  
52 - * @param deptMap 用户部门信息 map  
53 - */  
54 - static void setUserInfo(CrmCustomerRespVO customer, Map<Long, AdminUserRespDTO> userMap, Map<Long, DeptRespDTO> deptMap) {  
55 - customer.setAreaName(AreaUtils.format(customer.getAreaId()));  
56 - findAndThen(userMap, customer.getOwnerUserId(), user -> {  
57 - customer.setOwnerUserName(user.getNickname());  
58 - findAndThen(deptMap, user.getDeptId(), dept -> customer.setOwnerUserDeptName(dept.getName()));  
59 - });  
60 - findAndThen(userMap, Long.parseLong(customer.getCreator()), user -> customer.setCreatorName(user.getNickname()));  
61 - }  
62 -  
63 - @Mapping(target = "bizId", source = "reqVO.id")  
64 - CrmPermissionTransferReqBO convert(CrmCustomerTransferReqVO reqVO, Long userId);  
65 -  
66 -}  
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/dataobject/customer/CrmCustomerDO.java
@@ -57,10 +57,6 @@ public class CrmCustomerDO extends BaseDO { @@ -57,10 +57,6 @@ public class CrmCustomerDO extends BaseDO {
57 * 关联 AdminUserDO 的 id 字段 57 * 关联 AdminUserDO 的 id 字段
58 */ 58 */
59 private Long ownerUserId; 59 private Long ownerUserId;
60 - /**  
61 - * 最后接收时间  
62 - */  
63 - private LocalDateTime receiveTime;  
64 60
65 /** 61 /**
66 * 锁定状态 62 * 锁定状态
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerMapper.java
@@ -6,7 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -6,7 +6,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; 6 import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
7 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; 7 import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
8 import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; 8 import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
9 -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerPageReqVO; 9 +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer.CrmCustomerPageReqVO;
10 import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO; 10 import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;
11 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 11 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
12 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO; 12 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/clue/CrmClueServiceImpl.java
@@ -8,7 +8,7 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils; @@ -8,7 +8,7 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
8 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO; 8 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmCluePageReqVO;
9 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueSaveReqVO; 9 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueSaveReqVO;
10 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransferReqVO; 10 import cn.iocoder.yudao.module.crm.controller.admin.clue.vo.CrmClueTransferReqVO;
11 -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.CrmCustomerSaveReqVO; 11 +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer.CrmCustomerSaveReqVO;
12 import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO; 12 import cn.iocoder.yudao.module.crm.dal.dataobject.clue.CrmClueDO;
13 import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO; 13 import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;
14 import cn.iocoder.yudao.module.crm.dal.mysql.clue.CrmClueMapper; 14 import cn.iocoder.yudao.module.crm.dal.mysql.clue.CrmClueMapper;
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java
1 package cn.iocoder.yudao.module.crm.service.customer; 1 package cn.iocoder.yudao.module.crm.service.customer;
2 2
3 import cn.iocoder.yudao.framework.common.pojo.PageResult; 3 import cn.iocoder.yudao.framework.common.pojo.PageResult;
4 -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.*; 4 +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer.*;
5 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 5 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
6 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO; 6 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;
7 import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO; 7 import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO;
@@ -38,6 +38,14 @@ public interface CrmCustomerService { @@ -38,6 +38,14 @@ public interface CrmCustomerService {
38 void updateCustomer(@Valid CrmCustomerSaveReqVO updateReqVO); 38 void updateCustomer(@Valid CrmCustomerSaveReqVO updateReqVO);
39 39
40 /** 40 /**
  41 + * 更新客户的跟进状态
  42 + *
  43 + * @param id 编号
  44 + * @param dealStatus 跟进状态
  45 + */
  46 + void updateCustomerDealStatus(Long id, Boolean dealStatus);
  47 +
  48 + /**
41 * 删除客户 49 * 删除客户
42 * 50 *
43 * @param id 编号 51 * @param id 编号
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java
@@ -10,8 +10,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; @@ -10,8 +10,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
10 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 10 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
11 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; 11 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
12 import cn.iocoder.yudao.framework.common.util.object.BeanUtils; 12 import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
13 -import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.*;  
14 -import cn.iocoder.yudao.module.crm.convert.customer.CrmCustomerConvert; 13 +import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.customer.*;
15 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; 14 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
16 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO; 15 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO;
17 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO; 16 import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerPoolConfigDO;
@@ -27,6 +26,7 @@ import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO; @@ -27,6 +26,7 @@ import cn.iocoder.yudao.module.crm.service.customer.bo.CrmCustomerCreateReqBO;
27 import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO; 26 import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
28 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService; 27 import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
29 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO; 28 import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
  29 +import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
30 import cn.iocoder.yudao.module.system.api.user.AdminUserApi; 30 import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
31 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; 31 import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
32 import com.mzt.logapi.context.LogRecordContext; 32 import com.mzt.logapi.context.LogRecordContext;
@@ -138,6 +138,26 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -138,6 +138,26 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
138 } 138 }
139 139
140 @Override 140 @Override
  141 + @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_UPDATE_DEAL_STATUS_SUB_TYPE, bizNo = "{{#id}}",
  142 + success = CRM_CUSTOMER_UPDATE_DEAL_STATUS_SUCCESS)
  143 + @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#id", level = CrmPermissionLevelEnum.WRITE)
  144 + public void updateCustomerDealStatus(Long id, Boolean dealStatus) {
  145 + // 1.1 校验存在
  146 + CrmCustomerDO customer = validateCustomerExists(id);
  147 + // 1.2 校验是否重复操作
  148 + if (Objects.equals(customer.getDealStatus(), dealStatus)) {
  149 + throw exception(CUSTOMER_UPDATE_DEAL_STATUS_FAIL);
  150 + }
  151 +
  152 + // 2. 更新客户的成交状态
  153 + customerMapper.updateById(new CrmCustomerDO().setId(id).setDealStatus(dealStatus));
  154 +
  155 + // 3. 记录操作日志上下文
  156 + LogRecordContext.putVariable("customerName", customer.getName());
  157 + LogRecordContext.putVariable("dealStatus", dealStatus);
  158 + }
  159 +
  160 + @Override
141 @Transactional(rollbackFor = Exception.class) 161 @Transactional(rollbackFor = Exception.class)
142 @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_DELETE_SUB_TYPE, bizNo = "{{#id}}", 162 @LogRecord(type = CRM_CUSTOMER_TYPE, subType = CRM_CUSTOMER_DELETE_SUB_TYPE, bizNo = "{{#id}}",
143 success = CRM_CUSTOMER_DELETE_SUCCESS) 163 success = CRM_CUSTOMER_DELETE_SUCCESS)
@@ -146,7 +166,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -146,7 +166,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
146 // 1.1 校验存在 166 // 1.1 校验存在
147 CrmCustomerDO customer = validateCustomerExists(id); 167 CrmCustomerDO customer = validateCustomerExists(id);
148 // 1.2 检查引用 168 // 1.2 检查引用
149 - checkCustomerReference(id); 169 + validateCustomerReference(id);
150 170
151 // 2. 删除 171 // 2. 删除
152 customerMapper.deleteById(id); 172 customerMapper.deleteById(id);
@@ -162,7 +182,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -162,7 +182,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
162 * 182 *
163 * @param id 客户编号 183 * @param id 客户编号
164 */ 184 */
165 - private void checkCustomerReference(Long id) { 185 + private void validateCustomerReference(Long id) {
166 if (contactService.getContactCountByCustomerId(id) > 0) { 186 if (contactService.getContactCountByCustomerId(id) > 0) {
167 throw exception(CUSTOMER_DELETE_FAIL_HAVE_REFERENCE, CrmBizTypeEnum.CRM_CONTACT.getName()); 187 throw exception(CUSTOMER_DELETE_FAIL_HAVE_REFERENCE, CrmBizTypeEnum.CRM_CONTACT.getName());
168 } 188 }
@@ -186,8 +206,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -186,8 +206,8 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
186 validateCustomerExceedOwnerLimit(reqVO.getNewOwnerUserId(), 1); 206 validateCustomerExceedOwnerLimit(reqVO.getNewOwnerUserId(), 1);
187 207
188 // 2.1 数据权限转移 208 // 2.1 数据权限转移
189 - permissionService.transferPermission(  
190 - CrmCustomerConvert.INSTANCE.convert(reqVO, userId).setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType())); 209 + permissionService.transferPermission(new CrmPermissionTransferReqBO(userId, CrmBizTypeEnum.CRM_CUSTOMER.getType(),
  210 + reqVO.getId(), reqVO.getNewOwnerUserId(), reqVO.getOldOwnerPermissionLevel()));
191 // 2.2 转移后重新设置负责人 211 // 2.2 转移后重新设置负责人
192 customerMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId()); 212 customerMapper.updateOwnerUserIdById(reqVO.getId(), reqVO.getNewOwnerUserId());
193 213
@@ -230,8 +250,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService { @@ -230,8 +250,7 @@ public class CrmCustomerServiceImpl implements CrmCustomerService {
230 success = CRM_CUSTOMER_CREATE_SUCCESS) 250 success = CRM_CUSTOMER_CREATE_SUCCESS)
231 public Long createCustomer(CrmCustomerCreateReqBO customerCreateReq, Long userId) { 251 public Long createCustomer(CrmCustomerCreateReqBO customerCreateReq, Long userId) {
232 // 1. 插入客户 252 // 1. 插入客户
233 - CrmCustomerDO customer = BeanUtils.toBean(customerCreateReq, CrmCustomerDO.class).setOwnerUserId(userId)  
234 - .setLockStatus(false).setDealStatus(false).setReceiveTime(LocalDateTime.now()); 253 + CrmCustomerDO customer = BeanUtils.toBean(customerCreateReq, CrmCustomerDO.class).setOwnerUserId(userId);
235 customerMapper.insert(customer); 254 customerMapper.insert(customer);
236 255
237 // 2. 创建数据权限 256 // 2. 创建数据权限