Commit 924580f4a29db98d7add392e516be8505d0ed61b

Authored by YunaiV
1 parent 3b379d8c

CRM:重构【客户画像】,从【客户统计】拆分出去先~

Showing 16 changed files with 400 additions and 286 deletions
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java
@@ -2,10 +2,6 @@ package cn.iocoder.yudao.module.crm.controller.admin.statistics; @@ -2,10 +2,6 @@ package cn.iocoder.yudao.module.crm.controller.admin.statistics;
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.module.crm.controller.admin.statistics.vo.customer.*; 4 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*;
5 -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerAreaRespVO;  
6 -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerIndustryRespVO;  
7 -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerLevelRespVO;  
8 -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerSourceRespVO;  
9 import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsCustomerService; 5 import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsCustomerService;
10 import io.swagger.v3.oas.annotations.Operation; 6 import io.swagger.v3.oas.annotations.Operation;
11 import io.swagger.v3.oas.annotations.tags.Tag; 7 import io.swagger.v3.oas.annotations.tags.Tag;
@@ -90,32 +86,4 @@ public class CrmStatisticsCustomerController { @@ -90,32 +86,4 @@ public class CrmStatisticsCustomerController {
90 86
91 // TODO dhb52:【成交周期分析】里,有按照员工(已实现)、地区(未实现)、产品(未实现),需要在看看哈;可以把 CustomerDealCycle 拆成 3 个 tab,员工客户成交周期分析、地区客户成交周期分析、产品客户成交周期分析; 87 // TODO dhb52:【成交周期分析】里,有按照员工(已实现)、地区(未实现)、产品(未实现),需要在看看哈;可以把 CustomerDealCycle 拆成 3 个 tab,员工客户成交周期分析、地区客户成交周期分析、产品客户成交周期分析;
92 88
93 - @GetMapping("/get-customer-industry-summary")  
94 - @Operation(summary = "获取客户行业统计数据")  
95 - @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")  
96 - public CommonResult<List<CrmStatisticCustomerIndustryRespVO>> getCustomerIndustry(@Valid CrmStatisticsCustomerReqVO reqVO) {  
97 - return success(customerService.getCustomerIndustry(reqVO));  
98 - }  
99 -  
100 - @GetMapping("/get-customer-source-summary")  
101 - @Operation(summary = "获取客户来源统计数据")  
102 - @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")  
103 - public CommonResult<List<CrmStatisticCustomerSourceRespVO>> getCustomerSource(@Valid CrmStatisticsCustomerReqVO reqVO) {  
104 - return success(customerService.getCustomerSource(reqVO));  
105 - }  
106 -  
107 - @GetMapping("/get-customer-level-summary")  
108 - @Operation(summary = "获取客户级别统计数据")  
109 - @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")  
110 - public CommonResult<List<CrmStatisticCustomerLevelRespVO>> getCustomerLevel(@Valid CrmStatisticsCustomerReqVO reqVO) {  
111 - return success(customerService.getCustomerLevel(reqVO));  
112 - }  
113 -  
114 - @GetMapping("/get-customer-area-summary")  
115 - @Operation(summary = "获取客户地区统计数据")  
116 - @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')")  
117 - public CommonResult<List<CrmStatisticCustomerAreaRespVO>> getCustomerArea(@Valid CrmStatisticsCustomerReqVO reqVO) {  
118 - return success(customerService.getCustomerArea(reqVO));  
119 - }  
120 -  
121 } 89 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsPortraitController.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.controller.admin.statistics;
  2 +
  3 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  4 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO;
  5 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerAreaRespVO;
  6 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerIndustryRespVO;
  7 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerLevelRespVO;
  8 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerSourceRespVO;
  9 +import cn.iocoder.yudao.module.crm.service.statistics.CrmStatisticsPortraitService;
  10 +import io.swagger.v3.oas.annotations.Operation;
  11 +import io.swagger.v3.oas.annotations.tags.Tag;
  12 +import jakarta.annotation.Resource;
  13 +import jakarta.validation.Valid;
  14 +import org.springframework.security.access.prepost.PreAuthorize;
  15 +import org.springframework.validation.annotation.Validated;
  16 +import org.springframework.web.bind.annotation.GetMapping;
  17 +import org.springframework.web.bind.annotation.RequestMapping;
  18 +import org.springframework.web.bind.annotation.RestController;
  19 +
  20 +import java.util.List;
  21 +
  22 +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
  23 +
  24 +@Tag(name = "管理后台 - CRM 客户画像")
  25 +@RestController
  26 +@RequestMapping("/crm/statistics-portrait")
  27 +@Validated
  28 +public class CrmStatisticsPortraitController {
  29 +
  30 + @Resource
  31 + private CrmStatisticsPortraitService statisticsPortraitService;
  32 +
  33 + @GetMapping("/get-customer-area-summary")
  34 + @Operation(summary = "获取客户地区统计数据", description = "用于【城市分布分析】页面")
  35 + @PreAuthorize("@ss.hasPermission('crm:statistics-portrait:query')")
  36 + public CommonResult<List<CrmStatisticCustomerAreaRespVO>> getCustomerAreaSummary(@Valid CrmStatisticsCustomerReqVO reqVO) {
  37 + return success(statisticsPortraitService.getCustomerArea(reqVO));
  38 + }
  39 +
  40 + @GetMapping("/get-customer-industry-summary")
  41 + @Operation(summary = "获取客户行业统计数据")
  42 + @PreAuthorize("@ss.hasPermission('crm:statistics-portrait:query')")
  43 + public CommonResult<List<CrmStatisticCustomerIndustryRespVO>> getCustomerIndustry(@Valid CrmStatisticsCustomerReqVO reqVO) {
  44 + return success(statisticsPortraitService.getCustomerIndustry(reqVO));
  45 + }
  46 +
  47 + @GetMapping("/get-customer-level-summary")
  48 + @Operation(summary = "获取客户级别统计数据")
  49 + @PreAuthorize("@ss.hasPermission('crm:statistics-portrait:query')")
  50 + public CommonResult<List<CrmStatisticCustomerLevelRespVO>> getCustomerLevel(@Valid CrmStatisticsCustomerReqVO reqVO) {
  51 + return success(statisticsPortraitService.getCustomerLevel(reqVO));
  52 + }
  53 +
  54 + @GetMapping("/get-customer-source-summary")
  55 + @Operation(summary = "获取客户来源统计数据")
  56 + @PreAuthorize("@ss.hasPermission('crm:statistics-portrait:query')")
  57 + public CommonResult<List<CrmStatisticCustomerSourceRespVO>> getCustomerSource(@Valid CrmStatisticsCustomerReqVO reqVO) {
  58 + return success(statisticsPortraitService.getCustomerSource(reqVO));
  59 + }
  60 +
  61 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsRankController.java
@@ -18,7 +18,6 @@ import java.util.List; @@ -18,7 +18,6 @@ import java.util.List;
18 18
19 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; 19 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
20 20
21 -  
22 @Tag(name = "管理后台 - CRM 排行榜统计") 21 @Tag(name = "管理后台 - CRM 排行榜统计")
23 @RestController 22 @RestController
24 @RequestMapping("/crm/statistics-rank") 23 @RequestMapping("/crm/statistics-rank")
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerReqVO.java
@@ -45,12 +45,4 @@ public class CrmStatisticsCustomerReqVO { @@ -45,12 +45,4 @@ public class CrmStatisticsCustomerReqVO {
45 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) 45 @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
46 private LocalDateTime[] times; 46 private LocalDateTime[] times;
47 47
48 - /**  
49 - * group by DATE_FORMAT(field, #{dateFormat})  
50 - * 非前端传递, 由Service计算后传递给Mapper的参数  
51 - */  
52 - @Deprecated  
53 - @Schema(description = "Group By 日期格式", hidden = true, example = "%Y%m")  
54 - private String sqlDateFormat;  
55 -  
56 } 48 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/analyze/CrmStatisticCustomerAreaRespVO.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerAreaRespVO.java
1 -package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze; 1 +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait;
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/statistics/vo/customer/analyze/CrmStatisticCustomerIndustryRespVO.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerIndustryRespVO.java
1 -package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze; 1 +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait;
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/statistics/vo/customer/analyze/CrmStatisticCustomerLevelRespVO.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerLevelRespVO.java
1 -package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze; 1 +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait;
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/statistics/vo/customer/analyze/CrmStatisticCustomerSourceRespVO.java renamed to yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/portrait/CrmStatisticCustomerSourceRespVO.java
1 -package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze; 1 +package cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait;
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/dal/mysql/statistics/CrmStatisticsCustomerMapper.java
1 package cn.iocoder.yudao.module.crm.dal.mysql.statistics; 1 package cn.iocoder.yudao.module.crm.dal.mysql.statistics;
2 2
3 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; 3 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*;
4 -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerAreaRespVO;  
5 -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerIndustryRespVO;  
6 -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerLevelRespVO;  
7 -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerSourceRespVO;  
8 import org.apache.ibatis.annotations.Mapper; 4 import org.apache.ibatis.annotations.Mapper;
9 5
10 import java.util.List; 6 import java.util.List;
11 7
12 /** 8 /**
13 - * CRM 数据统计 员工客户分析 Mapper 9 + * CRM 数据统计 Mapper
14 * 10 *
15 * @author dhb52 11 * @author dhb52
16 */ 12 */
@@ -45,12 +41,4 @@ public interface CrmStatisticsCustomerMapper { @@ -45,12 +41,4 @@ public interface CrmStatisticsCustomerMapper {
45 41
46 List<CrmStatisticsCustomerDealCycleByUserRespVO> selectCustomerDealCycleGroupByUser(CrmStatisticsCustomerReqVO reqVO); 42 List<CrmStatisticsCustomerDealCycleByUserRespVO> selectCustomerDealCycleGroupByUser(CrmStatisticsCustomerReqVO reqVO);
47 43
48 - List<CrmStatisticCustomerIndustryRespVO> selectCustomerIndustryListGroupbyIndustryId(CrmStatisticsCustomerReqVO reqVO);  
49 -  
50 - List<CrmStatisticCustomerSourceRespVO> selectCustomerSourceListGroupbySource(CrmStatisticsCustomerReqVO reqVO);  
51 -  
52 - List<CrmStatisticCustomerLevelRespVO> selectCustomerLevelListGroupbyLevel(CrmStatisticsCustomerReqVO reqVO);  
53 -  
54 - List<CrmStatisticCustomerAreaRespVO> selectSummaryListByAreaId(CrmStatisticsCustomerReqVO reqVO);  
55 -  
56 } 44 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsPortraitMapper.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.dal.mysql.statistics;
  2 +
  3 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*;
  4 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerAreaRespVO;
  5 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerIndustryRespVO;
  6 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerLevelRespVO;
  7 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerSourceRespVO;
  8 +import org.apache.ibatis.annotations.Mapper;
  9 +
  10 +import java.util.List;
  11 +
  12 +/**
  13 + * CRM 数据画像 Mapper
  14 + *
  15 + * @author dhb52
  16 + */
  17 +@Mapper
  18 +public interface CrmStatisticsPortraitMapper {
  19 +
  20 + List<CrmStatisticCustomerIndustryRespVO> selectCustomerIndustryListGroupbyIndustryId(CrmStatisticsCustomerReqVO reqVO);
  21 +
  22 + List<CrmStatisticCustomerSourceRespVO> selectCustomerSourceListGroupbySource(CrmStatisticsCustomerReqVO reqVO);
  23 +
  24 + List<CrmStatisticCustomerLevelRespVO> selectCustomerLevelListGroupbyLevel(CrmStatisticsCustomerReqVO reqVO);
  25 +
  26 + List<CrmStatisticCustomerAreaRespVO> selectSummaryListByAreaId(CrmStatisticsCustomerReqVO reqVO);
  27 +
  28 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java
1 package cn.iocoder.yudao.module.crm.service.statistics; 1 package cn.iocoder.yudao.module.crm.service.statistics;
2 2
3 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; 3 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*;
4 -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerAreaRespVO;  
5 -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerIndustryRespVO;  
6 -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerLevelRespVO;  
7 -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerSourceRespVO;  
8 4
9 import java.util.List; 5 import java.util.List;
10 6
@@ -81,36 +77,4 @@ public interface CrmStatisticsCustomerService { @@ -81,36 +77,4 @@ public interface CrmStatisticsCustomerService {
81 */ 77 */
82 List<CrmStatisticsCustomerDealCycleByUserRespVO> getCustomerDealCycleByUser(CrmStatisticsCustomerReqVO reqVO); 78 List<CrmStatisticsCustomerDealCycleByUserRespVO> getCustomerDealCycleByUser(CrmStatisticsCustomerReqVO reqVO);
83 79
84 - /**  
85 - * 获取客户行业统计数据  
86 - *  
87 - * @param reqVO 请求参数  
88 - * @return 统计数据  
89 - */  
90 - List<CrmStatisticCustomerIndustryRespVO> getCustomerIndustry(CrmStatisticsCustomerReqVO reqVO);  
91 -  
92 - /**  
93 - * 获取客户来源统计数据  
94 - *  
95 - * @param reqVO 请求参数  
96 - * @return 统计数据  
97 - */  
98 - List<CrmStatisticCustomerSourceRespVO> getCustomerSource(CrmStatisticsCustomerReqVO reqVO);  
99 -  
100 - /**  
101 - * 获取客户级别统计数据  
102 - *  
103 - * @param reqVO 请求参数  
104 - * @return 统计数据  
105 - */  
106 - List<CrmStatisticCustomerLevelRespVO> getCustomerLevel(CrmStatisticsCustomerReqVO reqVO);  
107 -  
108 - /**  
109 - * 获取客户地区统计数据  
110 - *  
111 - * @param reqVO 请求参数  
112 - * @return 统计数据  
113 - */  
114 - List<CrmStatisticCustomerAreaRespVO> getCustomerArea(CrmStatisticsCustomerReqVO reqVO);  
115 -  
116 } 80 }
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java
@@ -4,14 +4,7 @@ import cn.hutool.core.collection.CollUtil; @@ -4,14 +4,7 @@ import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.util.ObjUtil; 4 import cn.hutool.core.util.ObjUtil;
5 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; 5 import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
6 import cn.iocoder.yudao.framework.common.util.number.NumberUtils; 6 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
7 -import cn.iocoder.yudao.framework.ip.core.Area;  
8 -import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum;  
9 -import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;  
10 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*; 7 import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.*;
11 -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerAreaRespVO;  
12 -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerIndustryRespVO;  
13 -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerLevelRespVO;  
14 -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerSourceRespVO;  
15 import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper; 8 import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper;
16 import cn.iocoder.yudao.module.system.api.dept.DeptApi; 9 import cn.iocoder.yudao.module.system.api.dept.DeptApi;
17 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; 10 import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
@@ -30,8 +23,6 @@ import java.util.stream.Stream; @@ -30,8 +23,6 @@ import java.util.stream.Stream;
30 23
31 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; 24 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
32 import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; 25 import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
33 -import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;  
34 -import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.*;  
35 26
36 /** 27 /**
37 * CRM 客户分析 Service 实现类 28 * CRM 客户分析 Service 实现类
@@ -245,104 +236,6 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe @@ -245,104 +236,6 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe
245 return summaryList; 236 return summaryList;
246 } 237 }
247 238
248 - @Override  
249 - public List<CrmStatisticCustomerIndustryRespVO> getCustomerIndustry(CrmStatisticsCustomerReqVO reqVO) {  
250 - // 1. 获得用户编号数组  
251 - List<Long> userIds = getUserIds(reqVO);  
252 - if (CollUtil.isEmpty(userIds)) {  
253 - return Collections.emptyList();  
254 - }  
255 - reqVO.setUserIds(userIds);  
256 - // 2. 获取客户行业统计数据  
257 - List<CrmStatisticCustomerIndustryRespVO> industryRespVOList = customerMapper.selectCustomerIndustryListGroupbyIndustryId(reqVO);  
258 - if (CollUtil.isEmpty(industryRespVOList)) {  
259 - return Collections.emptyList();  
260 - }  
261 -  
262 - return convertList(industryRespVOList, item -> {  
263 - if (ObjUtil.isNull(item.getIndustryId())) {  
264 - return item;  
265 - }  
266 - item.setIndustryName(dictDataApi.getDictDataLabel(CRM_CUSTOMER_INDUSTRY, item.getIndustryId()));  
267 - return item;  
268 - });  
269 - }  
270 -  
271 - @Override  
272 - public List<CrmStatisticCustomerSourceRespVO> getCustomerSource(CrmStatisticsCustomerReqVO reqVO) {  
273 - // 1. 获得用户编号数组  
274 - List<Long> userIds = getUserIds(reqVO);  
275 - if (CollUtil.isEmpty(userIds)) {  
276 - return Collections.emptyList();  
277 - }  
278 - reqVO.setUserIds(userIds);  
279 - // 2. 获取客户行业统计数据  
280 - List<CrmStatisticCustomerSourceRespVO> sourceRespVOList = customerMapper.selectCustomerSourceListGroupbySource(reqVO);  
281 - if (CollUtil.isEmpty(sourceRespVOList)) {  
282 - return Collections.emptyList();  
283 - }  
284 -  
285 - return convertList(sourceRespVOList, item -> {  
286 - if (ObjUtil.isNull(item.getSource())) {  
287 - return item;  
288 - }  
289 - item.setSourceName(dictDataApi.getDictDataLabel(CRM_CUSTOMER_SOURCE, item.getSource()));  
290 - return item;  
291 - });  
292 - }  
293 -  
294 - @Override  
295 - public List<CrmStatisticCustomerLevelRespVO> getCustomerLevel(CrmStatisticsCustomerReqVO reqVO) {  
296 - // 1. 获得用户编号数组  
297 - List<Long> userIds = getUserIds(reqVO);  
298 - if (CollUtil.isEmpty(userIds)) {  
299 - return Collections.emptyList();  
300 - }  
301 - reqVO.setUserIds(userIds);  
302 - // 2. 获取客户行业统计数据  
303 - List<CrmStatisticCustomerLevelRespVO> levelRespVOList = customerMapper.selectCustomerLevelListGroupbyLevel(reqVO);  
304 - if (CollUtil.isEmpty(levelRespVOList)) {  
305 - return Collections.emptyList();  
306 - }  
307 -  
308 - return convertList(levelRespVOList, item -> {  
309 - if (ObjUtil.isNull(item.getLevel())) {  
310 - return item;  
311 - }  
312 - item.setLevelName(dictDataApi.getDictDataLabel(CRM_CUSTOMER_LEVEL, item.getLevel()));  
313 - return item;  
314 - });  
315 - }  
316 -  
317 - @Override  
318 - public List<CrmStatisticCustomerAreaRespVO> getCustomerArea(CrmStatisticsCustomerReqVO reqVO) {  
319 - // 1. 获得用户编号数组  
320 - List<Long> userIds = getUserIds(reqVO);  
321 - if (CollUtil.isEmpty(userIds)) {  
322 - return Collections.emptyList();  
323 - }  
324 - reqVO.setUserIds(userIds);  
325 - // 2. 获取客户地区统计数据  
326 - List<CrmStatisticCustomerAreaRespVO> list = customerMapper.selectSummaryListByAreaId(reqVO);  
327 - if (CollUtil.isEmpty(list)) {  
328 - return Collections.emptyList();  
329 - }  
330 -  
331 - // 拼接数据  
332 - List<Area> areaList = AreaUtils.getByType(AreaTypeEnum.PROVINCE, area -> area);  
333 - areaList.add(new Area().setId(null).setName("未知"));  
334 - Map<Integer, Area> areaMap = convertMap(areaList, Area::getId);  
335 - List<CrmStatisticCustomerAreaRespVO> customerAreaRespVOList = convertList(list, item -> {  
336 - Integer parentId = AreaUtils.getParentIdByType(item.getAreaId(), AreaTypeEnum.PROVINCE);  
337 - if (parentId == null) {  
338 - return item;  
339 - }  
340 - findAndThen(areaMap, parentId, area -> item.setAreaId(parentId).setAreaName(area.getName()));  
341 - return item;  
342 - });  
343 - return customerAreaRespVOList;  
344 - }  
345 -  
346 /** 239 /**
347 * 拼接用户信息(昵称) 240 * 拼接用户信息(昵称)
348 * 241 *
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitService.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.service.statistics;
  2 +
  3 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO;
  4 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerAreaRespVO;
  5 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerIndustryRespVO;
  6 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerLevelRespVO;
  7 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerSourceRespVO;
  8 +
  9 +import java.util.List;
  10 +
  11 +/**
  12 + * CRM 客户画像 Service 接口
  13 + *
  14 + * @author HUIHUI
  15 + */
  16 +public interface CrmStatisticsPortraitService {
  17 +
  18 + /**
  19 + * 获取客户地区统计数据
  20 + *
  21 + * @param reqVO 请求参数
  22 + * @return 统计数据
  23 + */
  24 + List<CrmStatisticCustomerAreaRespVO> getCustomerArea(CrmStatisticsCustomerReqVO reqVO);
  25 +
  26 + /**
  27 + * 获取客户行业统计数据
  28 + *
  29 + * @param reqVO 请求参数
  30 + * @return 统计数据
  31 + */
  32 + List<CrmStatisticCustomerIndustryRespVO> getCustomerIndustry(CrmStatisticsCustomerReqVO reqVO);
  33 +
  34 + /**
  35 + * 获取客户级别统计数据
  36 + *
  37 + * @param reqVO 请求参数
  38 + * @return 统计数据
  39 + */
  40 + List<CrmStatisticCustomerLevelRespVO> getCustomerLevel(CrmStatisticsCustomerReqVO reqVO);
  41 +
  42 + /**
  43 + * 获取客户来源统计数据
  44 + *
  45 + * @param reqVO 请求参数
  46 + * @return 统计数据
  47 + */
  48 + List<CrmStatisticCustomerSourceRespVO> getCustomerSource(CrmStatisticsCustomerReqVO reqVO);
  49 +
  50 +}
yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsPortraitServiceImpl.java 0 → 100644
  1 +package cn.iocoder.yudao.module.crm.service.statistics;
  2 +
  3 +import cn.hutool.core.collection.CollUtil;
  4 +import cn.hutool.core.util.ObjUtil;
  5 +import cn.iocoder.yudao.framework.ip.core.Area;
  6 +import cn.iocoder.yudao.framework.ip.core.enums.AreaTypeEnum;
  7 +import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
  8 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO;
  9 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerAreaRespVO;
  10 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerIndustryRespVO;
  11 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerLevelRespVO;
  12 +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerSourceRespVO;
  13 +import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsPortraitMapper;
  14 +import cn.iocoder.yudao.module.system.api.dept.DeptApi;
  15 +import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
  16 +import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
  17 +import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
  18 +import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
  19 +import jakarta.annotation.Resource;
  20 +import org.springframework.stereotype.Service;
  21 +
  22 +import java.util.Collections;
  23 +import java.util.List;
  24 +import java.util.Map;
  25 +
  26 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
  27 +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
  28 +import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
  29 +import static cn.iocoder.yudao.module.crm.enums.DictTypeConstants.*;
  30 +
  31 +/**
  32 + * CRM 客户画像 Service 实现类
  33 + *
  34 + * @author HUIHUI
  35 + */
  36 +@Service
  37 +public class CrmStatisticsPortraitServiceImpl implements CrmStatisticsPortraitService {
  38 +
  39 + @Resource
  40 + private CrmStatisticsPortraitMapper portraitMapper;
  41 +
  42 + @Resource
  43 + private AdminUserApi adminUserApi;
  44 + @Resource
  45 + private DeptApi deptApi;
  46 + @Resource
  47 + private DictDataApi dictDataApi;
  48 +
  49 + @Override
  50 + public List<CrmStatisticCustomerIndustryRespVO> getCustomerIndustry(CrmStatisticsCustomerReqVO reqVO) {
  51 + // 1. 获得用户编号数组
  52 + List<Long> userIds = getUserIds(reqVO);
  53 + if (CollUtil.isEmpty(userIds)) {
  54 + return Collections.emptyList();
  55 + }
  56 + reqVO.setUserIds(userIds);
  57 + // 2. 获取客户行业统计数据
  58 + List<CrmStatisticCustomerIndustryRespVO> industryRespVOList = portraitMapper.selectCustomerIndustryListGroupbyIndustryId(reqVO);
  59 + if (CollUtil.isEmpty(industryRespVOList)) {
  60 + return Collections.emptyList();
  61 + }
  62 +
  63 + return convertList(industryRespVOList, item -> {
  64 + if (ObjUtil.isNull(item.getIndustryId())) {
  65 + return item;
  66 + }
  67 + item.setIndustryName(dictDataApi.getDictDataLabel(CRM_CUSTOMER_INDUSTRY, item.getIndustryId()));
  68 + return item;
  69 + });
  70 + }
  71 +
  72 + @Override
  73 + public List<CrmStatisticCustomerSourceRespVO> getCustomerSource(CrmStatisticsCustomerReqVO reqVO) {
  74 + // 1. 获得用户编号数组
  75 + List<Long> userIds = getUserIds(reqVO);
  76 + if (CollUtil.isEmpty(userIds)) {
  77 + return Collections.emptyList();
  78 + }
  79 + reqVO.setUserIds(userIds);
  80 + // 2. 获取客户行业统计数据
  81 + List<CrmStatisticCustomerSourceRespVO> sourceRespVOList = portraitMapper.selectCustomerSourceListGroupbySource(reqVO);
  82 + if (CollUtil.isEmpty(sourceRespVOList)) {
  83 + return Collections.emptyList();
  84 + }
  85 +
  86 + return convertList(sourceRespVOList, item -> {
  87 + if (ObjUtil.isNull(item.getSource())) {
  88 + return item;
  89 + }
  90 + item.setSourceName(dictDataApi.getDictDataLabel(CRM_CUSTOMER_SOURCE, item.getSource()));
  91 + return item;
  92 + });
  93 + }
  94 +
  95 + @Override
  96 + public List<CrmStatisticCustomerLevelRespVO> getCustomerLevel(CrmStatisticsCustomerReqVO reqVO) {
  97 + // 1. 获得用户编号数组
  98 + List<Long> userIds = getUserIds(reqVO);
  99 + if (CollUtil.isEmpty(userIds)) {
  100 + return Collections.emptyList();
  101 + }
  102 + reqVO.setUserIds(userIds);
  103 + // 2. 获取客户行业统计数据
  104 + List<CrmStatisticCustomerLevelRespVO> levelRespVOList = portraitMapper.selectCustomerLevelListGroupbyLevel(reqVO);
  105 + if (CollUtil.isEmpty(levelRespVOList)) {
  106 + return Collections.emptyList();
  107 + }
  108 +
  109 + return convertList(levelRespVOList, item -> {
  110 + if (ObjUtil.isNull(item.getLevel())) {
  111 + return item;
  112 + }
  113 + item.setLevelName(dictDataApi.getDictDataLabel(CRM_CUSTOMER_LEVEL, item.getLevel()));
  114 + return item;
  115 + });
  116 + }
  117 +
  118 + @Override
  119 + public List<CrmStatisticCustomerAreaRespVO> getCustomerArea(CrmStatisticsCustomerReqVO reqVO) {
  120 + // 1. 获得用户编号数组
  121 + List<Long> userIds = getUserIds(reqVO);
  122 + if (CollUtil.isEmpty(userIds)) {
  123 + return Collections.emptyList();
  124 + }
  125 + reqVO.setUserIds(userIds);
  126 + // 2. 获取客户地区统计数据
  127 + List<CrmStatisticCustomerAreaRespVO> list = portraitMapper.selectSummaryListByAreaId(reqVO);
  128 + if (CollUtil.isEmpty(list)) {
  129 + return Collections.emptyList();
  130 + }
  131 +
  132 + // 拼接数据
  133 + List<Area> areaList = AreaUtils.getByType(AreaTypeEnum.PROVINCE, area -> area);
  134 + areaList.add(new Area().setId(null).setName("未知"));
  135 + Map<Integer, Area> areaMap = convertMap(areaList, Area::getId);
  136 + List<CrmStatisticCustomerAreaRespVO> customerAreaRespVOList = convertList(list, item -> {
  137 + Integer parentId = AreaUtils.getParentIdByType(item.getAreaId(), AreaTypeEnum.PROVINCE);
  138 + if (parentId == null) {
  139 + return item;
  140 + }
  141 + findAndThen(areaMap, parentId, area -> item.setAreaId(parentId).setAreaName(area.getName()));
  142 + return item;
  143 + });
  144 + return customerAreaRespVOList;
  145 + }
  146 +
  147 + /**
  148 + * 获取用户编号数组。如果用户编号为空, 则获得部门下的用户编号数组,包括子部门的所有用户编号
  149 + *
  150 + * @param reqVO 请求参数
  151 + * @return 用户编号数组
  152 + */
  153 + private List<Long> getUserIds(CrmStatisticsCustomerReqVO reqVO) {
  154 + // 情况一:选中某个用户
  155 + if (ObjUtil.isNotNull(reqVO.getUserId())) {
  156 + return List.of(reqVO.getUserId());
  157 + }
  158 + // 情况二:选中某个部门
  159 + // 2.1 获得部门列表
  160 + List<Long> deptIds = convertList(deptApi.getChildDeptList(reqVO.getDeptId()), DeptRespDTO::getId);
  161 + deptIds.add(reqVO.getDeptId());
  162 + // 2.2 获得用户编号
  163 + return convertList(adminUserApi.getUserListByDeptIds(deptIds), AdminUserRespDTO::getId);
  164 + }
  165 +
  166 +}
yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml
@@ -229,89 +229,4 @@ @@ -229,89 +229,4 @@
229 GROUP BY customer.owner_user_id 229 GROUP BY customer.owner_user_id
230 </select> 230 </select>
231 231
232 - <select id="selectCustomerIndustryListGroupbyIndustryId"  
233 - resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerIndustryRespVO">  
234 - SELECT  
235 - industry_id,  
236 - COUNT(*) AS customerCount,  
237 - SUM(deal_status) AS dealCount,  
238 - ROUND(COUNT(*) / (SELECT COUNT(*) FROM crm_customer WHERE deleted = 0) * 100, 2) AS industryPortion,  
239 - ROUND(SUM(deal_status) / NULLIF(COUNT(*), 0) * 100, 2) AS dealPortion  
240 - FROM  
241 - crm_customer  
242 - WHERE  
243 - deleted = 0 AND industry_id IS NOT NULL  
244 - AND owner_user_id IN  
245 - <foreach collection="userIds" item="userId" open="(" close=")" separator=",">  
246 - #{userId}  
247 - </foreach>  
248 - AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND  
249 - #{times[1],javaType=java.time.LocalDateTime}  
250 - GROUP BY  
251 - industry_id;  
252 - </select>  
253 - <select id="selectCustomerSourceListGroupbySource"  
254 - resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerSourceRespVO">  
255 - SELECT  
256 - source,  
257 - COUNT(*) AS customerCount,  
258 - SUM(deal_status) AS dealCount,  
259 - ROUND(COUNT(*) / (SELECT COUNT(*) FROM crm_customer WHERE deleted = 0) * 100, 2) AS sourcePortion,  
260 - ROUND(SUM(deal_status) / NULLIF(COUNT(*), 0) * 100, 2) AS dealPortion  
261 - FROM  
262 - crm_customer  
263 - WHERE  
264 - deleted = 0 AND source IS NOT NULL  
265 - AND owner_user_id IN  
266 - <foreach collection="userIds" item="userId" open="(" close=")" separator=",">  
267 - #{userId}  
268 - </foreach>  
269 - AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND  
270 - #{times[1],javaType=java.time.LocalDateTime}  
271 - GROUP BY  
272 - source;  
273 - </select>  
274 - <select id="selectCustomerLevelListGroupbyLevel"  
275 - resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerLevelRespVO">  
276 - SELECT  
277 - level,  
278 - COUNT(*) AS customerCount,  
279 - SUM(deal_status) AS dealCount,  
280 - ROUND(COUNT(*) / (SELECT COUNT(*) FROM crm_customer WHERE deleted = 0) * 100, 2) AS levelPortion,  
281 - ROUND(SUM(deal_status) / NULLIF(COUNT(*), 0) * 100, 2) AS dealPortion  
282 - FROM  
283 - crm_customer  
284 - WHERE  
285 - deleted = 0 AND level IS NOT NULL  
286 - AND owner_user_id IN  
287 - <foreach collection="userIds" item="userId" open="(" close=")" separator=",">  
288 - #{userId}  
289 - </foreach>  
290 - AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND  
291 - #{times[1],javaType=java.time.LocalDateTime}  
292 - GROUP BY  
293 - level;  
294 - </select>  
295 - <select id="selectSummaryListByAreaId"  
296 - resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.analyze.CrmStatisticCustomerAreaRespVO">  
297 - SELECT  
298 - area_id,  
299 - COUNT(*) AS customerCount,  
300 - SUM(deal_status) AS dealCount,  
301 - ROUND(COUNT(*) / (SELECT COUNT(*) FROM crm_customer WHERE deleted = 0) * 100, 2) AS areaPortion,  
302 - ROUND(SUM(deal_status) / NULLIF(COUNT(*), 0) * 100, 2) AS dealPortion  
303 - FROM  
304 - crm_customer  
305 - WHERE  
306 - deleted = 0 AND area_id IS NOT NULL  
307 - AND owner_user_id IN  
308 - <foreach collection="userIds" item="userId" open="(" close=")" separator=",">  
309 - #{userId}  
310 - </foreach>  
311 - AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND  
312 - #{times[1],javaType=java.time.LocalDateTime}  
313 - GROUP BY  
314 - area_id;  
315 - </select>  
316 -  
317 </mapper> 232 </mapper>
yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsPortraitMapper.xml 0 → 100644
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  3 +<mapper namespace="cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsPortraitMapper">
  4 +
  5 + <select id="selectCustomerIndustryListGroupbyIndustryId"
  6 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerIndustryRespVO">
  7 + SELECT
  8 + industry_id,
  9 + COUNT(*) AS customerCount,
  10 + SUM(deal_status) AS dealCount,
  11 + ROUND(COUNT(*) / (SELECT COUNT(*) FROM crm_customer WHERE deleted = 0) * 100, 2) AS industryPortion,
  12 + ROUND(SUM(deal_status) / NULLIF(COUNT(*), 0) * 100, 2) AS dealPortion
  13 + FROM
  14 + crm_customer
  15 + WHERE
  16 + deleted = 0 AND industry_id IS NOT NULL
  17 + AND owner_user_id IN
  18 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  19 + #{userId}
  20 + </foreach>
  21 + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
  22 + #{times[1],javaType=java.time.LocalDateTime}
  23 + GROUP BY
  24 + industry_id;
  25 + </select>
  26 + <select id="selectCustomerSourceListGroupbySource"
  27 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerSourceRespVO">
  28 + SELECT
  29 + source,
  30 + COUNT(*) AS customerCount,
  31 + SUM(deal_status) AS dealCount,
  32 + ROUND(COUNT(*) / (SELECT COUNT(*) FROM crm_customer WHERE deleted = 0) * 100, 2) AS sourcePortion,
  33 + ROUND(SUM(deal_status) / NULLIF(COUNT(*), 0) * 100, 2) AS dealPortion
  34 + FROM
  35 + crm_customer
  36 + WHERE
  37 + deleted = 0 AND source IS NOT NULL
  38 + AND owner_user_id IN
  39 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  40 + #{userId}
  41 + </foreach>
  42 + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
  43 + #{times[1],javaType=java.time.LocalDateTime}
  44 + GROUP BY
  45 + source;
  46 + </select>
  47 + <select id="selectCustomerLevelListGroupbyLevel"
  48 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerLevelRespVO">
  49 + SELECT
  50 + level,
  51 + COUNT(*) AS customerCount,
  52 + SUM(deal_status) AS dealCount,
  53 + ROUND(COUNT(*) / (SELECT COUNT(*) FROM crm_customer WHERE deleted = 0) * 100, 2) AS levelPortion,
  54 + ROUND(SUM(deal_status) / NULLIF(COUNT(*), 0) * 100, 2) AS dealPortion
  55 + FROM
  56 + crm_customer
  57 + WHERE
  58 + deleted = 0 AND level IS NOT NULL
  59 + AND owner_user_id IN
  60 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  61 + #{userId}
  62 + </foreach>
  63 + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
  64 + #{times[1],javaType=java.time.LocalDateTime}
  65 + GROUP BY
  66 + level;
  67 + </select>
  68 + <select id="selectSummaryListByAreaId"
  69 + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.portrait.CrmStatisticCustomerAreaRespVO">
  70 + SELECT
  71 + area_id,
  72 + COUNT(*) AS customerCount,
  73 + SUM(deal_status) AS dealCount,
  74 + ROUND(COUNT(*) / (SELECT COUNT(*) FROM crm_customer WHERE deleted = 0) * 100, 2) AS areaPortion,
  75 + ROUND(SUM(deal_status) / NULLIF(COUNT(*), 0) * 100, 2) AS dealPortion
  76 + FROM
  77 + crm_customer
  78 + WHERE
  79 + deleted = 0 AND area_id IS NOT NULL
  80 + AND owner_user_id IN
  81 + <foreach collection="userIds" item="userId" open="(" close=")" separator=",">
  82 + #{userId}
  83 + </foreach>
  84 + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND
  85 + #{times[1],javaType=java.time.LocalDateTime}
  86 + GROUP BY
  87 + area_id;
  88 + </select>
  89 +
  90 +</mapper>