wangxy пре 1 недеља
родитељ
комит
bb61892b3c

+ 74 - 0
fs-admin/src/main/java/com/fs/company/controller/CompanyStatisticsController.java

@@ -14,6 +14,8 @@ import com.fs.company.service.ICompanySmsLogsService;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.company.service.ICompanyVoiceLogsService;
 import com.fs.company.vo.*;
+import com.fs.course.param.FsCourseWatchLogStatisticsListParam;
+import com.fs.course.service.IFsCourseWatchLogService;
 import com.fs.crm.param.CrmCustomerStatisticsParam;
 import com.fs.crm.service.ICrmCustomerService;
 import com.fs.crm.service.ICrmCustomerVisitService;
@@ -26,6 +28,7 @@ import com.fs.his.service.IFsPackageOrderService;
 import com.fs.his.service.IFsStoreAfterSalesService;
 import com.fs.his.service.IFsStoreOrderService;
 import com.fs.his.service.IFsStorePaymentService;
+import com.fs.his.vo.FsCourseReportVO;
 import com.fs.his.vo.FsOrderReportVO;
 import com.fs.his.vo.FsStoreOrderAmountScrmStatsVo;
 import com.fs.his.vo.FsStoreOrderAmountStatsVo;
@@ -80,6 +83,9 @@ public class CompanyStatisticsController extends BaseController {
     @Autowired
     private IFsPackageOrderService fsPackageOrderService;
 
+    @Autowired
+    private IFsCourseWatchLogService fsCourseWatchLogService;
+
     @GetMapping("/storeOrder")
     public R storeOrder(FsStoreStatisticsParam param) {
         if (StringUtils.isNotEmpty(param.getUserIds())) {
@@ -747,4 +753,72 @@ public class CompanyStatisticsController extends BaseController {
         return AjaxResult.success(result);
     }
 
+    /**
+     * 课程统计报表
+     *
+     * @param param
+     * @return
+     */
+    @GetMapping("/courseReport")
+    public AjaxResult selectFsCourseReportVO(FsCourseWatchLogStatisticsListParam param) {
+        List<FsCourseReportVO> fsCourseReportVOS = fsCourseWatchLogService.selectFsCourseReportVO(param);
+        // 创建合计对象
+        FsCourseReportVO totalVo = new FsCourseReportVO();
+        totalVo.setCompanyName("总计");
+
+        // 计算各项人数总和
+        totalVo.setAccessCount(fsCourseReportVOS.stream()
+                .mapToInt(FsCourseReportVO::getAccessCount)
+                .sum());
+
+        totalVo.setPendingCount(fsCourseReportVOS.stream()
+                .mapToInt(FsCourseReportVO::getPendingCount)
+                .sum());
+
+        totalVo.setWatchingCount(fsCourseReportVOS.stream()
+                .mapToInt(FsCourseReportVO::getWatchingCount)
+                .sum());
+
+        totalVo.setFinishedCount(fsCourseReportVOS.stream()
+                .mapToInt(FsCourseReportVO::getFinishedCount)
+                .sum());
+
+        totalVo.setInterruptedCount(fsCourseReportVOS.stream()
+                .mapToInt(FsCourseReportVO::getInterruptedCount)
+                .sum());
+
+        totalVo.setAnswerUserCount(fsCourseReportVOS.stream()
+                .mapToInt(FsCourseReportVO::getAnswerUserCount)
+                .sum());
+
+        totalVo.setPacketUserCount(fsCourseReportVOS.stream()
+                .mapToInt(FsCourseReportVO::getPacketUserCount)
+                .sum());
+
+        // 计算金额总和
+        totalVo.setPacketAmount(fsCourseReportVOS.stream()
+                .map(FsCourseReportVO::getPacketAmount)
+                .filter(Objects::nonNull)
+                .reduce(BigDecimal.ZERO, BigDecimal::add));
+
+        // 重新计算总体看课率和完课率
+        int totalCount = totalVo.getAccessCount();
+        if (totalCount > 0) {
+            BigDecimal watchRate = new BigDecimal((totalVo.getWatchingCount() + totalVo.getFinishedCount()) * 100.0 / totalCount)
+                    .setScale(2, BigDecimal.ROUND_HALF_UP);
+            totalVo.setWatchRate(watchRate);
+
+            BigDecimal finishRate = new BigDecimal(totalVo.getFinishedCount() * 100.0 / totalCount)
+                    .setScale(2, BigDecimal.ROUND_HALF_UP);
+            totalVo.setFinishRate(finishRate);
+        } else {
+            totalVo.setWatchRate(BigDecimal.ZERO);
+            totalVo.setFinishRate(BigDecimal.ZERO);
+        }
+
+        Map<String, Object> result = new HashMap<>();
+        result.put("data", fsCourseReportVOS);
+        result.put("total", totalVo);
+        return AjaxResult.success(result);
+    }
 }

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

@@ -5,6 +5,7 @@ import com.fs.course.domain.FsCourseWatchLog;
 import com.fs.course.dto.WatchLogDTO;
 import com.fs.course.param.*;
 import com.fs.course.vo.*;
+import com.fs.his.vo.FsCourseReportVO;
 import com.fs.qw.domain.QwExternalContact;
 import com.fs.qw.param.QwSidebarStatsParam;
 import com.fs.sop.vo.QwRatingVO;
@@ -558,4 +559,11 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
 
     // 统计当天各公司的观看人数和完播人数, 存到redis中,定时任务每 ? 分钟执行一次
     List<WatchCourseStatisticsResultDTO> watchCourseStatisticsGroupByCompany(@Param("params") Map<String, Object> params);
+
+    /**
+     * 看课统计报表
+     * @param param
+     * @return
+     */
+    List<FsCourseReportVO>  selectFsCourseReportVO(FsCourseWatchLogStatisticsListParam param);
 }

+ 10 - 0
fs-service/src/main/java/com/fs/course/param/FsCourseWatchLogStatisticsListParam.java

@@ -42,4 +42,14 @@ public class FsCourseWatchLogStatisticsListParam {
     private Long pageSize;
 
     private Integer sendType; //归属发送方式:1 个微  2 企微
+
+    /**
+     * 训练营id
+     */
+    private Long  trainingCampId;
+
+    /**
+     * 营期id
+     */
+    private  Long periodId;
 }

+ 8 - 0
fs-service/src/main/java/com/fs/course/service/IFsCourseWatchLogService.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.service.IService;
 import com.fs.course.domain.FsCourseWatchLog;
 import com.fs.course.param.*;
 import com.fs.course.vo.*;
+import com.fs.his.vo.FsCourseReportVO;
 import com.fs.qw.param.QwSidebarStatsParam;
 import com.fs.qw.vo.QwWatchLogStatisticsListVO;
 
@@ -139,4 +140,11 @@ public interface IFsCourseWatchLogService extends IService<FsCourseWatchLog> {
      * 看课统计
      * */
     List<FsCourseWatchLogStatisticsListVO> selectQwFsCourseWatchLogStatisticsListVO(QwSidebarStatsParam param);
+
+    /**
+     * 看课统计报表
+     * @param param
+     * @return
+     */
+    List<FsCourseReportVO> selectFsCourseReportVO(FsCourseWatchLogStatisticsListParam param);
 }

+ 6 - 0
fs-service/src/main/java/com/fs/course/service/IFsUserCourseTrainingCampService.java

@@ -2,6 +2,7 @@ package com.fs.course.service;
 
 
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.course.domain.FsUserCoursePeriod;
 import com.fs.course.domain.FsUserCourseTrainingCamp;
 import com.fs.course.dto.FsUserCourseTrainingCampDTO;
 import com.fs.course.vo.FsUserCourseTrainingCampVO;
@@ -49,4 +50,9 @@ public interface IFsUserCourseTrainingCampService extends IService<FsUserCourseT
      * @return  list
      */
     List<OptionsVO> selectCampListByMap(Map<String, Object> params);
+
+    /**
+     * 查询训练营的营期
+     */
+    List<FsUserCoursePeriod> selectPeriodListByTrainingCampId(Long trainingCampId);
 }

