Browse Source

sop分析api

shudonghui 1 năm trước cách đây
mục cha
commit
e4e96b40cb

+ 122 - 0
sop-api/src/main/java/com/qmth/sop/server/api/SopAnalyseController.java

@@ -0,0 +1,122 @@
+package com.qmth.sop.server.api;
+
+import com.qmth.boot.api.annotation.Aac;
+import com.qmth.boot.api.constant.ApiConstant;
+import com.qmth.sop.business.entity.TBService;
+import com.qmth.sop.business.service.SopAnalyseService;
+import com.qmth.sop.common.enums.SopAnalyseGroupEnum;
+import com.qmth.sop.common.enums.SopAnalyseSortEnum;
+import com.qmth.sop.common.util.Result;
+import com.qmth.sop.common.util.ResultUtil;
+import io.swagger.annotations.*;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * sop分析 控制器
+ *
+ * @author: shudonghui
+ * @date: 2023-08-14 15:55:18
+ * @version: 1.0
+ * @email: shudonghui@qmth.com.cn
+ * @Company: www.qmth.com.cn
+ */
+@Api(tags = "sop分析 Controller")
+@RestController
+@RequestMapping(ApiConstant.DEFAULT_URI_PREFIX + "/sop/analyse")
+public class SopAnalyseController {
+
+
+    @Resource
+    SopAnalyseService sopAnalyseService;
+
+
+
+    @ApiOperation(value = "服务单元列表接口")
+    @RequestMapping(value = "/list", method = RequestMethod.POST)
+    @ApiResponses({@ApiResponse(code = 200, message = "服务单元列表", response = TBService.class)})
+    public Result list(@ApiParam(value = "年度", required = true) @RequestParam String year) {
+        List<TBService> list = sopAnalyseService.list(year);
+        return ResultUtil.ok(list);
+    }
+
+    /*
+     * 1.监控台是对当前SOP执行情况(延期、违规预警),以及项目管理角色(大区、供应商)对SOP预警处理情况、考勤异常的实时监控;
+     * 2.动态查询条件:时间周期(年度等)、服务单元(已发布状态);
+     * 3.总体概况(中间上面一部分的数据看板)
+     * ①可按照大区/人力供应商进行切换如图;
+     * ②按照派单数TOP3的大区、人力供应商显示;
+     * ③完成进度:该大区/供应商派单的完成进度,按照派单已有完成SOP,且没有在执行的SOP认为是已完成的派单;
+     * ④平均处理时限:该大区/供应商预警处理的平均时限。按照预警产生到关闭的时长的均值,≥1小时,按小时显示;<小时,按分钟显示;
+     * 若预警为关闭后重启,则需要重新从预警产生到最后一次关闭的时长进行计算;
+     */
+
+    @ApiOperation(value = "总体概况接口")
+    @RequestMapping(value = "/overview", method = RequestMethod.POST)
+    @ApiResponses({@ApiResponse(code = 200, message = "总体概况", response = Map.class)})
+    public Result overview(@ApiParam(value = "服务单元", required = true) @RequestParam Long serviceId,
+                           @ApiParam(value = "分组", required = true) @RequestParam SopAnalyseGroupEnum group) {
+        List<Map<String, Object>> list = sopAnalyseService.overview(serviceId, group);
+        return ResultUtil.ok(list);
+    }
+
+
+    /*
+     * 4.项目/供应商/大区预警待处理TOP10:
+     * ①待处理(待处理预警):按派单统计待处理的预警数(违规+延期);
+     * ②处理最慢(已处理完毕的预警):
+     * A.平均时限:按照预警产生到关闭的时长的均值,≥1小时,按小时显示;<小时,按分钟显示;
+     * 若预警为关闭后重启,则需要重新从预警产生到最后一次关闭的时长进行计算;
+     * B.排序:按平均时限的倒序排列;
+     * ③处理最快(已处理完毕的预警):
+     * A.平均时限:同上;
+     * B.排序:按平均时限的顺序排列。
+     * ④(预警数)均值:由于供应商分配的项目数量不同,所以单从总数无法判定供应商对预警处理的力度。均值=预警总数/派单数;
+     * ⑤【数据下钻】不提供。
+     */
+
+    @ApiOperation(value = "项目/供应商/大区预警待处理TOP10接口")
+    @RequestMapping(value = "/processing", method = RequestMethod.POST)
+    @ApiResponses({@ApiResponse(code = 200, message = "项目/供应商/大区预警待处理TOP10", response = Map.class)})
+    public Result processing(@ApiParam(value = "服务单元", required = true) @RequestParam Long serviceId,
+                             @ApiParam(value = "分组", required = true) @RequestParam SopAnalyseGroupEnum group,
+                             @ApiParam(value = "排序", required = true) @RequestParam SopAnalyseSortEnum sort) {
+        List<Map<String, Object>> list = sopAnalyseService.processing(serviceId, group,sort);
+        return ResultUtil.ok(list);
+    }
+
+    /*
+     * 5.供应商预警均值走势:
+     * 供应商近一周预警数的均值走势。
+     */
+
+    @ApiOperation(value = "供应商预警均值走势接口")
+    @RequestMapping(value = "/trend", method = RequestMethod.POST)
+    @ApiResponses({@ApiResponse(code = 200, message = "供应商预警均值走势", response = Map.class)})
+    public Result trend(@ApiParam(value = "服务单元", required = true) @RequestParam Long serviceId) {
+        return ResultUtil.ok(sopAnalyseService.trend(serviceId));
+    }
+
+    /*
+      6.项目/大区/供应商考勤异常TOP:
+      ①(异常)均值=异常总数/派单数;
+      ②【数据下钻】不提供;
+     */
+
+    @ApiOperation(value = "项目/大区/供应商考勤异常TOP接口")
+    @RequestMapping(value = "/attendance", method = RequestMethod.POST)
+    @ApiResponses({@ApiResponse(code = 200, message = "项目/大区/供应商考勤异常TOP", response = Map.class)})
+    public Result attendance(@ApiParam(value = "服务单元", required = true) @RequestParam Long serviceId,
+                             @ApiParam(value = "分组", required = true) @RequestParam SopAnalyseGroupEnum group) {
+        List<Map<String, Object>> list = sopAnalyseService.attendance(serviceId, group);
+        return ResultUtil.ok(list);
+    }
+
+
+}

