Procházet zdrojové kódy

兄弟装修需求改动,外呼看板

lmx před 2 dny
rodič
revize
bee21ffbbc

+ 64 - 0
fs-company/src/main/java/com/fs/company/controller/crm/ManualOutboundCallDashboardController.java

@@ -0,0 +1,64 @@
+package com.fs.company.controller.crm;
+
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.company.dto.DailyCallTrendDTO;
+import com.fs.company.dto.ManualOutboundCallDashboardDTO;
+import com.fs.company.dto.VisitStatusStatisticsDTO;
+import com.fs.company.param.ManualOutboundCallDashboardParam;
+import com.fs.company.service.IManualOutboundCallDashboardService;
+import com.fs.framework.security.LoginUser;
+import com.fs.framework.security.SecurityUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * 手动外呼看板Controller
+ *
+ * @author MixLiu
+ * @date 2026/5/13 17:24
+ */
+@RestController
+@RequestMapping("/crm/manualOutboundCallDashboard")
+public class ManualOutboundCallDashboardController extends BaseController {
+
+    @Autowired
+    private IManualOutboundCallDashboardService manualOutboundCallDashboardService;
+
+    /**
+     * 获取手动外呼统计数据
+     */
+    @GetMapping("/statistics")
+    public AjaxResult statistics(ManualOutboundCallDashboardParam param) {
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        param.setCompanyId(loginUser.getCompany().getCompanyId());
+        List<ManualOutboundCallDashboardDTO> list = manualOutboundCallDashboardService.getCallStatistics(param);
+        return AjaxResult.success(list);
+    }
+
+    /**
+     * 获取客户跟进阶段统计数据
+     */
+    @GetMapping("/visitStatusStatistics")
+    public AjaxResult visitStatusStatistics(ManualOutboundCallDashboardParam param) {
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        param.setCompanyId(loginUser.getCompany().getCompanyId());
+        List<VisitStatusStatisticsDTO> list = manualOutboundCallDashboardService.getVisitStatusStatistics(param);
+        return AjaxResult.success(list);
+    }
+
+    /**
+     * 获取每日外呼趋势数据
+     */
+    @GetMapping("/dailyTrend")
+    public AjaxResult dailyTrend(ManualOutboundCallDashboardParam param) {
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        param.setCompanyId(loginUser.getCompany().getCompanyId());
+        List<DailyCallTrendDTO> list = manualOutboundCallDashboardService.getDailyCallTrend(param);
+        return AjaxResult.success(list);
+    }
+}

+ 13 - 0
fs-service/src/main/java/com/fs/company/dto/DailyCallTrendDTO.java

@@ -0,0 +1,13 @@
+package com.fs.company.dto;
+
+import lombok.Data;
+
+@Data
+public class DailyCallTrendDTO {
+    /** 日期 yyyy-MM-dd */
+    private String callDate;
+    /** 当日总外呼数 */
+    private Integer totalCalls;
+    /** 当日接通数 */
+    private Integer connectedCalls;
+}

+ 23 - 0
fs-service/src/main/java/com/fs/company/dto/ManualOutboundCallDashboardDTO.java

@@ -0,0 +1,23 @@
+package com.fs.company.dto;
+
+import lombok.Data;
+
+@Data
+public class ManualOutboundCallDashboardDTO {
+    /** 销售人员ID */
+    private Long companyUserId;
+    /** 销售人员姓名 */
+    private String companyUserName;
+    /** 总外呼数 */
+    private Integer totalCalls;
+    /** 接通数 */
+    private Integer connectedCalls;
+    /** 未接通数 */
+    private Integer unconnectedCalls;
+    /** 接通率(%) */
+    private Double connectRate;
+    /** 总通话时长(秒) */
+    private Long totalDuration;
+    /** 平均通话时长(秒) */
+    private Long avgDuration;
+}

+ 11 - 0
fs-service/src/main/java/com/fs/company/dto/VisitStatusStatisticsDTO.java

@@ -0,0 +1,11 @@
+package com.fs.company.dto;
+
+import lombok.Data;
+
+@Data
+public class VisitStatusStatisticsDTO {
+    /** 跟进阶段值 */
+    private Integer visitStatus;
+    /** 该阶段客户数量 */
+    private Integer customerCount;
+}

+ 18 - 0
fs-service/src/main/java/com/fs/company/mapper/ManualOutboundCallDashboardMapper.java