+ 7 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java

@@ -29,6 +29,7 @@ import com.fs.his.config.FsSysConfig;
 import com.fs.his.domain.FsUser;
 import com.fs.his.service.IFsUserService;
 import com.fs.his.utils.ConfigUtil;
+import com.fs.his.vo.FsCourseReportVO;
 import com.fs.qw.Bean.MsgBean;
 import com.fs.qw.cache.IQwExternalContactCacheService;
 import com.fs.qw.cache.IQwUserCacheService;
@@ -75,6 +76,7 @@ import java.util.stream.Collectors;
  * @date 2024-09-18
  */
 @Service
+
 public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMapper, FsCourseWatchLog> implements IFsCourseWatchLogService
 {
     private static final Logger log = LoggerFactory.getLogger(FsCourseWatchLogServiceImpl.class);
@@ -1281,5 +1283,10 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
         return fsCourseWatchLogMapper.selectQwFsCourseWatchLogStatisticsListVO(param);
     }
 
+    @Override
+    public List<FsCourseReportVO> selectFsCourseReportVO(FsCourseWatchLogStatisticsListParam param) {
+        return fsCourseWatchLogMapper.selectFsCourseReportVO(param);
+    }
+
 
 }

+ 5 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseTrainingCampServiceImpl.java

@@ -182,4 +182,9 @@ public class FsUserCourseTrainingCampServiceImpl extends ServiceImpl<FsUserCours
     public List<OptionsVO> selectCampListByMap(Map<String, Object> params) {
         return baseMapper.selectCampListByMap(params);
     }
