Преглед на файлове

管理端营期数据统计功能完成

caoliqin преди 1 месец
родител
ревизия
43778f1f72

+ 10 - 0
fs-admin/src/main/java/com/fs/course/controller/FsUserCoursePeriodController.java

@@ -7,9 +7,11 @@ import com.fs.course.domain.FsUserCoursePeriodDays;
 import com.fs.course.domain.FsUserCourseVideoRedPackage;
 import com.fs.course.param.CompanyRedPacketParam;
 import com.fs.course.param.FsBatchPeriodRedPackageParam;
+import com.fs.course.param.PeriodCountParam;
 import com.fs.course.param.PeriodRedPacketParam;
 import com.fs.course.service.IFsUserCoursePeriodDaysService;
 import com.fs.course.service.IFsUserCourseVideoRedPackageService;
+import com.fs.course.vo.FsPeriodCountVO;
 import com.fs.course.vo.FsUserCoursePeriodVO;
 import io.swagger.annotations.ApiOperation;
 import lombok.AllArgsConstructor;
@@ -178,4 +180,12 @@ public class FsUserCoursePeriodController extends BaseController {
         return R.ok();
     }
 
+    @PostMapping("/periodCount")
+    @ApiOperation("营期统计")
+    public TableDataInfo periodCourseCount(@RequestBody PeriodCountParam param) {
+        startPage();
+        List<FsPeriodCountVO> list = fsUserCoursePeriodDaysService.periodCourseCount(param);
+        return getDataTable(list);
+    }
+
 }

+ 31 - 0
fs-service-system/src/main/java/com/fs/course/param/PeriodCountParam.java

@@ -0,0 +1,31 @@
+package com.fs.course.param;
+
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 营期统计入参
+ */
+@Data
+public class PeriodCountParam implements Serializable {
+
+    @ApiModelProperty(value = "页码,默认为1", required = true)
+    private Integer pageNum = 1;
+
+    @ApiModelProperty(value = "页大小,默认为10", required = true)
+    private Integer pageSize = 10;
+
+    @ApiModelProperty(value = "营期id")
+    @NotBlank
+    private Long periodId;
+
+    @ApiModelProperty(value = "视频id")
+    private List<Long> videoIdList;
+
+}
+

+ 10 - 0
fs-service-system/src/main/java/com/fs/course/service/IFsUserCoursePeriodDaysService.java

@@ -5,7 +5,10 @@ import com.baomidou.mybatisplus.extension.service.IService;
 import com.fs.common.core.domain.R;
 import com.fs.course.domain.FsUserCoursePeriodDays;
 import com.fs.course.param.CompanyRedPacketParam;
+import com.fs.course.param.PeriodCountParam;
 import com.fs.course.param.PeriodRedPacketParam;
+import com.fs.course.vo.FsPeriodCountVO;
+import com.fs.course.vo.newfs.FsCourseAnalysisCountVO;
 
 /**
  * 营期课程Service接口
@@ -81,4 +84,11 @@ public interface IFsUserCoursePeriodDaysService extends IService<FsUserCoursePer
      * @return
      */
     List<PeriodRedPacketParam> getPeriodRedPacketList(Long periodId, Long companyId);
+
+    /**
+     * 根据营期按课程统计
+     * @param param
+     * @return
+     */
+    List<FsPeriodCountVO> periodCourseCount(PeriodCountParam param);
 }

+ 76 - 0
fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCoursePeriodDaysServiceImpl.java

@@ -1,8 +1,12 @@
 package com.fs.course.service.impl;
 
+import java.math.BigDecimal;
+import java.math.BigInteger;
 import java.util.Date;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
@@ -14,8 +18,14 @@ import com.fs.common.utils.date.TimeTypeEnum;
 import com.fs.course.domain.FsUserCoursePeriod;
 import com.fs.course.mapper.FsUserCoursePeriodMapper;
 import com.fs.course.param.CompanyRedPacketParam;
+import com.fs.course.param.PeriodCountParam;
 import com.fs.course.param.PeriodRedPacketParam;
+import com.fs.course.vo.FsPeriodCountVO;
+import com.fs.course.vo.newfs.FsCourseAnalysisCountVO;
+import com.fs.store.mapper.FsUserMapper;
+import com.fs.store.param.h5.CourseAnalysisParam;
 import lombok.AllArgsConstructor;
+import org.springframework.beans.BeanUtils;
 import org.springframework.stereotype.Service;
 import com.fs.course.mapper.FsUserCoursePeriodDaysMapper;
 import com.fs.course.domain.FsUserCoursePeriodDays;