@@ -0,0 +1,18 @@
+package com.fs.company.mapper;
+
+import com.fs.company.dto.DailyCallTrendDTO;
+import com.fs.company.dto.ManualOutboundCallDashboardDTO;
+import com.fs.company.dto.VisitStatusStatisticsDTO;
+import com.fs.company.param.ManualOutboundCallDashboardParam;
+import org.apache.ibatis.annotations.Mapper;
+
+import java.util.List;
+
+@Mapper
+public interface ManualOutboundCallDashboardMapper {
+    List<ManualOutboundCallDashboardDTO> selectCallStatisticsByUser(ManualOutboundCallDashboardParam param);
+
+    List<VisitStatusStatisticsDTO> selectVisitStatusStatistics(ManualOutboundCallDashboardParam param);
+
+    List<DailyCallTrendDTO> selectDailyCallTrend(ManualOutboundCallDashboardParam param);
+}

+ 13 - 0
fs-service/src/main/java/com/fs/company/param/ManualOutboundCallDashboardParam.java

@@ -0,0 +1,13 @@
+package com.fs.company.param;
+
+import lombok.Data;
+
+@Data
+public class ManualOutboundCallDashboardParam {
+    /** 开始时间 yyyy-MM-dd */
+    private String startTime;
+    /** 结束时间 yyyy-MM-dd */
+    private String endTime;
+    /** 公司ID(Controller自动注入) */
+    private Long companyId;
+}

+ 25 - 0
fs-service/src/main/java/com/fs/company/service/IManualOutboundCallDashboardService.java

@@ -0,0 +1,25 @@
+package com.fs.company.service;
+
+import com.fs.company.dto.DailyCallTrendDTO;
+import com.fs.company.dto.ManualOutboundCallDashboardDTO;
+import com.fs.company.dto.VisitStatusStatisticsDTO;
+import com.fs.company.param.ManualOutboundCallDashboardParam;
+
+import java.util.List;
+
+public interface IManualOutboundCallDashboardService {
+    /**
+     * 获取手动外呼统计数据(按销售人员分组)
+     */
+    List<ManualOutboundCallDashboardDTO> getCallStatistics(ManualOutboundCallDashboardParam param);
+
+    /**
+     * 获取客户跟进阶段统计数据
+     */
+    List<VisitStatusStatisticsDTO> getVisitStatusStatistics(ManualOutboundCallDashboardParam param);
+
+    /**
+     * 获取每日外呼趋势数据
+     */
+    List<DailyCallTrendDTO> getDailyCallTrend(ManualOutboundCallDashboardParam param);
+}

+ 57 - 0
fs-service/src/main/java/com/fs/company/service/impl/ManualOutboundCallDashboardServiceImpl.java

@@ -0,0 +1,57 @@
+package com.fs.company.service.impl;
+
+import com.fs.company.dto.DailyCallTrendDTO;
+import com.fs.company.dto.ManualOutboundCallDashboardDTO;
+import com.fs.company.dto.VisitStatusStatisticsDTO;
+import com.fs.company.mapper.ManualOutboundCallDashboardMapper;
+import com.fs.company.param.ManualOutboundCallDashboardParam;
+import com.fs.company.service.IManualOutboundCallDashboardService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class ManualOutboundCallDashboardServiceImpl implements IManualOutboundCallDashboardService {
+
+    @Autowired
+    private ManualOutboundCallDashboardMapper manualOutboundCallDashboardMapper;
+
+    @Override
+    public List<ManualOutboundCallDashboardDTO> getCallStatistics(ManualOutboundCallDashboardParam param) {
+        List<ManualOutboundCallDashboardDTO> list = manualOutboundCallDashboardMapper.selectCallStatisticsByUser(param);
+        // 计算接通率和平均通话时长
+        for (ManualOutboundCallDashboardDTO dto : list) {
+            int totalCalls = dto.getTotalCalls() != null ? dto.getTotalCalls() : 0;
+            int connectedCalls = dto.getConnectedCalls() != null ? dto.getConnectedCalls() : 0;
+            dto.setUnconnectedCalls(totalCalls - connectedCalls);
+            // 接通率
+            if (totalCalls > 0) {
+                double rate = (double) connectedCalls / totalCalls * 100;
+                dto.setConnectRate(Math.round(rate * 100.0) / 100.0);
+            } else {
+                dto.setConnectRate(0.0);
+            }
+            // 总通话时长:毫秒转秒,向上取整
+            long totalDurationMs = dto.getTotalDuration() != null ? dto.getTotalDuration() : 0L;
+            dto.setTotalDuration((long) Math.ceil(totalDurationMs / 1000.0));
+            // 平均通话时长:毫秒转秒,向上取整
+            if (connectedCalls > 0) {
+                dto.setAvgDuration((long) Math.ceil((double) totalDurationMs / connectedCalls / 1000.0));
+            } else {
+                dto.setAvgDuration(0L);
+            }
+        }
+        return list;
+    }
+
+    @Override
+    public List<VisitStatusStatisticsDTO> getVisitStatusStatistics(ManualOutboundCallDashboardParam param) {
+        return manualOutboundCallDashboardMapper.selectVisitStatusStatistics(param);
+    }
+
+    @Override
+    public List<DailyCallTrendDTO> getDailyCallTrend(ManualOutboundCallDashboardParam param) {
+        return manualOutboundCallDashboardMapper.selectDailyCallTrend(param);
+    }
+}

