diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java index ffd88e97a..5644c512b 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/CrmStatisticsCustomerController.java @@ -55,4 +55,18 @@ public class CrmStatisticsCustomerController { return success(customerService.getDistinctRecordCount(reqVO)); } + @GetMapping("/get-record-type-count") + @Operation(summary = "获取客户跟进方式统计数") + @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") + public CommonResult<List<CrmStatisticsCustomerCountVO>> getRecordTypeCount(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getRecordTypeCount(reqVO)); + } + + @GetMapping("/get-customer-cycle") + @Operation(summary = "获取客户成交周期") + @PreAuthorize("@ss.hasPermission('crm:statistics-customer:query')") + public CommonResult<List<CrmStatisticsCustomerCountVO>> getCustomerCycle(@Valid CrmStatisticsCustomerReqVO reqVO) { + return success(customerService.getCustomerCycle(reqVO)); + } + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerCountVO.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerCountVO.java index 01dbd6fc2..a2537db9a 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerCountVO.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/statistics/vo/customer/CrmStatisticsCustomerCountVO.java @@ -23,6 +23,12 @@ public class CrmStatisticsCustomerCountVO { * 2. 个数:签约合同排行、产品销量排行、产品销量排行、新增客户数排行、新增联系人排行、跟进次数排行、跟进客户数排行 */ @Schema(description = "数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer count; + private Integer count = 0; + + /** + * 成交周期(天) + */ + @Schema(description = "成交周期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1.0") + private Double cycle = 0.0; } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java index 1be2dc1ff..464c521c6 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/statistics/CrmStatisticsCustomerMapper.java @@ -22,4 +22,8 @@ public interface CrmStatisticsCustomerMapper { List<CrmStatisticsCustomerCountVO> selectDistinctRecordCountGroupbyDate(CrmStatisticsCustomerReqVO reqVO); + List<CrmStatisticsCustomerCountVO> selectRecordCountGroupbyType(CrmStatisticsCustomerReqVO reqVO); + + List<CrmStatisticsCustomerCountVO> selectCustomerCycleGroupbyDate(CrmStatisticsCustomerReqVO reqVO); + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java index 908f02c99..e568816d6 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerService.java @@ -45,4 +45,20 @@ public interface CrmStatisticsCustomerService { */ List<CrmStatisticsCustomerCountVO> getDistinctRecordCount(CrmStatisticsCustomerReqVO reqVO); + /** + * 获取客户跟进方式统计数 + * + * @param reqVO 请求参数 + * @return 客户跟进方式统计数 + */ + List<CrmStatisticsCustomerCountVO> getRecordTypeCount(CrmStatisticsCustomerReqVO reqVO); + + /** + * 获取客户成交周期 + * + * @param reqVO 请求参数 + * @return 客户成交周期 + */ + List<CrmStatisticsCustomerCountVO> getCustomerCycle(CrmStatisticsCustomerReqVO reqVO); + } diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java index 08cd1c480..94eb560d1 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/java/cn/iocoder/yudao/module/crm/service/statistics/CrmStatisticsCustomerServiceImpl.java @@ -3,12 +3,14 @@ package cn.iocoder.yudao.module.crm.service.statistics; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.util.ObjUtil; -import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO; import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO; +import cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerReqVO; import cn.iocoder.yudao.module.crm.dal.mysql.statistics.CrmStatisticsCustomerMapper; import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum; import cn.iocoder.yudao.module.system.api.dept.DeptApi; import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; +import cn.iocoder.yudao.module.system.api.dict.DictDataApi; +import cn.iocoder.yudao.module.system.api.dict.dto.DictDataRespDTO; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import jakarta.annotation.Resource; @@ -21,7 +23,8 @@ import java.util.List; import java.util.Map; import java.util.function.Function; -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; /** * CRM 数据统计 员工客户分析 Service 实现类 @@ -39,6 +42,8 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe private AdminUserApi adminUserApi; @Resource private DeptApi deptApi; + @Resource + private DictDataApi dictDataApi; @Override public List<CrmStatisticsCustomerCountVO> getTotalCustomerCount(CrmStatisticsCustomerReqVO reqVO) { @@ -62,6 +67,37 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe return getStat(reqVO, customerMapper::selectDistinctRecordCountGroupbyDate); } + @Override + public List<CrmStatisticsCustomerCountVO> getRecordTypeCount(CrmStatisticsCustomerReqVO reqVO) { + // 1. 获得用户编号数组: 如果用户编号为空, 则获得部门下的用户编号数组 + if (ObjUtil.isNotNull(reqVO.getUserId())) { + reqVO.setUserIds(List.of(reqVO.getUserId())); + } else { + reqVO.setUserIds(getUserIds(reqVO.getDeptId())); + } + if (CollUtil.isEmpty(reqVO.getUserIds())) { + return Collections.emptyList(); + } + + // 2. 获得排行数据 + reqVO.setBizType(CrmBizTypeEnum.CRM_CUSTOMER.getType()); + List<CrmStatisticsCustomerCountVO> stats = customerMapper.selectRecordCountGroupbyType(reqVO); + + // 3. 获取字典数据 + List<DictDataRespDTO> followUpTypes = dictDataApi.getDictDataList("crm_follow_up_type"); + final Map<String, String> followUpTypeMap = convertMap(followUpTypes, DictDataRespDTO::getValue, DictDataRespDTO::getLabel); + stats.forEach(stat -> { + stat.setCategory(followUpTypeMap.get(stat.getCategory())); + }); + + return stats; + } + + @Override + public List<CrmStatisticsCustomerCountVO> getCustomerCycle(CrmStatisticsCustomerReqVO reqVO) { + return getStat(reqVO, customerMapper::selectCustomerCycleGroupbyDate); + } + /** * 获得统计数据 * @@ -98,9 +134,9 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe // 4. 生成时间序列 List<CrmStatisticsCustomerCountVO> result = CollUtil.newArrayList(); - while (startTime.compareTo(endTime) <= 0) { + while (!startTime.isAfter(endTime)) { final String category = LocalDateTimeUtil.format(startTime, byMonth ? "yyyyMM" : "yyyyMMdd"); - result.add(new CrmStatisticsCustomerCountVO().setCategory(category).setCount(0)); + result.add(new CrmStatisticsCustomerCountVO().setCategory(category)); if (byMonth) startTime = startTime.plusMonths(1); else @@ -108,12 +144,13 @@ public class CrmStatisticsCustomerServiceImpl implements CrmStatisticsCustomerSe } // 5. 使用时间序列填充结果 - final Map<String, Integer> statMap = convertMap(stats, - CrmStatisticsCustomerCountVO::getCategory, - CrmStatisticsCustomerCountVO::getCount); + final Map<String, CrmStatisticsCustomerCountVO> statMap = convertMap(stats, + CrmStatisticsCustomerCountVO::getCategory, + Function.identity()); result.forEach(r -> { if (statMap.containsKey(r.getCategory())) { - r.setCount(statMap.get(r.getCategory())); + r.setCount(statMap.get(r.getCategory()).getCount()) + .setCycle(statMap.get(r.getCategory()).getCycle()); } }); diff --git a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml index cbc87f59f..06affccab 100644 --- a/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml +++ b/yudao-module-crm/yudao-module-crm-biz/src/main/resources/mapper/statistics/CrmStatisticsCustomerMapper.xml @@ -6,8 +6,8 @@ <select id="selectCustomerCountGroupbyDate" resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO"> SELECT - count(*) AS count, - DATE_FORMAT( create_time, #{sqlDateFormat,javaType=java.lang.String} ) AS category + DATE_FORMAT( create_time, #{sqlDateFormat,javaType=java.lang.String} ) AS category, + count(*) AS count FROM crm_customer WHERE @@ -18,15 +18,14 @@ </foreach> AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} - GROUP BY - DATE_FORMAT( create_time, #{sqlDateFormat,javaType=java.lang.String} ) + GROUP BY category </select> <select id="selectDealCustomerCountGroupbyDate" resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO"> SELECT - count( DISTINCT a.id ) AS count, - DATE_FORMAT( b.order_date, #{sqlDateFormat,javaType=java.lang.String} ) AS category + DATE_FORMAT( b.order_date, #{sqlDateFormat,javaType=java.lang.String} ) AS category, + count( DISTINCT a.id ) AS count FROM crm_customer AS a LEFT JOIN crm_contract AS b ON b.customer_id = a.id @@ -39,8 +38,7 @@ </foreach> AND b.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} - GROUP BY - DATE_FORMAT( b.order_date, #{sqlDateFormat,javaType=java.lang.String} ) + GROUP BY category </select> <select id="selectRecordCountGroupbyDate" @@ -58,8 +56,7 @@ AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} AND biz_type = #{bizType,javaType=java.lang.Integer} - GROUP BY - DATE_FORMAT (create_time, #{sqlDateFormat,javaType=java.lang.String} ) + GROUP BY category </select> <select id="selectDistinctRecordCountGroupbyDate" @@ -77,7 +74,43 @@ AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND #{times[1],javaType=java.time.LocalDateTime} AND biz_type = #{bizType,javaType=java.lang.Integer} - GROUP BY DATE_FORMAT (create_time, #{sqlDateFormat,javaType=java.lang.String} ) + GROUP BY category + </select> + + <select id="selectRecordCountGroupbyType" + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO"> + SELECT + type AS category, + count(*) AS count + FROM crm_follow_up_record + WHERE + creator IN + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> + #{userId} + </foreach> + AND create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND + #{times[1],javaType=java.time.LocalDateTime} + AND biz_type = #{bizType,javaType=java.lang.Integer} + GROUP BY category + </select> + + <select id="selectCustomerCycleGroupbyDate" + resultType="cn.iocoder.yudao.module.crm.controller.admin.statistics.vo.customer.CrmStatisticsCustomerCountVO"> + SELECT + DATE_FORMAT( b.order_date, #{sqlDateFormat,javaType=java.lang.String} ) AS category, + IFNULL( TRUNCATE ( AVG( TIMESTAMPDIFF( DAY, a.create_time, b.order_date )), 1 ), 0 ) AS cycle + FROM crm_customer AS a + LEFT JOIN crm_contract AS b ON b.customer_id = a.id + WHERE + a.deleted = 0 AND b.deleted = 0 + AND b.audit_status = 20 + AND a.owner_user_id IN + <foreach collection="userIds" item="userId" open="(" close=")" separator=","> + #{userId} + </foreach> + AND b.create_time BETWEEN #{times[0],javaType=java.time.LocalDateTime} AND + #{times[1],javaType=java.time.LocalDateTime} + GROUP BY category </select> </mapper>