+ 28 - 0
sop-business/src/main/java/com/qmth/sop/business/mapper/SopAnalyseMapper.java

@@ -0,0 +1,28 @@
+package com.qmth.sop.business.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.qmth.sop.business.entity.TBService;
+import com.qmth.sop.business.entity.TBSopInfo;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * <p>
+ * </p>
+ *
+ * @author dhshu
+ *
+ */
+public interface SopAnalyseMapper extends BaseMapper<TBSopInfo> {
+
+
+    List<TBService> list(@Param("year") String year);
+
+    List<Map<String, Object>> overview(@Param("serviceId") Long serviceId);
+
+    List<Map<String, Object>> trend(@Param("serviceId")Long serviceId);
+
+    List<Map<String, Object>> attendance(@Param("serviceId")Long serviceId);
+}

+ 23 - 0
sop-business/src/main/java/com/qmth/sop/business/service/SopAnalyseService.java

@@ -0,0 +1,23 @@
+package com.qmth.sop.business.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.qmth.sop.business.entity.TBService;
+import com.qmth.sop.business.entity.TBSopInfo;
+import com.qmth.sop.common.enums.SopAnalyseGroupEnum;
+import com.qmth.sop.common.enums.SopAnalyseSortEnum;
+
+import java.util.List;
+import java.util.Map;
+
+public interface SopAnalyseService extends IService<TBSopInfo> {
+    List<TBService> list(String year);
+
+
+    List<Map<String, Object>> overview(Long serviceId, SopAnalyseGroupEnum group);
+
+    List<Map<String, Object>> processing(Long serviceId, SopAnalyseGroupEnum group, SopAnalyseSortEnum sort);
+
+    Map<Object, Map<Object, List<Map<String, Object>>>> trend(Long serviceId);
+
+    List<Map<String, Object>> attendance(Long serviceId, SopAnalyseGroupEnum group);
+}