+
+    @Override
+    public List<FsUserCoursePeriod> selectPeriodListByTrainingCampId(Long trainingCampId) {
+        return fsUserCoursePeriodMapper.selectFsUserCoursePeriodListByTrainingCampId(trainingCampId);
+    }
 }

+ 63 - 0
fs-service/src/main/java/com/fs/his/vo/FsCourseReportVO.java

@@ -2,6 +2,69 @@ package com.fs.his.vo;
 
 import lombok.Data;
 
+import java.math.BigDecimal;
+
 @Data
 public class FsCourseReportVO {
+
+    /** 公司名称 */
+
+    private String companyName;
+
+    /** 训练营名称 */
+    private  String trainingCampName;
+    /**
+     * 营期
+     */
+    private  String periodName;
+
+    /**
+     * 进线人数
+     */
+    private  Integer accessCount;
+
+    /**
+     * 待看课人数
+     */
+    private  Integer pendingCount;
+
+    /**
+     * 看课中人数
+     */
+    private  Integer watchingCount;
+
+    /**
+     * 完课人数
+     */
+    private  Integer finishedCount;
+
+    /**
+     * 看课中断人数
+     */
+    private  Integer interruptedCount;
+
+    /**
+     * 看课率
+     */
+    private BigDecimal watchRate;
+
+    /**
+     * 完课率
+     */
+    private   BigDecimal finishRate;
+
+    /**
+     * 答题人数
+     */
+    private  Integer answerUserCount;
+
+    /**
+     * 红包领取数
+     */
+    private  Integer packetUserCount;
+
+    /**
+     * 红包金额
+     */
+    private  BigDecimal packetAmount;
 }

+ 24 - 0
fs-service/src/main/java/com/fs/his/vo/FsUserReportVO.java

@@ -0,0 +1,24 @@
+package com.fs.his.vo;
+
+import lombok.Data;
+
+@Data
+public class FsUserReportVO {
+
+
+    private Long userId;
+    /**
+     * 昵称
+     */
+    private String nickName;
+
+    /** 1为正常,0为禁止 */
+    private Integer status;
+
+    /**
+     * 所属销售
+     */
+    private String companyUserName;
+
+
+}

+ 143 - 0
fs-service/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml

@@ -993,5 +993,148 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             o.company_id,
             o.send_type
     </select>
