Browse Source

app统计报表和首页app统计修改

wangxy 4 days ago
parent
commit
43310d6b60

+ 2 - 2
fs-company/src/main/java/com/fs/company/controller/course/FsCourseWatchLogController.java

@@ -420,7 +420,7 @@ public class FsCourseWatchLogController extends BaseController
                 "salesName", "appUserCount", "newAppUserCount", "salesDept",
                 "salesCompany", "trainingCampName", "periodName", "videoTitle",
                 "finishedCount", "unfinishedCount", "completionRate",
-                "notWatchedCount", "notAnsweredCount", "redPacketAmount", "historyOrderCount"
+                "notWatchedCount", "notAnsweredCount","redPacketCount", "redPacketAmount", "historyOrderCount"
         );
 
         // 销售部门维度字段(去掉销售列,销售数放在销售部门后面)
@@ -428,7 +428,7 @@ public class FsCourseWatchLogController extends BaseController
                 "salesDept", "salesCount", "appUserCount", "newAppUserCount",
                 "salesCompany", "trainingCampName", "periodName", "videoTitle",
                 "finishedCount", "unfinishedCount", "completionRate",
-                "notWatchedCount", "notAnsweredCount", "redPacketAmount", "historyOrderCount"
+                "notWatchedCount", "notAnsweredCount","redPacketCount", "redPacketAmount", "historyOrderCount"
         );
 
         return "dept".equals(dimension) ? deptFields : salesFields;

+ 7 - 0
fs-service/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java

@@ -769,6 +769,13 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
      */
     List<WatchLogReportVO> selectCompanyBaseDataOptimized(FsCourseWatchLogStatisticsListParam param);
 