@@ -32,6 +42,9 @@ import com.fs.course.service.IFsUserCoursePeriodDaysService;
 public class FsUserCoursePeriodDaysServiceImpl extends ServiceImpl<FsUserCoursePeriodDaysMapper, FsUserCoursePeriodDays> implements IFsUserCoursePeriodDaysService {
 
     private final FsUserCoursePeriodMapper fsUserCoursePeriodMapper;
+
+    private final FsUserMapper  fsUserMapper;
+
     /**
      * 查询营期课程
      *
@@ -157,4 +170,67 @@ public class FsUserCoursePeriodDaysServiceImpl extends ServiceImpl<FsUserCourseP
     public List<PeriodRedPacketParam> getPeriodRedPacketList(Long periodId, Long companyId) {
         return fsUserCoursePeriodMapper.selectPeriodRedPacket(periodId, companyId);
     }
+
+    @Override
+    public List<FsPeriodCountVO> periodCourseCount(PeriodCountParam param) {
+        //1、查询课程视频信息
+        FsUserCoursePeriodDays fsUserCoursePeriodDays = new FsUserCoursePeriodDays();
+        fsUserCoursePeriodDays.setPeriodId(param.getPeriodId());
+        fsUserCoursePeriodDays.setVideoIds(param.getVideoIdList());
+
+        List<FsUserCoursePeriodDays> videoList = baseMapper.selectFsUserCoursePeriodDaysList(fsUserCoursePeriodDays);
+
+        //2、查询统计
+        CourseAnalysisParam courseAnalysisParam = new CourseAnalysisParam();
+        courseAnalysisParam.setPeriodId(param.getPeriodId());
+        courseAnalysisParam.setVideoIdList(param.getVideoIdList());
+        List<FsCourseAnalysisCountVO> courseCountList = fsUserMapper.courseAnalysisWatchLog(courseAnalysisParam);
+        List<FsCourseAnalysisCountVO> redPacketCountList = fsUserMapper.courseAnalysisRedPacketCount(courseAnalysisParam);
+        List<FsCourseAnalysisCountVO> answerCountList = fsUserMapper.courseAnalysisAnswerCount(courseAnalysisParam);
+
+        //3、转化为map
+        Map<Long, FsCourseAnalysisCountVO> courseMap = courseCountList.stream()
+                .collect(Collectors.toMap(
+                        FsCourseAnalysisCountVO::getVideoId,
+                        Function.identity()
+                ));
+        Map<Long, FsCourseAnalysisCountVO> redPacketMap = redPacketCountList.stream()
+                .collect(Collectors.toMap(
+                        FsCourseAnalysisCountVO::getVideoId,
+                        Function.identity()
+                ));
+        Map<Long, FsCourseAnalysisCountVO> answerMap = answerCountList.stream()
+                .collect(Collectors.toMap(
+                        FsCourseAnalysisCountVO::getVideoId,
+                        Function.identity()
+                ));
+
+        //4、处理数据
+        return videoList.stream().map(v -> {
+            FsPeriodCountVO allVO = new FsPeriodCountVO();
+            BeanUtils.copyProperties(v, allVO);
+            allVO.setTitle(v.getVideoName());
+
+            FsCourseAnalysisCountVO countVO = getCourseAnalysisCountVO(v, courseMap, redPacketMap, answerMap);
+            allVO.setCountDetailsVO(countVO);
+            return allVO;
+        }).collect(Collectors.toList());
+    }
+
+    private static FsCourseAnalysisCountVO getCourseAnalysisCountVO(FsUserCoursePeriodDays v, Map<Long, FsCourseAnalysisCountVO> courseMap, Map<Long, FsCourseAnalysisCountVO> redPacketMap, Map<Long, FsCourseAnalysisCountVO> answerMap) {
+        FsCourseAnalysisCountVO countVO = new FsCourseAnalysisCountVO();
+        FsCourseAnalysisCountVO courseVO = courseMap.getOrDefault(v.getVideoId(), countVO);
+        FsCourseAnalysisCountVO redPacketVO = redPacketMap.getOrDefault(v.getVideoId(), countVO);
+        FsCourseAnalysisCountVO answerVO = answerMap.getOrDefault(v.getVideoId(), countVO);
+        //单独赋值
+        countVO.setVideoId(v.getVideoId()).setCourseWatchNum(courseVO.getCourseWatchNum()).setCourseCompleteNum(courseVO.getCourseWatchNum())
+                .setCompleteRate(courseVO.getCompleteRate() != null ? courseVO.getCompleteRate() : new BigDecimal(BigInteger.ZERO))
+                .setCourseWatchTimes(courseVO.getCourseWatchTimes()).setCourseCompleteTimes(courseVO.getCourseCompleteTimes());
+        countVO.setRedPacketNum(redPacketVO.getRedPacketNum())
+                .setRedPacketAmount(redPacketVO.getRedPacketAmount() != null ? redPacketVO.getRedPacketAmount() : new BigDecimal(BigInteger.ZERO));
+        countVO.setAnswerNum(answerVO.getAnswerNum()).setAnswerRightNum(answerVO.getAnswerRightNum())
+                .setAnswerRightRate(answerVO.getAnswerRightRate()!=null ? answerVO.getAnswerRightRate() : new BigDecimal(BigInteger.ZERO))
+                .setAnswerTimes(answerVO.getAnswerTimes()).setAnswerRightTimes(answerVO.getAnswerRightTimes());
+        return countVO;
+    }
 }

+ 27 - 0
fs-service-system/src/main/java/com/fs/course/vo/FsPeriodCountVO.java

@@ -0,0 +1,27 @@
+package com.fs.course.vo;
+
+import com.fs.course.vo.newfs.FsCourseAnalysisCountVO;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel
+public class FsPeriodCountVO {
+
+    @ApiModelProperty(value = "课程id")
+    private Long courseId;
+
+    @ApiModelProperty(value = "课程名称")
+    private String courseName;
+
+    @ApiModelProperty(value = "关联视频id")
+    private Long videoId;
+
+    @ApiModelProperty(value = "视频标题")
+    private String title;
+
+    @ApiModelProperty(value = "统计明细")
+    private FsCourseAnalysisCountVO countDetailsVO;
+
+}

+ 15 - 0
fs-service-system/src/main/java/com/fs/course/vo/newfs/FsCourseAnalysisCountVO.java

@@ -39,6 +39,21 @@ public class FsCourseAnalysisCountVO implements Cloneable {
     @ApiModelProperty(value = "正确率")
     private BigDecimal answerRightRate;
 
+    /**
+     * 以下是新增字段(PC端需要获取)
+     */
+    @ApiModelProperty(value = "答题次数-管理端展示")
+    private int answerTimes;
+
+    @ApiModelProperty(value = "答题正确次数-管理端展示")
+    private int answerRightTimes;
+
+    @ApiModelProperty(value = "观看次数-管理端展示")
+    private int courseWatchTimes;
+
+    @ApiModelProperty(value = "完播次数-管理端展示")
+    private int courseCompleteTimes;
+
     @Override
     public FsCourseAnalysisCountVO clone() {
         FsCourseAnalysisCountVO countVO;

+ 7 - 0
fs-service-system/src/main/java/com/fs/store/mapper/FsUserMapper.java

@@ -274,4 +274,11 @@ public interface FsUserMapper
 
     List<FsUser> getFsUserByCompanyUserAndId(@Param("companyUserId")Long companyUserId, @Param("userId") Long userId);
 
+    /**
+     * 管理端统计看课的相关数据
+     * @param param
+     * @return
+     */
+    List<FsCourseAnalysisCountVO> courseAnalysisWatchLog(CourseAnalysisParam param);
+
 }

+ 6 - 2
fs-service-system/src/main/java/com/fs/store/param/h5/CourseAnalysisParam.java

@@ -5,6 +5,7 @@ import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
 import java.io.Serializable;
+import java.util.List;
 
 /**
  * 课程分析查询-入参
@@ -22,10 +23,10 @@ public class CourseAnalysisParam implements Serializable {
     private Long companyUserId;
 
     @ApiModelProperty(value = "营期id")
-    private String periodId;
+    private Long periodId;
 
     @ApiModelProperty(value = "视频id")
-    private String videoId;
+    private Long videoId;
 
 //    @ApiModelProperty(value = "登录用户id,不传")
 //    private Long userId;
@@ -33,5 +34,8 @@ public class CourseAnalysisParam implements Serializable {
     @ApiModelProperty(value = "公司id,不传")
     private Long companyId;
 
+    @ApiModelProperty(value = "视频id-管理端传参")
+    private List<Long> videoIdList;
+
 }
 

+ 6 - 0
fs-service-system/src/main/resources/mapper/course/FsUserCoursePeriodDaysMapper.xml

@@ -33,6 +33,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="periodId != null "> and period_id = #{periodId}</if>
             <if test="courseId != null "> and course_id = #{courseId}</if>
             <if test="videoId != null "> and video_id = #{videoId}</if>
+            <if test="videoIds != null and videoIds.size > 0">
+                and a.video_id in
+                <foreach collection="videoIds" open="(" close=")" separator="," item="videoId">
+                    #{videoId}
+                </foreach>
+            </if>
         </where>
     </select>
 

+ 73 - 16
fs-service-system/src/main/resources/mapper/store/FsUserMapper.xml

@@ -1220,14 +1220,23 @@
         fs_course_red_packet_log flog
         LEFT JOIN fs_user ON fs_user.user_id = flog.user_id
         LEFT JOIN company_user ON company_user.user_id = fs_user.company_user_id
-        WHERE
-        company_user.user_id = #{companyUserId}
-        <if test="periodId != null and periodId != ''">
-            AND flog.period_id =  #{periodId}
-        </if>
-        <if test="videoId != null and videoId != ''">
-            AND flog.video_id = #{videoId}
-        </if>
+        <where>
+            <if test="companyUserId != null and companyUserId !='' ">
+                AND company_user.user_id = #{companyUserId}
+            </if>
+            <if test="periodId != null and periodId != ''">
+                AND flog.period_id =  #{periodId}
+            </if>
+            <if test="videoId != null and videoId != ''">
+                AND flog.video_id = #{videoId}
+            </if>
+            <if test="videoIdList != null and videoIdList.size > 0 ">
+                AND flog.video_id in
+                <foreach collection="videoIdList" open="(" close=")" separator="," item="videoId">
+                    #{videoId}
+                </foreach>
+            </if>
+        </where>
         GROUP BY
         flog.video_id
     </select>
@@ -1236,7 +1245,9 @@
     <select id="courseAnalysisAnswerCount" resultType="FsCourseAnalysisCountVO">
         SELECT
         count( DISTINCT fs_user.user_id ) as answerNum,
+        count(fs_course_answer_logs.log_id) answerTimes,
         COUNT( DISTINCT CASE WHEN fs_course_answer_logs.is_right = 1 THEN fs_user.user_id END ) as answerRightNum,
+        COUNT( CASE WHEN fs_course_answer_logs.is_right = 1 THEN fs_course_answer_logs.log_id END ) as answerRightTimes,
         ifnull(
         ROUND(
         (
@@ -1250,14 +1261,24 @@
         fs_course_answer_logs
         LEFT JOIN fs_user ON fs_user.user_id = fs_course_answer_logs.user_id
         LEFT JOIN company_user ON company_user.user_id = fs_user.company_user_id
-        WHERE
-        company_user.user_id = #{companyUserId}
-        <if test="periodId != null and periodId != ''">
-            AND fs_course_answer_logs.period_id =  #{periodId}
-        </if>
-        <if test="videoId != null and videoId != ''">
-            AND fs_course_answer_logs.video_id = #{videoId}
-        </if>
+        <where>
+            <if test="companyUserId != null and companyUserId !='' ">
+                AND company_user.user_id = #{companyUserId}
+            </if>
+            <if test="periodId != null and periodId != ''">
+                AND fs_course_answer_logs.period_id =  #{periodId}
+            </if>
+            <if test="videoId != null and videoId != ''">
+                AND fs_course_answer_logs.video_id = #{videoId}
+            </if>
+            <if test="videoIdList != null and videoIdList.size > 0 ">
+                AND fs_course_answer_logs.video_id in
+                <foreach collection="videoIdList" open="(" close=")" separator="," item="videoId">
+                    #{videoId}
+                </foreach>
+            </if>
+
+        </where>
         GROUP BY
         fs_course_answer_logs.video_id
     </select>
@@ -1383,4 +1404,40 @@
             </if>
         </where>
     </select>
+
+    <select id="courseAnalysisWatchLog" resultType="FsCourseAnalysisCountVO">
+        SELECT
+        count( DISTINCT CASE WHEN fwl.log_type != 3 THEN fwl.user_id END ) AS courseWatchNum,
+        count( CASE WHEN fwl.log_type != 3 THEN fwl.log_id END ) AS courseWatchTimes,
+        count( DISTINCT CASE WHEN fwl.log_type = 2 THEN fwl.user_id END ) AS courseCompleteNum,
+        count( CASE WHEN fwl.log_type = 2 THEN fwl.log_id END ) AS courseCompleteTimes,
+        ifnull(
+        ROUND(
+        (
+        COUNT( DISTINCT CASE WHEN fwl.log_type = 2 THEN fwl.user_id END ) / count( DISTINCT CASE WHEN fwl.log_type != 3 THEN fwl.user_id END )) * 100,
+        2
+        ),
+        0
+        ) AS completeRate,
+        fwl.video_id
+        FROM
+        fs_course_watch_log fwl
+        <where>
+            <if test="periodId != null and periodId != ''">
+                AND fwl.period_id =  #{periodId}
+            </if>
+            <if test="videoId != null and videoId != ''">
+                AND fwl.video_id = #{videoId}
+            </if>
+            <if test="videoIdList != null and videoIdList.size > 0 ">
+                AND fwl.video_id in
+                <foreach collection="videoIdList" open="(" close=")" separator="," item="videoId">
+                    #{videoId}
+                </foreach>
+            </if>
+        </where>
+        GROUP BY
+        fwl.video_id
+    </select>
+
 </mapper>