+ 214 - 0
sop-business/src/main/java/com/qmth/sop/business/service/impl/SopAnalyseServiceImpl.java

@@ -0,0 +1,214 @@
+package com.qmth.sop.business.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.qmth.sop.business.entity.TBService;
+import com.qmth.sop.business.entity.TBSopInfo;
+import com.qmth.sop.business.mapper.SopAnalyseMapper;
+import com.qmth.sop.business.service.SopAnalyseService;
+import com.qmth.sop.common.enums.SopAnalyseGroupEnum;
+import com.qmth.sop.common.enums.SopAnalyseSortEnum;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Service
+public class SopAnalyseServiceImpl extends ServiceImpl<SopAnalyseMapper, TBSopInfo> implements SopAnalyseService {
+
+
+    @Override
+    public List<TBService> list(String year) {
+        return this.baseMapper.list(year);
+    }
+
+
+    /**
+     * * ①可按照大区/人力供应商进行切换如图;
+     * * ②按照派单数TOP3的大区、人力供应商显示;
+     * * ③完成进度:该大区/供应商派单的完成进度,按照派单已有完成SOP,且没有在执行的SOP认为是已完成的派单;
+     * * ④平均处理时限:该大区/供应商预警处理的平均时限。按照预警产生到关闭的时长的均值,≥1小时,按小时显示;<小时,按分钟显示;
+     */
+    @Override
+    public List<Map<String, Object>> overview(Long serviceId, SopAnalyseGroupEnum group) {
+        List<Map<String, Object>> list = this.baseMapper.overview(serviceId);
+        List<Map<String, Object>> result = new ArrayList<>();
+        if (group.equals(SopAnalyseGroupEnum.REGION)) {
+            list.stream().collect(Collectors.groupingBy(map -> map.get("province"))).forEach((k, v) -> {
+                Map<String, Object> map = new HashMap<>();
+                map.put("province", k);
+                processOverviewData(v, map);
+                result.add(map);
+            });
+        } else if (group.equals(SopAnalyseGroupEnum.SUPPLIER)) {
+            list.stream().collect(Collectors.groupingBy(map -> map.get("supplier"))).forEach((k, v) -> {
+                Map<String, Object> map = new HashMap<>();
+                map.put("supplier", k);
+                processOverviewData(v, map);
+                result.add(map);
+            });
+        }
+        return result.stream().sorted(Comparator.comparingInt(o -> Integer.parseInt(o.get("crmNum").toString()))).limit(3).collect(Collectors.toList());
+    }
+
+    private static void processOverviewData(List<Map<String, Object>> v, Map<String, Object> map) {
+        //派单数
+        map.put("crmNum", v.stream().collect(Collectors.groupingBy(map1 -> map1.get("crm_no"))).size());
+        //已完成派单数
+        map.put("finishCrmNum", v.stream().collect(Collectors.groupingBy(map1 -> map1.get("crm_no"))).values().stream().filter(list1 -> list1.stream().allMatch(map1 -> "FINISH".equals(map1.get("sopStatus"))) || list1.stream().allMatch(map1 -> "FINISH".equals(map1.get("status")))).count());
+        //违规数
+        map.put("violationNum", v.stream().filter(map1 -> map1.get("did") != null).collect(Collectors.groupingBy(map1 -> map1.get("did"))).size());
+        //已关闭违规数
+        map.put("finishViolationNum", v.stream().filter(map1 -> map1.get("did") != null && "CLOSE".equals(map1.get("dstatus"))).count());
+        //延期数
+        map.put("delayNum", v.stream().filter(map1 -> map1.get("vid") != null).collect(Collectors.groupingBy(map1 -> map1.get("vid"))).size());
+        //已关闭延期数
+        map.put("finishDelayNum", v.stream().filter(map1 -> map1.get("vid") != null && "CLOSE".equals(map1.get("vstatus"))).count());
+        Double processViolationTime = v.stream().filter(map1 -> map1.get("ddiff") != null).mapToDouble(map1 -> Double.parseDouble(map1.get("ddiff").toString())).sum();
+        Double processDelayTime = v.stream().filter(map1 -> map1.get("vdiff") != null).mapToDouble(map1 -> Double.parseDouble(map1.get("vdiff").toString())).sum();
+        //平均处理时限分钟
+        int total = (Integer.parseInt(map.get("finishViolationNum").toString()) + Integer.parseInt(map.get("finishDelayNum").toString()));
+        map.put("avgMinutes", total == 0 ? 0 : (processViolationTime + processDelayTime) / total);
+
+
+    }
+
+    /*
+     * 4.项目/供应商/大区预警待处理TOP10:
+     * ①待处理(待处理预警):按派单统计待处理的预警数(违规+延期);
+     * ②处理最慢(已处理完毕的预警):
+     * A.平均时限:按照预警产生到关闭的时长的均值,≥1小时,按小时显示;<小时,按分钟显示;
+     * 若预警为关闭后重启,则需要重新从预警产生到最后一次关闭的时长进行计算;
+     * B.排序:按平均时限的倒序排列;
+     * ③处理最快(已处理完毕的预警):
+     * A.平均时限:同上;
+     * B.排序:按平均时限的顺序排列。
+     * ④(预警数)均值:由于供应商分配的项目数量不同,所以单从总数无法判定供应商对预警处理的力度。均值=预警总数/派单数;
+     * ⑤【数据下钻】不提供。
+     */
+    @Override
+    public List<Map<String, Object>> processing(Long serviceId, SopAnalyseGroupEnum group, SopAnalyseSortEnum sort) {
+        List<Map<String, Object>> result = new ArrayList<>();
+        List<Map<String, Object>> list = this.baseMapper.overview(serviceId);
+        if (group.equals(SopAnalyseGroupEnum.CRM)) {
+            list.stream().collect(Collectors.groupingBy(map -> map.get("name"))).forEach((k, v) -> {
+                Map<String, Object> map = new HashMap<>();
+                map.put("crmName", k);
+                //大区
+                map.put("province", v.get(0).get("province"));
+                processing(v, map);
+                result.add(map);
+            });
+        } else if (group.equals(SopAnalyseGroupEnum.REGION)) {
+            list.stream().collect(Collectors.groupingBy(map -> map.get("province"))).forEach((k, v) -> {
+                Map<String, Object> map = new HashMap<>();
+                map.put("province", k);
+                processing(v, map);
+                result.add(map);
+            });
+        } else if (group.equals(SopAnalyseGroupEnum.SUPPLIER)) {
+            list.stream().collect(Collectors.groupingBy(map -> map.get("supplier"))).forEach((k, v) -> {
+                Map<String, Object> map = new HashMap<>();
+                map.put("supplier", k);
+                //大区
+                map.put("province", v.get(0).get("province"));
+                processing(v, map);
+                result.add(map);
+            });
+        }
+
+        if (sort.equals(SopAnalyseSortEnum.PENDING)) {
+            return result.stream().sorted(Comparator.comparingInt(o -> -Integer.parseInt(o.get("pendingProcessing").toString()))).limit(10).collect(Collectors.toList());
+        } else if (sort.equals(SopAnalyseSortEnum.SLOWEST)) {
+            return result.stream().sorted(Comparator.comparingDouble(o -> -Double.parseDouble(o.get("avgMinutes").toString()))).limit(10).collect(Collectors.toList());
+        } else if (sort.equals(SopAnalyseSortEnum.FASTEST)) {
+            return result.stream().sorted(Comparator.comparingDouble(o -> Double.parseDouble(o.get("avgMinutes").toString()))).limit(10).collect(Collectors.toList());
+        }
+        return null;
+
+    }
+
+    private static void processing(List<Map<String, Object>> v, Map<String, Object> map) {
+        //区域协调人
+        map.put("real_name", v.get(0).get("real_name"));
+        //违规数
+        Integer violationNum = v.stream().filter(map1 -> map1.get("did") != null).collect(Collectors.groupingBy(map1 -> map1.get("did"))).size();
+        //已关闭违规数
+        Long finishViolationNum = v.stream().filter(map1 -> map1.get("did") != null && "CLOSE".equals(map1.get("dstatus"))).count();
+        //延期数
+        Integer delayNum = v.stream().filter(map1 -> map1.get("vid") != null).collect(Collectors.groupingBy(map1 -> map1.get("vid"))).size();
+        //已关闭延期数
+        Long finishDelayNum = v.stream().filter(map1 -> map1.get("vid") != null && "CLOSE".equals(map1.get("vstatus"))).count();
+        Double processViolationTime = v.stream().filter(map1 -> map1.get("ddiff") != null).mapToDouble(map1 -> Double.parseDouble(map1.get("ddiff").toString())).sum();
+        Double processDelayTime = v.stream().filter(map1 -> map1.get("vdiff") != null).mapToDouble(map1 -> Double.parseDouble(map1.get("vdiff").toString())).sum();
+        //平均处理时限分钟
+        long total = finishViolationNum + finishDelayNum;
+        map.put("avgMinutes", total == 0 ? 0 : (processViolationTime + processDelayTime) / total);
+        //待处理总数数
+        map.put("pendingProcessing", violationNum + delayNum - total);
+        //预警均值
+        map.put("avgWarn", (violationNum + delayNum) / v.stream().collect(Collectors.groupingBy(map1 -> map1.get("crm_no"))).size());
+    }
+
+    /*
+     * 5.供应商预警均值走势:
+     * 供应商近一周预警数的均值走势。
+     */
+    @Override
+    public Map<Object, Map<Object, List<Map<String, Object>>>> trend(Long serviceId) {
+        List<Map<String, Object>> list = this.baseMapper.trend(serviceId);
+        Map<Object, List<Map<String, Object>>> collect1 = list.stream().collect(Collectors.groupingBy(map -> map.get("click_date")));
+        Map<Object, Map<Object, List<Map<String, Object>>>> collect = new HashMap<>();
+        collect1.forEach((k, v) -> collect.put(k,v.stream().filter(map -> map.get("supplier") != null).collect(Collectors.groupingBy(map1 -> map1.get("supplier")))));
+        return collect;
+
+    }
+
+    /*
+      6.项目/大区/供应商考勤异常TOP:
+      ①(异常)均值=异常总数/派单数;
+      ②【数据下钻】不提供;
+     */
+    @Override
+    public List<Map<String, Object>> attendance(Long serviceId, SopAnalyseGroupEnum group) {
+        List<Map<String, Object>> result = new ArrayList<>();
+        List<Map<String, Object>> attendance = this.baseMapper.attendance(serviceId);
+        if (group.equals(SopAnalyseGroupEnum.CRM)) {
+            attendance.stream().collect(Collectors.groupingBy(map -> map.get("name"))).forEach((k, v) -> {
+
+                v.stream().filter(map -> map.get("dname")!=null).collect(Collectors.groupingBy(map -> map.get("dname"))).forEach((x,y)->{
+                    Map<String, Object> map = new HashMap<>();
+                    map.put("crmName", k);
+                    map.put("dname", x);
+                    map.put("real_name", v.get(0).get("real_name"));
+                    map.put("total", y.stream().filter(map1 -> map1.get("id") != null).count());
+                    result.add(map);
+                });
+            });
+
+
+        } else if (group.equals(SopAnalyseGroupEnum.REGION)) {
+            attendance.stream().collect(Collectors.groupingBy(map -> map.get("province"))).forEach((k, v) -> {
+                Map<String, Object> map = new HashMap<>();
+                map.put("supplier", k);
+                map.put("real_name", v.get(0).get("real_name"));
+                long total = v.stream().filter(map1 -> map1.get("id") != null).count();
+                map.put("total", total);
+                map.put("avg", Double.parseDouble(total+"")/v.stream().collect(Collectors.groupingBy(map1 -> map1.get("crm_no"))).size());
+                result.add(map);
+            });
+
+        } else if (group.equals(SopAnalyseGroupEnum.SUPPLIER)) {
+            attendance.stream().collect(Collectors.groupingBy(map -> map.get("supplier"))).forEach((k, v) -> {
+                Map<String, Object> map = new HashMap<>();
+                map.put("supplier", k);
+                long total = v.stream().filter(map1 -> map1.get("id") != null).count();
+                map.put("total",total);
+                map.put("avg", Double.parseDouble(total+"")/v.stream().collect(Collectors.groupingBy(map1 -> map1.get("crm_no"))).size());
+                result.add(map);
+            });
+
+        }
+        return result.stream().sorted(Comparator.comparingInt(o -> -Integer.parseInt(o.get("total").toString()))).limit(10).collect(Collectors.toList());
+    }
+
+}