+    <select id="selectFsCourseReportVO" resultType="com.fs.his.vo.FsCourseReportVO">
+        SELECT
+        c.company_id AS companyId,
+        c.company_name AS companyName,
+
+        <!-- 看课统计数据 -->
+        COALESCE(watch.pending_count, 0) AS pendingCount,
+        COALESCE(watch.watching_count, 0) AS watchingCount,
+        COALESCE(watch.finished_count, 0) AS finishedCount,
+        COALESCE(watch.interrupted_count, 0) AS interruptedCount,
+        COALESCE(watch.today_access_count, 0) AS accessCount,
+
+        <!-- 计算比率(避免除零) -->
+        CASE
+        WHEN COALESCE(watch.today_access_count, 0) > 0
+        THEN ROUND(
+        (COALESCE(watch.watching_count, 0) + COALESCE(watch.finished_count, 0)) * 100.0 /
+        watch.today_access_count,
+        2
+        )
+        ELSE 0
+        END AS watchRate,
+
+        CASE
+        WHEN COALESCE(watch.today_access_count, 0) > 0
+        THEN ROUND(
+        COALESCE(watch.finished_count, 0) * 100.0 /
+        watch.today_access_count,
+        2
+        )
+        ELSE 0
+        END AS finishRate,
+
+        <!-- 答题人数 -->
+        COALESCE(answer.answer_user_count, 0) AS answerUserCount,
+
+        <!-- 红包统计 -->
+        COALESCE(packet.packet_user_count, 0) AS packetUserCount,
+        COALESCE(packet.packet_amount, 0) AS packetAmount
+
+        <!-- 动态判断是否查询营期信息 -->
+        <if test="trainingCampId != null">
+            ,(SELECT training_camp_name FROM fs_user_course_training_camp
+            WHERE training_camp_id = (SELECT training_camp_id FROM fs_user_course_period WHERE period_id = watch.sample_period_id LIMIT 1)
+            ) AS trainingCampName
+        </if>
+        <if test="periodId != null">
+           , (SELECT period_name FROM fs_user_course_period WHERE period_id = watch.sample_period_id LIMIT 1) AS periodName
+        </if>
+
+        FROM company c
+
+        <!-- 看课统计(一次性预聚合) -->
+        LEFT JOIN (
+        SELECT
+        company_id,
+        MAX(period_id) AS sample_period_id,
+        COUNT(DISTINCT CASE WHEN log_type = 3 THEN user_id END) AS pending_count,
+        COUNT(DISTINCT CASE WHEN log_type = 1 THEN user_id END) AS watching_count,
+        COUNT(DISTINCT CASE WHEN log_type = 2 THEN user_id END) AS finished_count,
+        COUNT(DISTINCT CASE WHEN log_type = 4 THEN user_id END) AS interrupted_count,
+        COUNT(DISTINCT CASE WHEN DATE(create_time) = CURDATE() THEN user_id END) AS today_access_count
+        FROM fs_course_watch_log
+        <where>
+            <if test="sTime != null and eTime != null">
+                AND create_time BETWEEN #{sTime} AND #{eTime}
+            </if>
+            <if test="trainingCampId != null">
+                AND period_id IN (SELECT period_id FROM fs_user_course_period WHERE training_camp_id = #{trainingCampId})
+            </if>
+            <if test="periodId !=null">
+                AND period_id =#{periodId}
+            </if>
+        </where>
+        GROUP BY company_id
+        ) AS watch ON c.company_id = watch.company_id
+
+        <!-- 答题统计(预聚合) -->
+        LEFT JOIN (
+        SELECT
+        w.company_id,
+        COUNT(DISTINCT a.user_id) AS answer_user_count
+        FROM fs_course_answer_logs a
+        INNER JOIN fs_course_watch_log w ON a.watch_log_id = w.log_id
+            <where>
+                <if test="sTime != null and eTime != null">
+                    AND w.create_time BETWEEN #{sTime} AND #{eTime}
+                </if>
+                <if test="trainingCampId != null">
+                    AND w.period_id IN (SELECT period_id FROM fs_user_course_period WHERE training_camp_id = #{trainingCampId})
+                </if>
+                <if test="periodId!=null">
+                    AND w.period_id = #{periodId}
+                </if>
+            </where>
+        GROUP BY w.company_id
+        ) AS answer ON c.company_id = answer.company_id
+
+        <!-- 红包统计(预聚合) -->
+        LEFT JOIN (
+        SELECT
+        company_id,
+        COUNT(DISTINCT user_id) AS packet_user_count,
+        COALESCE(SUM(amount), 0) AS packet_amount
+            FROM red_packet_log
+        <where>
+            <if test="sTime != null and eTime != null">
+                AND create_time BETWEEN #{sTime} AND #{eTime}
+            </if>
+        </where>
+        GROUP BY company_id
+        ) AS packet ON c.company_id = packet.company_id
+
+        <!-- 只显示有数据的公司 -->
+        WHERE watch.company_id IS NOT NULL
+
+        <!-- 公司筛选条件 -->
+
+         <if test="companyId != null and companyId != ''">
+            AND c.company_id =#{companyId}
+        </if>
+
+        <!-- 训练营筛选条件 -->
+        <if test="trainingCampId != null">
+            AND EXISTS (
+            SELECT 1 FROM fs_course_watch_log w2
+            INNER JOIN fs_user_course_period p ON w2.period_id = p.period_id
+            WHERE w2.company_id = c.company_id
+            AND p.training_camp_id = #{trainingCampId}
+            )
+        </if>
+
+        <if test="periodId != null">
+            AND EXISTS (
+            SELECT 1 FROM fs_course_watch_log w2
+            WHERE w2.company_id = c.company_id
+            AND w2.period_id = #{periodId}
+            )
+        </if>
+
+        ORDER BY COALESCE(watch.today_access_count, 0) DESC
+
+    </select>
 
 </mapper>