+    /**
+     * 销售端看课报表 公司维度合并查询(基础数据+统计数据)
+     * @param param
+     * @return
+     */
+    List<WatchLogReportVO> selectCompanyDeptStatsMerged(FsCourseWatchLogStatisticsListParam param);
+
     /**
      * 销售端看课报表 公司维度基础数据(带ID字段,用于assembleStatisticsData)
      * @param param

+ 101 - 150
fs-service/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java

@@ -1707,6 +1707,11 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
             //加密手机号
             param.setUserPhone(PhoneUtil.encryptPhone(param.getUserPhone()));
         }
+        if (param.getSTime() != null && param.getETime() != null) {
+            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
+            param.setStartDate(simpleDateFormat.format(param.getSTime()));
+            param.setEndDate(simpleDateFormat.format(param.getETime()));
+        }
         // 获取基础数据
         List<WatchLogReportVO> baseData = getBaseDataByDimension(param);
         if (CollectionUtils.isEmpty(baseData)) {
@@ -1786,6 +1791,7 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
             WatchLogReportVO redPacketStats = redPacketMap.getOrDefault(item.getLogId(), null);
             if (redPacketStats != null) {
                 item.setRedPacketAmount(redPacketStats.getRedPacketAmount());
+                item.setRedPacketCount(redPacketStats.getRedPacketCount());
             }
 
             // 订单数据
@@ -1862,12 +1868,12 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
         // 订单统计
         List<AppSalesWatchLogReportVO> orderStatsList = fsCourseWatchLogMapper.selectAppSalesOrderStats(param);
 
-        // 2. 查询营期信息
-        List<Long> periodIds = watchStatsList.stream()
-                .map(AppSalesWatchLogReportVO::getPeriodId)
-                .distinct()
-                .collect(Collectors.toList());
-        List<AppSalesWatchLogReportVO> campPeriodList = fsCourseWatchLogMapper.selectAppSalesCampPeriod(periodIds);
+//        // 2. 查询营期信息
+//        List<Long> periodIds = watchStatsList.stream()
+//                .map(AppSalesWatchLogReportVO::getPeriodId)
+//                .distinct()
+//                .collect(Collectors.toList());
+//        List<AppSalesWatchLogReportVO> campPeriodList = fsCourseWatchLogMapper.selectAppSalesCampPeriod(periodIds);
 
         // 3. 转换为Map便于查找
         // APP会员数统计按销售ID分组
@@ -1877,13 +1883,13 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
                         Function.identity(),
                         (e, r) -> e
                 ));
-        // 看课统计(已包含基础数据、答题、红包)按销售ID+营期ID+视频ID分组
-        Map<String, AppSalesWatchLogReportVO> watchStatsMap = watchStatsList.stream()
-                .collect(Collectors.toMap(
-                        item -> item.getSalesId() + "_" + item.getPeriodId() + "_" + item.getVideoId(),
-                        Function.identity(),
-                        (e, r) -> e
-                ));
+//        // 看课统计(已包含基础数据、答题、红包)按销售ID+营期ID+视频ID分组
+//        Map<String, AppSalesWatchLogReportVO> watchStatsMap = watchStatsList.stream()
+//                .collect(Collectors.toMap(
+//                        item -> item.getSalesId() + "_" + item.getPeriodId() + "_" + item.getVideoId(),
+//                        Function.identity(),
+//                        (e, r) -> e
+//                ));
         // 订单统计按销售ID+营期ID+视频ID分组
         Map<String, AppSalesWatchLogReportVO> orderStatsMap = orderStatsList.stream()
                 .collect(Collectors.toMap(
@@ -1891,12 +1897,12 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
                         Function.identity(),
                         (e, r) -> e
                 ));
-        Map<Long, AppSalesWatchLogReportVO> campPeriodMap = campPeriodList.stream()
-                .collect(Collectors.toMap(
-                        AppSalesWatchLogReportVO::getPeriodId,
-                        Function.identity(),
-                        (e, r) -> e
-                ));
+//        Map<Long, AppSalesWatchLogReportVO> campPeriodMap = campPeriodList.stream()
+//                .collect(Collectors.toMap(
+//                        AppSalesWatchLogReportVO::getPeriodId,
+//                        Function.identity(),
+//                        (e, r) -> e
+//                ));
 
         // 4. 组装数据
         for (AppSalesWatchLogReportVO vo : watchStatsList) {
@@ -1920,19 +1926,21 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
             } else {
                 vo.setCompletionRate(BigDecimal.ZERO);
             }
-
+            Integer answeredCount = vo.getAnsweredCount() != null ? vo.getAnsweredCount() : 0;
+            Integer finishedCount = vo.getFinishedCount() != null ? vo.getFinishedCount() : 0;
+            vo.setNotAnsweredCount(finishedCount - answeredCount);
             // 订单统计
             AppSalesWatchLogReportVO orderStats = orderStatsMap.get(key);
             if (orderStats != null) {
                 vo.setHistoryOrderCount(orderStats.getHistoryOrderCount());
             }
 
-            // 营期信息
-            AppSalesWatchLogReportVO campPeriod = campPeriodMap.get(vo.getPeriodId());
-            if (campPeriod != null) {
-                vo.setPeriodName(campPeriod.getPeriodName());
-                vo.setTrainingCampName(campPeriod.getTrainingCampName());
-            }
+//            // 营期信息
+//            AppSalesWatchLogReportVO campPeriod = campPeriodMap.get(vo.getPeriodId());
+//            if (campPeriod != null) {
+//                vo.setPeriodName(campPeriod.getPeriodName());
+//                vo.setTrainingCampName(campPeriod.getTrainingCampName());
+//            }
         }
 
         return watchStatsList;
@@ -1953,13 +1961,13 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
         List<AppSalesWatchLogReportVO> userStatsList = fsCourseWatchLogMapper.selectAppDeptUserStats(param);
         // 订单统计
         List<AppSalesWatchLogReportVO> orderStatsList = fsCourseWatchLogMapper.selectAppDeptOrderStats(param);
-
-        // 2. 查询营期信息
-        List<Long> periodIds = watchStatsList.stream()
-                .map(AppSalesWatchLogReportVO::getPeriodId)
-                .distinct()
-                .collect(Collectors.toList());
-        List<AppSalesWatchLogReportVO> campPeriodList = fsCourseWatchLogMapper.selectAppSalesCampPeriod(periodIds);
+//
+//        // 2. 查询营期信息
+//        List<Long> periodIds = watchStatsList.stream()
+//                .map(AppSalesWatchLogReportVO::getPeriodId)
+//                .distinct()
+//                .collect(Collectors.toList());
+//        List<AppSalesWatchLogReportVO> campPeriodList = fsCourseWatchLogMapper.selectAppSalesCampPeriod(periodIds);
 
         // 3. 转换为Map便于查找
         // APP会员数统计按部门ID分组
@@ -1969,13 +1977,6 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
                         Function.identity(),
                         (e, r) -> e
                 ));
-        // 看课统计(已包含基础数据、答题、红包)按部门ID+营期ID+视频ID分组
-        Map<String, AppSalesWatchLogReportVO> watchStatsMap = watchStatsList.stream()
-                .collect(Collectors.toMap(
-                        item -> item.getDeptId() + "_" + item.getPeriodId() + "_" + item.getVideoId(),
-                        Function.identity(),
-                        (e, r) -> e
-                ));
         // 订单统计按部门ID+营期ID+视频ID分组
         Map<String, AppSalesWatchLogReportVO> orderStatsMap = orderStatsList.stream()
                 .collect(Collectors.toMap(
@@ -1983,12 +1984,12 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
                         Function.identity(),
                         (e, r) -> e
                 ));
-        Map<Long, AppSalesWatchLogReportVO> campPeriodMap = campPeriodList.stream()
-                .collect(Collectors.toMap(
-                        AppSalesWatchLogReportVO::getPeriodId,
-                        Function.identity(),
-                        (e, r) -> e
-                ));
+//        Map<Long, AppSalesWatchLogReportVO> campPeriodMap = campPeriodList.stream()
+//                .collect(Collectors.toMap(
+//                        AppSalesWatchLogReportVO::getPeriodId,
+//                        Function.identity(),
+//                        (e, r) -> e
+//                ));
 
         // 4. 组装数据
         for (AppSalesWatchLogReportVO vo : watchStatsList) {
@@ -2015,18 +2016,22 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
                 vo.setCompletionRate(BigDecimal.ZERO);
             }
 
+            //计算未答题数
+            Integer answeredCount = vo.getAnsweredCount() != null ? vo.getAnsweredCount() : 0;
+            Integer finishedCount = vo.getFinishedCount() != null ? vo.getFinishedCount() : 0;
+            vo.setNotAnsweredCount(finishedCount - answeredCount);
             // 订单统计
             AppSalesWatchLogReportVO orderStats = orderStatsMap.get(key);
             if (orderStats != null) {
                 vo.setHistoryOrderCount(orderStats.getHistoryOrderCount());
             }
 
-            // 营期信息
-            AppSalesWatchLogReportVO campPeriod = campPeriodMap.get(vo.getPeriodId());
-            if (campPeriod != null) {
-                vo.setPeriodName(campPeriod.getPeriodName());
-                vo.setTrainingCampName(campPeriod.getTrainingCampName());
-            }
+//            // 营期信息
+//            AppSalesWatchLogReportVO campPeriod = campPeriodMap.get(vo.getPeriodId());
+//            if (campPeriod != null) {
+//                vo.setPeriodName(campPeriod.getPeriodName());
+//                vo.setTrainingCampName(campPeriod.getTrainingCampName());
+//            }
         }
 
         return watchStatsList;
@@ -2049,58 +2054,10 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
     }
 
     /**
-     * 优化后的公司维度基础数据查询
+     * 优化后的公司维度基础数据查询(合并查询版本)
      */
     private List<WatchLogReportVO> getCompanyBaseDataOptimized(FsCourseWatchLogStatisticsListParam param) {
-        //查询部门基础信息
-        List<WatchLogReportVO> deptBaseData = fsCourseWatchLogMapper.selectCompanyBaseDataOptimized(param);
-
-        if (CollectionUtils.isEmpty(deptBaseData)) {
-            return Collections.emptyList();
-        }
-
-//        //获取所有符合条件的部门ID
-//        List<Long> allDeptIds = fsCourseWatchLogMapper.selectAllDeptIds(param);
-        List<Long> allDeptIds = deptBaseData.stream().map(WatchLogReportVO::getDeptId).collect(Collectors.toList());
-        param.setDeptIds(allDeptIds);
-        //符合条件的营期Id
-        List<Long> periods = deptBaseData.stream().map(WatchLogReportVO::getPeriodId).collect(Collectors.toList());
-        param.setPeriodIds(periods);
-        if (CollectionUtils.isEmpty(allDeptIds)) {
-            return Collections.emptyList();
-        }
-//        FsCourseWatchLogStatisticsListParam allDeptsParam = new FsCourseWatchLogStatisticsListParam();
-//        // 复制原有参数
-//        copyProperties(param, allDeptsParam);
-//        allDeptsParam.setDeptIds(allDeptIds);
-
-        // 一次性查询所有部门的统计数据
-        List<WatchLogReportVO> allStats = fsCourseWatchLogMapper.selectWatchStatsByDeptBatch(param);
-
-        // 7. 将统计数据转换为Map便于后续查找
-        Map<String, WatchLogReportVO> statsMap = allStats.stream()
-                .collect(Collectors.toMap(
-                        item -> item.getDeptId() + "_" + item.getPeriodId(),
-                        Function.identity()
-                ));
-
-        // 7. 将统计数据合并到基础数据中
-        for (WatchLogReportVO item : deptBaseData) {
-            String key = item.getDeptId() + "_" + item.getPeriodId();
-            WatchLogReportVO stats = statsMap.getOrDefault(key, new WatchLogReportVO());
-            if (stats != null) {
-                item.setTotalLogCount(stats.getTotalLogCount());
-                item.setFinishedCount(stats.getFinishedCount());
-                item.setUnfinishedCount(stats.getUnfinishedCount());
-                item.setNotWatchedCount(stats.getNotWatchedCount());
-                item.setVideoTitle(stats.getVideoTitle());
-                item.setPeriodId(stats.getPeriodId());
-                item.setLogId(stats.getLogId());
-                item.setUserId(stats.getUserId());
-            }
-        }
-
-        return deptBaseData;
+        return fsCourseWatchLogMapper.selectCompanyDeptStatsMerged(param);
     }
 
     /**
@@ -2125,57 +2082,48 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
      * 组装统计数据
      */
     private List<WatchLogReportVO> assembleStatisticsData(List<WatchLogReportVO> baseData, FsCourseWatchLogStatisticsListParam param) {
-        // 准备查询条件
-        List<Long> periods = baseData.stream().map(WatchLogReportVO::getPeriodId).collect(Collectors.toList());
-        List<Long> logIds = baseData.stream().map(WatchLogReportVO::getLogId).collect(Collectors.toList());
-
-        List<Long> userIds = baseData.stream().map(WatchLogReportVO::getUserId).collect(Collectors.toList());
-
-
-        // 批量查询统计数据
-        //营期数据
-        Map<Long, WatchLogReportVO> perMap = convertCampPeriodToMap(fsCourseWatchLogMapper.selectCampPeriodByPeriod(periods));
-
-        Map<Long, WatchLogReportVO> redPacketMap = convertRedPacketToMap(
-                fsCourseWatchLogMapper.selectRedPacketStats(logIds)
-        );
-
-        Map<Long, WatchLogReportVO> orderMap = convertOrderToMap(
-                fsCourseWatchLogMapper.selectOrderStats(userIds, param)
-        );
-        Map<Long, WatchLogReportVO> answerMap = convertAnswerToMap(
-                fsCourseWatchLogMapper.selectAnswerStats(logIds)
-        );
-
+        String dimension = param.getDimension();
+        
+        // 只有 user 维度需要单独查询红包和答题数据
+        // sales 和 company 维度已经在基础数据 SQL 中计算了
+        if ("user".equals(dimension)) {
+            List<Long> logIds = baseData.stream().map(WatchLogReportVO::getLogId).collect(Collectors.toList());
+            List<Long> userIds = baseData.stream().map(WatchLogReportVO::getUserId).collect(Collectors.toList());
 
-        // 组装数据
-        for (WatchLogReportVO item : baseData) {
-            if (!"user".equals(param.getDimension())) {
-                //完课率
-                item.setCompletionRate(calculateCompletionRate(item));
-            }
-            // 营期数据
-            WatchLogReportVO watchStats = perMap.getOrDefault(item.getPeriodId(), null);
-            if (watchStats != null) {
-                item.setPeriodName(watchStats.getPeriodName());
-                item.setTrainingCampName(watchStats.getTrainingCampName());
-            }
-            // 红包数据
-            WatchLogReportVO redPacketStats = redPacketMap.getOrDefault(item.getLogId(), null);
-            if (redPacketStats != null) {
-                item.setRedPacketAmount(redPacketStats.getRedPacketAmount());
-            }
+            Map<Long, WatchLogReportVO> redPacketMap = convertRedPacketToMap(
+                    fsCourseWatchLogMapper.selectRedPacketStats(logIds)
+            );
+            Map<Long, WatchLogReportVO> answerMap = convertAnswerToMap(
+                    fsCourseWatchLogMapper.selectAnswerStats(logIds)
+            );
 
-            // 订单数据
-            WatchLogReportVO order = orderMap.getOrDefault(item.getUserId(), null);
-            if (order != null) {
-                item.setHistoryOrderCount(order.getHistoryOrderCount());
+            for (WatchLogReportVO item : baseData) {
+                WatchLogReportVO redPacketStats = redPacketMap.getOrDefault(item.getLogId(), null);
+                if (redPacketStats != null) {
+                    item.setRedPacketAmount(redPacketStats.getRedPacketAmount());
+                    item.setRedPacketCount(redPacketStats.getRedPacketCount());
+                }
+                WatchLogReportVO answer = answerMap.getOrDefault(item.getLogId(), null);
+                if (answer != null) {
+                    item.setAnswerStatus(answer.getAnswerStatus());
+                }
             }
+        }
 
-            // 答题数据
-            WatchLogReportVO answer = answerMap.getOrDefault(item.getLogId(), null);
-            if (answer != null) {
-                setAnswerData(item, answer, param.getDimension());
+        // 订单数据(所有维度都需要)
+        List<Long> userIds = baseData.stream().map(WatchLogReportVO::getUserId).distinct().collect(Collectors.toList());
+        if (!userIds.isEmpty()) {
+            Map<Long, WatchLogReportVO> orderMap = convertOrderToMap(
+                    fsCourseWatchLogMapper.selectOrderStats(userIds, param)
+            );
+            for (WatchLogReportVO item : baseData) {
+                WatchLogReportVO order = orderMap.getOrDefault(item.getUserId(), null);
+                if (order != null) {
+                    item.setHistoryOrderCount(order.getHistoryOrderCount());
+                }
+                if (!"user".equals(dimension)) {
+                    item.setCompletionRate(calculateCompletionRate(item));
+                }
             }
         }
         return baseData;
@@ -2299,7 +2247,10 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
      * 计算完成率
      */
     private BigDecimal calculateCompletionRate(WatchLogReportVO watchStats) {
-        if (watchStats.getTotalLogCount() == 0) {
+        if (watchStats == null || watchStats.getTotalLogCount() == null || watchStats.getTotalLogCount() == 0) {
+            return BigDecimal.ZERO;
+        }
+        if (watchStats.getFinishedCount() == null) {
             return BigDecimal.ZERO;
         }
         return BigDecimal.valueOf(watchStats.getFinishedCount() * 100.0 / watchStats.getTotalLogCount())

+ 23 - 10
fs-service/src/main/java/com/fs/his/mapper/FsUserMapper.java

@@ -90,17 +90,31 @@ public interface FsUserMapper
     List<FsUser> findDuplicatePhonesWithCount();
 
     /**
-     * 查询 APP 总用户数(source 不为空的用户)
+     * 查询 APP 总用户数(app_create_time 不为空的用户)
      * @return APP 总用户数
      */
-    @Select("SELECT COUNT(user_id) FROM fs_user WHERE source IS NOT NULL AND is_del = 0")
+    @Select("SELECT COUNT(DISTINCT c.company_id, ucu.user_id, ucu.company_user_id) " +
+            "FROM fs_user_company_user ucu " +
+            "INNER JOIN fs_user u ON ucu.user_id = u.user_id " +
+            "INNER JOIN company c ON ucu.company_id = c.company_id " +
+            "WHERE u.app_create_time IS NOT NULL " +
+            "  AND u.status = 1 " +
+            "  AND ucu.status IN (0, 1)")
     Long selectAppUserCount();
 
-    @Select("SELECT COUNT(user_id) \n" +
-            "FROM fs_user \n" +
-            "WHERE source IS NOT NULL \n" +
-            "  AND is_del = 0 \n" +
-            "  AND DATE(create_time) = CURDATE()")
+    /**
+     * 查询今日新增 APP 用户数
+     * @return 今日新增 APP 用户数
+     */
+    @Select("SELECT COUNT(DISTINCT c.company_id, ucu.user_id, ucu.company_user_id) " +
+            "FROM fs_user_company_user ucu " +
+            "INNER JOIN fs_user u ON ucu.user_id = u.user_id " +
+            "INNER JOIN company c ON ucu.company_id = c.company_id " +
+            "WHERE u.app_create_time IS NOT NULL " +
+            "  AND u.status = 1 " +
+            "  AND ucu.status IN (0, 1) " +
+            "  AND u.app_create_time >= CURDATE() " +
+            "  AND u.app_create_time < DATE_ADD(CURDATE(), INTERVAL 1 DAY)")
     Long selectAppNewUser();
 
     /**
@@ -564,15 +578,14 @@ public interface FsUserMapper
             "FROM fs_user_company_user ucu " +
             "INNER JOIN fs_user u ON ucu.user_id = u.user_id " +
             "INNER JOIN company c ON ucu.company_id = c.company_id " +
-            "WHERE u.source IS NOT NULL " +
-            "  AND u.source != '' " +
+            "WHERE u.app_create_time IS NOT NULL " +
             "  AND u.status = 1 " +
             "  AND ucu.status IN (0, 1) " +
             "<if test=\"param.companyId != null\"> " +
             "  AND c.company_id = #{param.companyId} " +
             "</if> " +
             "<if test=\"param.startDate != null and param.startDate != '' and param.endDate != null and param.endDate != ''\"> " +
-            "  AND DATE(u.create_time) &gt;= #{param.startDate} AND DATE(u.create_time) &lt;= #{param.endDate} " +
+            "  AND u.app_create_time &gt;= #{param.startDate} AND u.app_create_time &lt; CONCAT(#{param.endDate}, ' 23:59:59') " +
             "</if> " +
             "</script>"})
     List<AppUserCompanyDTO> selectAppUserListForActiveCount(@Param("param")FsCourseWatchLogStatisticsListParam param);

+ 7 - 0
fs-service/src/main/java/com/fs/his/vo/AppSalesWatchLogReportVO.java

@@ -59,6 +59,9 @@ public class AppSalesWatchLogReportVO {
     @Excel(name = "未看数")
     private Integer notWatchedCount;
 
+    /** 已答题数 */
+    private Integer answeredCount;
+
     /** 未答题数 */
     @Excel(name = "未答题数")
     private Integer notAnsweredCount;
@@ -67,6 +70,10 @@ public class AppSalesWatchLogReportVO {
     @Excel(name = "红包金额")
     private BigDecimal redPacketAmount;
 
+    /** 红包数 */
+    @Excel(name = "红包数")
+    private Integer redPacketCount;
+
     /** 历史疗法订单数 */
     @Excel(name = "历史疗法订单数")
     private Integer historyOrderCount;

+ 6 - 0
fs-service/src/main/java/com/fs/his/vo/AppWatchLogReportVO.java

@@ -118,6 +118,12 @@ public class AppWatchLogReportVO {
     @Excel(name = "红包金额")
     private BigDecimal redPacketAmount;
 
+    /**
+     * 红包数
+     */
+    @Excel(name = "红包数")
+    private  Integer redPacketCount;
+
     /**
      * 历史订单数
      */

+ 6 - 0
fs-service/src/main/java/com/fs/his/vo/WatchLogReportVO.java

@@ -147,6 +147,12 @@ public class WatchLogReportVO {
     @Excel(name = "历史疗法订单数")
     private  Integer historyOrderCount;
 
+    /**
+     * 红包数
+     */
+    @Excel(name = "红包数")
+    private  Integer redPacketCount;
+
     /**
      * 销售id
      */

+ 112 - 63
fs-service/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml

@@ -1255,7 +1255,7 @@ FROM
         LEFT JOIN company c ON log.company_id = c.company_id
         LEFT JOIN company_dept cd ON cu.dept_id = cd.dept_id
         LEFT JOIN fs_user_course_video cv ON log.video_id = cv.video_id
-        WHERE log.send_type =1
+        WHERE log.send_type =1 and log.watch_type = 2
         <include refid="commonConditions"/>
          <if test="orderSTime != null and orderETime != null">
             AND DATE(po.create_time) BETWEEN #{orderSTime} AND #{orderETime}
@@ -1267,7 +1267,6 @@ FROM
         SELECT
         log.user_id userId,
         log.company_user_id companyUserId,
-        log.period_id periodId,
         log.log_id logId,
         log.create_time courseTime,
         cu.nick_name as salesName,
@@ -1275,11 +1274,13 @@ FROM
         c.company_name AS salesCompany,
         COUNT( DISTINCT u.user_id ) AS userCount,
         COUNT( DISTINCT CASE WHEN u.STATUS = 1 THEN u.user_id END ) AS onlineUserCount,
-        cv.title videoTitle,
         COUNT(DISTINCT log.log_id) AS totalLogCount,
         COUNT(DISTINCT CASE WHEN log.log_type = '2' THEN log.log_id END) AS finishedCount,
         COUNT(DISTINCT CASE WHEN log.log_type IN ('1', '3', '4') THEN log.log_id END) AS unfinishedCount,
-        COUNT(DISTINCT log.log_id)-COUNT(DISTINCT log.user_id) AS notWatchedCount
+        COUNT(DISTINCT log.log_id)-COUNT(DISTINCT log.user_id) AS notWatchedCount,
+        COUNT(DISTINCT log.log_id) - COUNT(DISTINCT a.watch_log_id) AS notAnsweredCount,
+        COALESCE(SUM(rp.amount), 0) AS redPacketAmount,
+        COUNT(DISTINCT rp.log_id) AS redPacketCount
         FROM
         fs_course_watch_log log
         <if test="orderSTime != null and orderETime != null">
@@ -1289,10 +1290,11 @@ FROM
         LEFT JOIN fs_user u on log.user_id= u.user_id
         LEFT JOIN company_dept d ON cu.dept_id = d.dept_id
         LEFT JOIN company c ON d.company_id = c.company_id
-        LEFT JOIN fs_user_course_video cv ON log.video_id = cv.video_id
-        WHERE log.send_type =1
-        <if test="sTime != null and eTime != null">
-            AND  DATE(log.create_time) BETWEEN #{sTime} AND #{eTime}
+        LEFT JOIN fs_course_answer_logs a ON a.watch_log_id = log.log_id
+        LEFT JOIN fs_course_red_packet_log rp ON rp.watch_log_id = log.log_id
+        WHERE log.send_type =1 and log.watch_type =2
+        <if test="startDate != null and startDate != '' and endDate != null and endDate != ''">
+            AND log.create_time &gt;= #{startDate} AND log.create_time &lt; DATE_ADD(#{endDate}, INTERVAL 1 DAY)
         </if>
         <if test="orderSTime != null and orderETime != null">
             AND DATE(po.create_time) BETWEEN #{orderSTime} AND #{orderETime}
@@ -1456,7 +1458,8 @@ FROM
     <select id="selectRedPacketStats" resultType="com.fs.his.vo.WatchLogReportVO">
         SELECT
         rp.watch_log_id  as  logId,
-        SUM(rp.amount) AS redPacketAmount
+        SUM(rp.amount) AS redPacketAmount,
+        COUNT( rp.log_id) AS redPacketCount
         FROM fs_course_red_packet_log rp
         WHERE  rp.watch_log_id IN
         <foreach collection="logIds" item="logId" open="(" separator="," close=")">
@@ -1771,7 +1774,6 @@ FROM
     <select id="selectCompanyBaseDataOptimized" resultType="com.fs.his.vo.WatchLogReportVO">
         SELECT
         d.dept_id AS deptId,
-        log.period_id periodId,
         d.dept_name AS salesDept,
         c.company_name AS salesCompany,
         COUNT(DISTINCT cu.user_id) AS salesCount,
@@ -1782,9 +1784,9 @@ FROM
         LEFT JOIN fs_user u ON log.user_id = u.user_id
         LEFT JOIN company_dept d ON cu.dept_id = d.dept_id
         LEFT JOIN company c ON d.company_id = c.company_id
-        WHERE log.send_type = 1
+        WHERE log.send_type = 1 and log.watch_type = 2
         <if test="sTime != null and eTime != null">
-            AND DATE(log.create_time) BETWEEN #{sTime} AND #{eTime}
+            AND log.create_time &gt;= #{sTime} AND log.create_time &lt; DATE_ADD(#{eTime}, INTERVAL 1 DAY)
         </if>
         <!-- 销售公司 -->
         <if test="companyId != null and companyId != ''">
@@ -1817,8 +1819,63 @@ FROM
         <if test="nickName != null and nickName != ''">
             AND u.nick_name LIKE CONCAT('%', #{nickName}, '%')
         </if>
-        GROUP BY d.dept_id, d.dept_name,log.period_id, c.company_name
+        GROUP BY d.dept_id
     </select>
+
+    <!-- 公司维度合并查询(基础数据+统计数据) -->
+    <select id="selectCompanyDeptStatsMerged" resultType="com.fs.his.vo.WatchLogReportVO">
+        SELECT
+            d.dept_id AS deptId,
+            d.dept_name AS salesDept,
+            c.company_name AS salesCompany,
+            COUNT(DISTINCT cu.user_id) AS salesCount,
+            COUNT(DISTINCT u.user_id) AS userCount,
+            COUNT(DISTINCT CASE WHEN u.STATUS = 1 THEN u.user_id END) AS onlineUserCount,
+            COUNT(DISTINCT log.log_id) AS totalLogCount,
+            COUNT(DISTINCT CASE WHEN log.log_type = '2' THEN log.log_id END) AS finishedCount,
+            COUNT(DISTINCT CASE WHEN log.log_type IN ('1', '3', '4') THEN log.log_id END) AS unfinishedCount,
+            COUNT(DISTINCT log.user_id) - COUNT(DISTINCT CASE WHEN log.log_type = '2' THEN log.user_id END) AS notWatchedCount,
+            COUNT(DISTINCT log.log_id) - COUNT(DISTINCT a.watch_log_id) AS notAnsweredCount,
+            COALESCE(SUM(rp.amount), 0) AS redPacketAmount,
+            COUNT(DISTINCT rp.log_id) AS redPacketCount
+        FROM fs_course_watch_log log
+        LEFT JOIN company_user cu ON log.company_user_id = cu.user_id
+        LEFT JOIN fs_user u ON log.user_id = u.user_id
+        LEFT JOIN company_dept d ON cu.dept_id = d.dept_id
+        LEFT JOIN company c ON d.company_id = c.company_id
+        LEFT JOIN fs_course_answer_logs a ON a.watch_log_id = log.log_id
+        LEFT JOIN fs_course_red_packet_log rp ON rp.watch_log_id = log.log_id
+        WHERE log.send_type = 1 AND log.watch_type = 2
+        <if test="startDate != null and startDate != '' and endDate != null and endDate != ''">
+            AND log.create_time &gt;= #{startDate} AND log.create_time &lt; DATE_ADD(#{endDate}, INTERVAL 1 DAY)
+        </if>
+        <if test="companyId != null and companyId != ''">
+            AND c.company_id = #{companyId}
+        </if>
+        <if test="deptId != null and deptId != ''">
+            AND d.dept_id = #{deptId}
+        </if>
+        <if test="trainingCampId != null and trainingCampId != ''">
+            AND log.period_id IN (SELECT period_id FROM fs_user_course_period WHERE training_camp_id = #{trainingCampId})
+        </if>
+        <if test="periodId != null and periodId != ''">
+            AND log.period_id = #{periodId}
+        </if>
+        <if test="userId != null and userId != ''">
+            AND log.user_id = #{userId}
+        </if>
+        <if test="userPhone != null and userPhone != ''">
+            AND u.phone LIKE CONCAT('%', #{userPhone}, '%')
+        </if>
+        <if test="project != null and project != ''">
+            AND log.project = #{project}
+        </if>
+        <if test="nickName != null and nickName != ''">
+            AND u.nick_name LIKE CONCAT('%', #{nickName}, '%')
+        </if>
+        GROUP BY d.dept_id
+    </select>
+
     <select id="selectAllDeptIds" resultType="java.lang.Long">
         SELECT DISTINCT d.dept_id
         FROM fs_course_watch_log log
@@ -1866,31 +1923,24 @@ FROM
         SELECT
         d.dept_id AS deptId,
         log.user_id AS userId,
-        log.period_id AS periodId,
         log.log_id AS logId,
-        cv.title videoTitle,
         COUNT(DISTINCT log.log_id) AS totalLogCount,
         COUNT(DISTINCT CASE WHEN log.log_type = '2' THEN log.log_id END) AS finishedCount,
         COUNT(DISTINCT CASE WHEN log.log_type IN ('1', '3', '4') THEN log.log_id END) AS unfinishedCount,
         COUNT(DISTINCT log.user_id) - COUNT(DISTINCT CASE WHEN log.log_type = '2' THEN log.user_id END) AS notWatchedCount
         FROM fs_course_watch_log log
-        LEFT JOIN fs_user_course_video cv ON log.video_id = cv.video_id
         <if test="orderSTime != null and orderETime != null">
             LEFT JOIN fs_package_order po ON po.user_id = log.user_id
         </if>
         LEFT JOIN company_user cu ON log.company_user_id = cu.user_id
         LEFT JOIN company_dept d ON cu.dept_id = d.dept_id
-        WHERE log.send_type = 1
+        WHERE log.send_type = 1 and log.watch_type = 2
         AND d.dept_id IN
         <foreach collection="deptIds" item="deptId" open="(" separator="," close=")">
             #{deptId}
         </foreach>
-        and  log.period_id in
-        <foreach collection="periodIds" item="periodId" open="(" separator="," close=")">
-            #{periodId}
-        </foreach>
         <if test="sTime != null and eTime != null">
-            AND DATE(log.create_time) BETWEEN #{sTime} AND #{eTime}
+            AND log.create_time &gt;= #{sTime} AND log.create_time &lt; DATE_ADD(#{eTime}, INTERVAL 1 DAY)
         </if>
         <if test="orderSTime != null and orderETime != null">
             AND DATE(po.create_time) BETWEEN #{orderSTime} AND #{orderETime}
@@ -1911,7 +1961,7 @@ FROM
         <if test="userId != null and userId != ''">
             AND log.user_id = #{userId}
         </if>
-        GROUP BY d.dept_id,log.period_id
+        GROUP BY d.dept_id
     </select>
     <select id="selectCompanyBaseDataWithIds" resultType="com.fs.his.vo.WatchLogReportVO">
         SELECT
@@ -2143,21 +2193,22 @@ FROM
     <select id="selectAppSalesUserStats" resultType="com.fs.his.vo.AppSalesWatchLogReportVO">
         SELECT
             cu.user_id AS salesId,
-            COUNT(DISTINCT CASE WHEN u.source IS NOT NULL THEN u.user_id END) AS appUserCount,
+            COUNT(DISTINCT CASE WHEN u.app_create_time IS NOT NULL THEN u.user_id END) AS appUserCount,
             <choose>
                 <when test="startDate != null and startDate != '' and endDate != null and endDate != ''">
-                    COUNT(DISTINCT CASE WHEN u.source IS NOT NULL AND u.register_date &gt;= #{startDate} AND u.register_date &lt; DATE_ADD(#{endDate}, INTERVAL 1 DAY) THEN u.user_id END)
+                    COUNT(DISTINCT CASE WHEN u.app_create_time IS NOT NULL AND u.app_create_time &gt;= #{startDate} AND u.app_create_time &lt; CONCAT(#{endDate}, ' 23:59:59') THEN u.user_id END)
                 </when>
                 <otherwise>
-                    COUNT(DISTINCT CASE WHEN u.source IS NOT NULL THEN u.user_id END)
+                    COUNT(DISTINCT CASE WHEN u.app_create_time IS NOT NULL THEN u.user_id END)
                 </otherwise>
             </choose> AS newAppUserCount
         FROM fs_user u
-        LEFT JOIN fs_user_company_user cuu ON cuu.user_id = u.user_id
-        LEFT JOIN company_user cu ON cuu.company_user_id = cu.user_id
-        LEFT JOIN company c ON cuu.company_id = c.company_id
-        LEFT JOIN company_dept cd ON cu.dept_id = cd.dept_id
-        WHERE u.source IS NOT NULL
+        INNER JOIN fs_user_company_user cuu ON cuu.user_id = u.user_id
+        INNER JOIN company_user cu ON cuu.company_user_id = cu.user_id
+        INNER JOIN company c ON cuu.company_id = c.company_id
+        WHERE u.app_create_time IS NOT NULL
+          AND u.status = 1
+          AND cuu.status IN (0, 1)
         <if test="companyId != null and companyId != ''">
             AND cuu.company_id = #{companyId}
         </if>
@@ -2174,23 +2225,21 @@ FROM
     </select>
 
     <!-- 销售维度基础数据+看课统计(合并查询) -->
-    <select id="selectAppSalesWatchStats" resultType="com.fs.his.vo.AppSalesWatchLogReportVO">
+        <select id="selectAppSalesWatchStats" resultType="com.fs.his.vo.AppSalesWatchLogReportVO">
         SELECT
             cu.user_id AS salesId,
             cu.nick_name AS salesName,
             cd.dept_name AS salesDept,
             c.company_name AS salesCompany,
-            log.period_id AS periodId,
-            log.video_id AS videoId,
-            cv.title AS videoTitle,
             cd.dept_id AS deptId,
             c.company_id AS companyId,
-            COUNT(DISTINCT CASE WHEN log.log_type = '2' THEN log.log_id END) AS finishedCount,
-            COUNT(DISTINCT CASE WHEN log.log_type = '1' THEN log.log_id END) AS unfinishedCount,
-            COUNT(DISTINCT CASE WHEN log.log_type = '3' THEN log.log_id END) AS notWatchedCount,
-            COUNT(DISTINCT CASE WHEN a.log_id IS NULL THEN log.log_id END) AS notAnsweredCount,
-            COALESCE(SUM(rpl.amount), 0) AS redPacketAmount
-        FROM fs_course_watch_log log
+            COUNT( CASE WHEN log.log_type = 2 THEN log.log_id END ) AS finishedCount,
+            COUNT(log.log_id)- COUNT(CASE WHEN log.log_type = 2 THEN log.log_id END )AS unfinishedCount,
+            COUNT( CASE WHEN log.log_type = 3 THEN log.log_id END ) AS notWatchedCount,
+            COUNT( a.watch_log_id ) answeredCount,
+            COALESCE ( SUM( rpl.amount ), 0 ) AS redPacketAmount,
+            COUNT( rpl.log_id) AS packetUserCount
+            FROM fs_course_watch_log log
         LEFT JOIN company_user cu ON log.company_user_id = cu.user_id
         LEFT JOIN company c ON log.company_id = c.company_id
         LEFT JOIN company_dept cd ON cu.dept_id = cd.dept_id
@@ -2200,7 +2249,7 @@ FROM
         WHERE log.send_type = 1
         AND log.watch_type = 1
         <include refid="commonConditions"/>
-        GROUP BY cu.user_id, log.period_id, log.video_id
+        GROUP BY cu.user_id
         ORDER BY cu.user_id
     </select>
 
@@ -2241,22 +2290,24 @@ FROM
     <select id="selectAppDeptUserStats" resultType="com.fs.his.vo.AppSalesWatchLogReportVO">
         SELECT
             cd.dept_id AS deptId,
-            COUNT(DISTINCT CASE WHEN u.source IS NOT NULL THEN u.user_id END) AS appUserCount,
+            COUNT(DISTINCT CASE WHEN u.app_create_time IS NOT NULL THEN u.user_id END) AS appUserCount,
             <choose>
                 <when test="startDate != null and startDate != '' and endDate != null and endDate != ''">
-                    COUNT(DISTINCT CASE WHEN u.source IS NOT NULL AND u.register_date &gt;= #{startDate} AND u.register_date &lt; DATE_ADD(#{endDate}, INTERVAL 1 DAY) THEN u.user_id END)
+                    COUNT(DISTINCT CASE WHEN u.app_create_time IS NOT NULL AND u.app_create_time &gt;= #{startDate} AND u.app_create_time &lt; CONCAT(#{endDate}, ' 23:59:59') THEN u.user_id END)
                 </when>
                 <otherwise>
-                    COUNT(DISTINCT CASE WHEN u.source IS NOT NULL THEN u.user_id END)
+                    COUNT(DISTINCT CASE WHEN u.app_create_time IS NOT NULL THEN u.user_id END)
                 </otherwise>
             </choose> AS newAppUserCount,
             COUNT(DISTINCT cu.user_id) AS salesCount
         FROM fs_user u
-        LEFT JOIN fs_user_company_user cuu ON cuu.user_id = u.user_id
-        LEFT JOIN company_user cu ON cuu.company_user_id = cu.user_id
-        LEFT JOIN company c ON cuu.company_id = c.company_id
-        LEFT JOIN company_dept cd ON cu.dept_id = cd.dept_id
-        WHERE u.source IS NOT NULL
+        INNER JOIN fs_user_company_user cuu ON cuu.user_id = u.user_id
+        INNER JOIN company_user cu ON cuu.company_user_id = cu.user_id
+        INNER JOIN company c ON cuu.company_id = c.company_id
+        INNER JOIN company_dept cd ON cu.dept_id = cd.dept_id
+        WHERE u.app_create_time IS NOT NULL
+          AND u.status = 1
+          AND cuu.status IN (0, 1)
         <if test="companyId != null and companyId != ''">
             AND cuu.company_id = #{companyId}
         </if>
@@ -2275,18 +2326,16 @@ FROM
     <!-- 销售部门维度基础数据+看课统计(合并查询) -->
     <select id="selectAppDeptWatchStats" resultType="com.fs.his.vo.AppSalesWatchLogReportVO">
         SELECT
-            cd.dept_id AS deptId,
-            cd.dept_name AS salesDept,
-            c.company_name AS salesCompany,
-            log.period_id AS periodId,
-            log.video_id AS videoId,
-            cv.title AS videoTitle,
-            c.company_id AS companyId,
-            COUNT(DISTINCT CASE WHEN log.log_type = '2' THEN log.log_id END) AS finishedCount,
-            COUNT(DISTINCT CASE WHEN log.log_type = '1' THEN log.log_id END) AS unfinishedCount,
-            COUNT(DISTINCT CASE WHEN log.log_type = '3' THEN log.log_id END) AS notWatchedCount,
-            COUNT(DISTINCT CASE WHEN a.log_id IS NULL THEN log.log_id END) AS notAnsweredCount,
-            COALESCE(SUM(rpl.amount), 0) AS redPacketAmount
+        cd.dept_id AS deptId,
+        cd.dept_name AS salesDept,
+        c.company_name AS salesCompany,
+        c.company_id AS companyId,
+        COUNT( CASE WHEN log.log_type = 2 THEN log.log_id END ) AS finishedCount,
+        COUNT(log.log_id)- COUNT(CASE WHEN log.log_type = 2 THEN log.log_id END )AS unfinishedCount,
+        COUNT( CASE WHEN log.log_type = 3 THEN log.log_id END ) AS notWatchedCount,
+        COUNT( a.watch_log_id ) anscount,
+        COALESCE ( SUM( rpl.amount ), 0 ) AS redPacketAmount,
+        COUNT( rpl.log_id) AS packetUserCount
         FROM fs_course_watch_log log
         LEFT JOIN company_user cu ON log.company_user_id = cu.user_id
         LEFT JOIN company c ON log.company_id = c.company_id
@@ -2297,7 +2346,7 @@ FROM
         WHERE log.send_type = 1
         AND log.watch_type = 1
         <include refid="commonConditions"/>
-        GROUP BY cd.dept_id, log.period_id, log.video_id
+        GROUP BY cd.dept_id
         ORDER BY cd.dept_id
     </select>