+ 136 - 0
sop-business/src/main/resources/mapper/SopAnalyseMapper.xml

@@ -0,0 +1,136 @@
+<?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.qmth.sop.business.mapper.SopAnalyseMapper">
+    <select id="list" resultType="com.qmth.sop.business.entity.TBService">
+        select * from t_b_service s
+        <where>
+            and s.status = 'PUBLISH'
+
+            <if test="year != null and year != ''">
+                and YEAR ( FROM_UNIXTIME( s.start_time / 1000 ))= #{year}
+            </if>
+
+        </where>
+        order by s.start_time desc
+    </select>
+    <select id="overview" resultType="java.util.Map">
+        SELECT
+        sc.province,
+        u.real_name,
+        su.NAME supplier,
+        c.crm_no,
+        c.name,
+        c.`status`,
+        s.sop_no,
+        tffa.status sopStatus,
+        d.id did,
+        d.`status` dstatus,
+        case when d.`status`='CLOSE' then ((d.close_time - d.warn_time)/60/1000) else null end ddiff,
+        v.id vid,
+        v.`status` vstatus,
+        case when v.`status`='CLOSE' then ((v.close_time - v.create_time)/60/1000) else null end vdiff
+        FROM
+        t_b_sop_info s
+        left join t_b_crm c on c.crm_no=s.crm_no
+        LEFT JOIN sys_custom sc ON sc.id = s.custom_id
+        LEFT join t_f_custom_flow_entity tfcfe on tfcfe.code = s.sop_no
+        LEFT join t_f_flow_approve tffa on tffa.flow_id = tfcfe.flow_id
+        left join t_b_sop_info_detail id on id.sop_info_id=s.id
+        LEFT JOIN sys_user u ON u.id = id.lead_id
+        LEFT JOIN t_b_user_archives ua ON ua.mobile_number = u.mobile_number
+        LEFT JOIN t_b_user_archives_supplier us ON us.user_archives_id = ua.id
+        LEFT JOIN sys_supplier su ON su.id = us.supplier_id
+        left join t_b_delay_warn d on d.sop_no=s.sop_no
+        left join t_b_violation v on v.sop_no=s.sop_no
+        <where>
+
+            <if test="serviceId != null and serviceId != ''">
+               and  s.service_id= #{serviceId}
+            </if>
+
+        </where>
+    </select>
+    <select id="trend" resultType="java.util.Map">
+        SELECT
+        a.click_date,
+        b.supplier,
+        ifnull( b.count, 0 ) AS count
+        FROM
+        (
+        SELECT
+        curdate() AS click_date UNION ALL
+        SELECT
+        date_sub( curdate(), INTERVAL 1 DAY ) AS click_date UNION ALL
+        SELECT
+        date_sub( curdate(), INTERVAL 2 DAY ) AS click_date UNION ALL
+        SELECT
+        date_sub( curdate(), INTERVAL 3 DAY ) AS click_date UNION ALL
+        SELECT
+        date_sub( curdate(), INTERVAL 4 DAY ) AS click_date UNION ALL
+        SELECT
+        date_sub( curdate(), INTERVAL 5 DAY ) AS click_date UNION ALL
+        SELECT
+        date_sub( curdate(), INTERVAL 6 DAY ) AS click_date
+        ) a
+        LEFT JOIN (
+        SELECT
+        su.NAME supplier,
+        DATE_FORMAT(
+        FROM_UNIXTIME( ( CASE WHEN d.create_time IS NOT NULL THEN d.create_time WHEN v.create_time IS NOT NULL THEN v.create_time ELSE NULL END )/ 1000 ),
+        '%Y-%m-%d'
+        ) dattime,
+        count( DISTINCT d.id )+ count( DISTINCT v.id ) count
+        FROM
+        t_b_sop_info s
+        LEFT JOIN t_b_sop_info_detail id ON id.sop_info_id = s.id
+        LEFT JOIN sys_user u ON u.id = id.lead_id
+        LEFT JOIN t_b_user_archives ua ON ua.mobile_number = u.mobile_number
+        LEFT JOIN t_b_user_archives_supplier us ON us.user_archives_id = ua.id
+        LEFT JOIN sys_supplier su ON su.id = us.supplier_id
+        LEFT JOIN t_b_delay_warn d ON d.sop_no = s.sop_no
+        LEFT JOIN t_b_violation v ON v.sop_no = s.sop_no
+        <where>
+            and (d.id IS NOT NULL OR v.id IS NOT NULL)
+            <if test="serviceId != null and serviceId != ''">
+                and  s.service_id= #{serviceId}
+            </if>
+
+        </where>
+        GROUP BY
+        su.NAME,
+        dattime
+        ) b ON a.click_date = b.dattime
+    </select>
+
+    <select id="attendance" resultType="java.util.Map">
+        SELECT
+        sc.province,
+        u.real_name,
+        su.NAME supplier,
+        c.crm_no,
+        c.name,
+        c.`status`,
+        s.sop_no,
+        d.id,
+        du.real_name dname
+
+        FROM
+        t_b_sop_info s
+        left join t_b_crm c on c.crm_no=s.crm_no
+        LEFT JOIN sys_custom sc ON sc.id = s.custom_id
+        left join t_b_sop_info_detail id on id.sop_info_id=s.id
+        LEFT JOIN sys_user u ON u.id = id.lead_id
+        LEFT JOIN t_b_user_archives ua ON ua.mobile_number = u.mobile_number
+        LEFT JOIN t_b_user_archives_supplier us ON us.user_archives_id = ua.id
+        LEFT JOIN sys_supplier su ON su.id = us.supplier_id
+        left join t_b_ding_apply d on d.sop_no=s.sop_no
+        left join sys_user du on du.id=d.create_id
+        <where>
+
+            <if test="serviceId != null and serviceId != ''">
+                and  s.service_id= #{serviceId}
+            </if>
+
+        </where>
+    </select>
+</mapper>