+ 74 - 0
fs-service/src/main/resources/mapper/company/ManualOutboundCallDashboardMapper.xml

@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.company.mapper.ManualOutboundCallDashboardMapper">
+
+    <select id="selectCallStatisticsByUser" parameterType="com.fs.company.param.ManualOutboundCallDashboardParam"
+            resultType="com.fs.company.dto.ManualOutboundCallDashboardDTO">
+        SELECT
+            ccl.company_user_id AS companyUserId,
+            cu.nick_name AS companyUserName,
+            COUNT(*) AS totalCalls,
+            SUM(CASE WHEN ccl.status = 2 AND ccl.call_time &gt; 0 THEN 1 ELSE 0 END) AS connectedCalls,
+            SUM(CASE WHEN ccl.status = 2 AND ccl.call_time &gt; 0 THEN ccl.call_time ELSE 0 END) AS totalDuration
+        FROM crm_customer_call_log ccl
+        LEFT JOIN company_user cu ON cu.user_id = ccl.company_user_id
+        <where>
+            <if test="companyId != null">
+                AND ccl.company_id = #{companyId}
+            </if>
+            <if test="startTime != null and startTime != ''">
+                AND DATE_FORMAT(ccl.create_time, '%Y-%m-%d') &gt;= #{startTime}
+            </if>
+            <if test="endTime != null and endTime != ''">
+                AND DATE_FORMAT(ccl.create_time, '%Y-%m-%d') &lt;= #{endTime}
+            </if>
+        </where>
+        GROUP BY ccl.company_user_id, cu.nick_name
+        ORDER BY totalCalls DESC
+    </select>
+
+    <select id="selectVisitStatusStatistics" parameterType="com.fs.company.param.ManualOutboundCallDashboardParam"
+            resultType="com.fs.company.dto.VisitStatusStatisticsDTO">
+        SELECT
+            cc.visit_status AS visitStatus,
+            COUNT(DISTINCT cc.customer_id) AS customerCount
+        FROM crm_customer_call_log ccl
+        INNER JOIN crm_customer cc ON cc.customer_id = ccl.customer_id
+        <where>
+            <if test="companyId != null">
+                AND ccl.company_id = #{companyId}
+            </if>
+            <if test="startTime != null and startTime != ''">
+                AND DATE_FORMAT(ccl.create_time, '%Y-%m-%d') &gt;= #{startTime}
+            </if>
+            <if test="endTime != null and endTime != ''">
+                AND DATE_FORMAT(ccl.create_time, '%Y-%m-%d') &lt;= #{endTime}
+            </if>
+        </where>
+        GROUP BY cc.visit_status
+        ORDER BY cc.visit_status ASC
+    </select>
+
+    <select id="selectDailyCallTrend" parameterType="com.fs.company.param.ManualOutboundCallDashboardParam"
+            resultType="com.fs.company.dto.DailyCallTrendDTO">
+        SELECT
+            DATE_FORMAT(ccl.create_time, '%Y-%m-%d') AS callDate,
+            COUNT(*) AS totalCalls,
+            SUM(CASE WHEN ccl.status = 2 AND ccl.call_time &gt; 0 THEN 1 ELSE 0 END) AS connectedCalls
+        FROM crm_customer_call_log ccl
+        <where>
+            <if test="companyId != null">
+                AND ccl.company_id = #{companyId}
+            </if>
+            <if test="startTime != null and startTime != ''">
+                AND DATE_FORMAT(ccl.create_time, '%Y-%m-%d') &gt;= #{startTime}
+            </if>
+            <if test="endTime != null and endTime != ''">
+                AND DATE_FORMAT(ccl.create_time, '%Y-%m-%d') &lt;= #{endTime}
+            </if>
+        </where>
+        GROUP BY DATE_FORMAT(ccl.create_time, '%Y-%m-%d')
+        ORDER BY callDate ASC
+    </select>
+
+</mapper>