Browse Source

feat: CRM/数据统计/员工客户分析 初稿

dhb52 1 year ago
parent
commit
34f68ce4c6

+ 14 - 0
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));
+    }
+
 }

+ 7 - 1
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;
 
 }

+ 4 - 0
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);
+
 }

+ 16 - 0
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);
+
 }

+ 45 - 8
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());
             }
         });
 

+ 44 - 11
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>