+ 25 - 0
sop-common/src/main/java/com/qmth/sop/common/enums/SopAnalyseGroupEnum.java

@@ -0,0 +1,25 @@
+package com.qmth.sop.common.enums;
+
+/**
+ * @Description: sop分组enum
+ * @Param:
+ * @return:
+ * @Author: dhshu
+ */
+public enum SopAnalyseGroupEnum {
+    CRM("项目"),
+
+    REGION("大区"),
+
+    SUPPLIER("供应商");
+
+    SopAnalyseGroupEnum(String title) {
+        this.title = title;
+    }
+
+    private final String title;
+
+    public String getTitle() {
+        return title;
+    }
+}

+ 25 - 0
sop-common/src/main/java/com/qmth/sop/common/enums/SopAnalyseSortEnum.java

@@ -0,0 +1,25 @@
+package com.qmth.sop.common.enums;
+
+/**
+ * @Description: sop分组enum
+ * @Param:
+ * @return:
+ * @Author: dhshu
+ */
+public enum SopAnalyseSortEnum {
+    PENDING("待处理"),
+
+    SLOWEST("处理最慢"),
+
+    FASTEST("处理最快");
+
+    SopAnalyseSortEnum(String title) {
+        this.title = title;
+    }
+
+    private final String title;
+
+    public String getTitle() {
+        return title;
+    }
+}