فهرست منبع

登录修改 销售app统计

xgb 1 ماه پیش
والد
کامیت
e716e96639

+ 106 - 0
fs-company/src/main/java/com/fs/app/controller/statistic/courseStatisticController.java

@@ -0,0 +1,106 @@
+package com.fs.app.controller.statistic;
+
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.course.param.FsCourseWatchLogStatisticsListParam;
+import com.fs.course.service.IFsCourseWatchLogService;
+import com.fs.framework.security.LoginUser;
+import com.fs.framework.service.TokenService;
+import com.fs.his.vo.AppCourseReportVO;
+import com.fs.his.vo.AppWatchLogReportVO;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * @description: app 看课统计
+ * @author: Xgb
+ * @createDate: 2026/3/16
+ * @version: 1.0
+ */
+@RestController
+@RequestMapping("/app/statistics")
+public class courseStatisticController extends BaseController {
+
+    @Autowired
+    private TokenService tokenService;
+
+    @Autowired
+    private IFsCourseWatchLogService courseWatchLogService;
+
+    /**
+     * 销售后台app看课统计 会员维度
+     * @param param
+     * @return
+     */
+    @GetMapping("/appWatchLogReport")
+    public TableDataInfo aPPWatchLogReport(FsCourseWatchLogStatisticsListParam param) {
+        startPage();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        param.setCompanyId(loginUser.getCompany().getCompanyId());
+        return getDataTable(courseWatchLogService.selectUserAppWatchLogReportVO(param));
+    }
+
+    /**
+     * 销售后台app看课统计 会员维度导出
+     * @param param
+     * @return
+     */
+    @GetMapping("/appWatchLogReportExport")
+    public AjaxResult appWatchLogReportExport(FsCourseWatchLogStatisticsListParam param) {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        param.setCompanyId(loginUser.getCompany().getCompanyId());
+        List<AppWatchLogReportVO> list = courseWatchLogService.selectUserAppWatchLogReportVO(param);
+        // 转换登录渠道和答题状态
+        list.forEach(this::convertAppWatchLogReportVO);
+        // 获取所有字段,排除会员维度的三个字段
+        List<String> selectedFields = getAppWatchLogReportFields();
+        ExcelUtil<AppWatchLogReportVO> util = new ExcelUtil<AppWatchLogReportVO>(AppWatchLogReportVO.class);
+        return util.exportExcelSelectedColumns(list, "APP看课统计报表", selectedFields);
+    }
+
+    /**
+     * 转换AppWatchLogReportVO字段
+     * - 登录渠道:有值显示"app",无值显示"小程序"
+     * - 答题状态:无值显示"未答题"
+     */
+    private void convertAppWatchLogReportVO(AppWatchLogReportVO vo) {
+        // 登录渠道转换
+        if (StringUtils.isNotEmpty(vo.getLoginChannel())) {
+            vo.setLoginChannel("app");
+        } else {
+            vo.setLoginChannel("小程序");
+        }
+        // 答题状态转换
+        if (StringUtils.isEmpty(vo.getAnswerStatus())) {
+            vo.setAnswerStatus("未答题");
+        }
+    }
+
+    /**
+     * 获取AppWatchLogReportVO需要导出的字段(排除特定字段)
+     */
+    private List<String> getAppWatchLogReportFields() {
+        // 需要排除的字段:app会员数、销售数、新注册app会员数
+        List<String> excludeFields = Arrays.asList("AppUserCount", "salesCount", "AppNewUser");
+
+        return Arrays.stream(AppWatchLogReportVO.class.getDeclaredFields())
+                .filter(field -> field.isAnnotationPresent(Excel.class))
+                .map(Field::getName)
+                .filter(fieldName -> !excludeFields.contains(fieldName))
+                .collect(Collectors.toList());
+    }
+
+
+}

+ 10 - 1
fs-company/src/main/java/com/fs/company/controller/company/CompanyDeptController.java

@@ -184,7 +184,16 @@ public class CompanyDeptController extends BaseController
         return toAjax(deptService.deleteCompanyDeptById(deptId));
     }
 
-
+    /**
+     * 获取部门列表树
+     */
+    @GetMapping("/selectDeptTree")
+    public AjaxResult selectDeptTree(CompanyDept dept)
+    {
+        dept.setStatus("0");
+        List<CompanyDept> depts = deptService.selectCompanyDeptList(dept);
+        return AjaxResult.success(deptService.buildDeptTreeSelect(depts));
+    }
 
 
 

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

@@ -6,6 +6,8 @@ import com.fs.course.dto.WatchLogDTO;
 import com.fs.course.param.*;
 import com.fs.course.vo.*;
 import com.fs.his.vo.AppCourseReportVO;
+import com.fs.his.vo.AppWatchLogReportVO;
+import com.fs.his.vo.WatchLogReportVO;
 import com.fs.qw.domain.QwExternalContact;
 import com.fs.qw.param.QwSidebarStatsParam;
 import com.fs.sop.vo.QwRatingVO;
@@ -750,4 +752,28 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
     List<AppCourseReportVO> selectAppAnswerStatistics(FsCourseWatchLogStatisticsListParam param);
 
     List<AppCourseReportVO> selectAppRedPacketStatistics(FsCourseWatchLogStatisticsListParam param);
+
+    List<AppWatchLogReportVO> selectAppUserBaseData(FsCourseWatchLogStatisticsListParam param);
+
+    /**
+     * 销售端看课报表 营期训练营明细
+     * @param
+     * @return
+     */
+    List<WatchLogReportVO>  selectCampPeriodByPeriod(@Param("periodIds") List<Long> periodIds );
+
+    /**
+     * 销售端看课报表 红包
+     * @param
+     * @return
+     */
+    List<WatchLogReportVO> selectRedPacketStats(@Param("logIds") List<Long> logIds);
+
+    /**
+     * 答题
+     * @param logIds
+     * @return
+     */
+    List<WatchLogReportVO> selectAnswerStats(@Param("logIds") List<Long> logIds);
+
 }

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

@@ -83,4 +83,9 @@ public class FsCourseWatchLogStatisticsListParam {
     }
 
     private String appId;
+
+    /**
+     * 手机号
+     */
+    private  String userPhone;
 }

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

@@ -5,6 +5,7 @@ import com.fs.course.domain.FsCourseWatchLog;
 import com.fs.course.param.*;
 import com.fs.course.vo.*;
 import com.fs.his.vo.AppCourseReportVO;
+import com.fs.his.vo.AppWatchLogReportVO;
 import com.fs.qw.param.QwSidebarStatsParam;
 import com.fs.qw.vo.QwWatchLogStatisticsListVO;
 
@@ -177,4 +178,5 @@ public interface IFsCourseWatchLogService extends IService<FsCourseWatchLog> {
      */
     List<AppCourseReportVO> selectAppCourseReportVO(FsCourseWatchLogStatisticsListParam param);
 
+    List<AppWatchLogReportVO> selectUserAppWatchLogReportVO(FsCourseWatchLogStatisticsListParam param);
 }

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

@@ -36,7 +36,10 @@ import com.fs.his.dto.AppUserCompanyDTO;
 import com.fs.his.mapper.FsUserMapper;
 import com.fs.his.service.IFsUserService;
 import com.fs.his.utils.ConfigUtil;
+import com.fs.his.utils.PhoneUtil;
 import com.fs.his.vo.AppCourseReportVO;
+import com.fs.his.vo.AppWatchLogReportVO;
+import com.fs.his.vo.WatchLogReportVO;
 import com.fs.qw.Bean.MsgBean;
 import com.fs.qw.cache.IQwExternalContactCacheService;
 import com.fs.qw.cache.IQwUserCacheService;
@@ -1829,6 +1832,162 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
 
         return allCompanies;
     }
+
+    @Override
+    public List<AppWatchLogReportVO> selectUserAppWatchLogReportVO(FsCourseWatchLogStatisticsListParam param) {
+        if (StringUtils.isNotEmpty(param.getUserPhone())) {
+            //加密手机号
+            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<AppWatchLogReportVO> baseData = fsCourseWatchLogMapper.selectAppUserBaseData(param);
+        if (CollectionUtils.isEmpty(baseData)) {
+            return Collections.emptyList();
+        }
+        // 获取统计数据和组装结果
+        return assembleAppStatisticsData(baseData, param);
+    }
+
+    /**
+     * 组装APP统计数据
+     */
+    private List<AppWatchLogReportVO> assembleAppStatisticsData(List<AppWatchLogReportVO> baseData, FsCourseWatchLogStatisticsListParam param) {
+        // 准备查询条件
+        List<Long> periods = baseData.stream().map(AppWatchLogReportVO::getPeriodId).collect(Collectors.toList());
+        List<Long> logIds = baseData.stream().map(AppWatchLogReportVO::getLogId).collect(Collectors.toList());
+        List<Long> userIds = baseData.stream().map(AppWatchLogReportVO::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)
+        );
+
+        // 学习时长数据(来自fs_user_course_study_log表)- 使用字符串时间
+//        Map<String, AppWatchLogReportVO> studyDurationMap = fsUserCourseStudyLogMapper.selectStudyDurationByUserIds(userIds, param.getStartDate(), param.getEndDate())
+//                .stream()
+//                .collect(Collectors.toMap(
+//                        item -> item.getUserId() + "_" + item.getVideoId(),
+//                        Function.identity()
+//                ));
+
+        // 组装数据
+        for (AppWatchLogReportVO item : baseData) {
+            // 营期数据
+            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());
+            }
+
+//            // 订单数据
+//            WatchLogReportVO order = orderMap.getOrDefault(item.getUserId(), null);
+//            if (order != null) {
+//                item.setHistoryOrderCount(order.getHistoryOrderCount());
+//            }
+
+            // 答题数据
+            WatchLogReportVO answer = answerMap.getOrDefault(item.getLogId(), null);
+            if (answer != null) {
+                item.setAnswerStatus(answer.getAnswerStatus());
+            }
+
+            // 学习时长数据
+//            AppWatchLogReportVO studyDuration = studyDurationMap.get(item.getUserId() + "_" + item.getVideoId());
+//            if (studyDuration != null && studyDuration.getPublicCourseDuration() != null) {
+//                // 将秒转换为时分秒格式
+//                item.setPublicCourseDuration(formatDuration(Long.valueOf(studyDuration.getPublicCourseDuration())));
+//            }
+        }
+        return baseData;
+    }
+
+    /**
+     * 红包数据转Map
+     */
+    public Map<Long, WatchLogReportVO> convertRedPacketToMap(List<WatchLogReportVO> list) {
+        if (list == null || list.isEmpty()) {
+            return new HashMap<>();
+        }
+        return list.stream()
+                .collect(Collectors.toMap(
+                        WatchLogReportVO::getLogId,
+                        Function.identity(),
+                        (existing, replacement) -> existing // 当出现重复键时,保留第一个值
+                ));
+    }
+
+    /**
+     * 营期数据转Map
+     */
+    public Map<Long, WatchLogReportVO> convertCampPeriodToMap(List<WatchLogReportVO> list) {
+        if (list == null || list.isEmpty()) {
+            return new HashMap<>();
+        }
+        return list.stream()
+                .collect(Collectors.toMap(
+                        WatchLogReportVO::getPeriodId,
+                        Function.identity(),
+                        (existing, replacement) -> existing // 当出现重复键时,保留第一个值
+                ));
+    }
+
+
+    /**
+     * 订单数据转Map
+     */
+    public Map<Long, WatchLogReportVO> convertOrderToMap(List<WatchLogReportVO> list) {
+        if (list == null || list.isEmpty()) {
+            return new HashMap<>();
+        }
+        return list.stream()
+                .collect(Collectors.toMap(
+                        WatchLogReportVO::getUserId,
+                        Function.identity(),
+                        (existing, replacement) -> existing // 当出现重复键时,保留第一个值
+                ));
+    }
+
+    /**
+     * 答题数据转Map
+     */
+    public Map<Long, WatchLogReportVO> convertAnswerToMap(List<WatchLogReportVO> list) {
+        if (list == null || list.isEmpty()) {
+            return new HashMap<>();
+        }
+        return list.stream()
+                .collect(Collectors.toMap(
+                        WatchLogReportVO::getLogId,
+                        Function.identity(),
+                        (existing, replacement) -> existing // 当出现重复键时,保留第一个值
+                ));
+    }
+
     /**
      * app 看课率((看课中人次+完课人次)/ 私域课总人次)
      * @param watchingCount 私域看课中人次

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

@@ -35,7 +35,7 @@ import org.apache.ibatis.annotations.*;
  */
 public interface FsUserMapper
 {
-    @Select("select * from fs_user where phone=#{phone}")
+    @Select("select * from fs_user where phone=#{phone} and is_del=0")
     List<FsUser> selectFsUsersByPhoneLimitOne(String phone);
     /**
      * 查询用户

+ 15 - 2
fs-service/src/main/java/com/fs/his/service/impl/FsUserServiceImpl.java

@@ -260,16 +260,29 @@ public class FsUserServiceImpl implements IFsUserService {
     @Override
     public int updateFsUser(FsUser fsUser) {
         fsUser.setUpdateTime(DateUtils.getNowDate());
-        if (fsUser.getPhone() != null && fsUser.getPhone().length() == 11 && fsUser.getPhone().matches("\\d+")) {
-            fsUser.setPhone(encryptPhone(fsUser.getPhone()));
+        if (fsUser.getPhone() != null) {
+            //明文手机号
+            if (fsUser.getPhone().length() == 11 && fsUser.getPhone().matches("\\d+")){
+                fsUser.setPhone(encryptPhone(fsUser.getPhone()));
+                //加密手机号
+            }else if (fsUser.getPhone().length() > 11&&fsUser.getPhone().endsWith("==")){
+                fsUser.setPhone(fsUser.getPhone());
+            }else {
+                fsUser.setPhone(null);
+            }
         } else {
             fsUser.setPhone(null);
         }
+
+
+
+
         if (ObjectUtils.isNotEmpty(fsUser.getLevel())&&fsUser.getLevel().equals(1)){
             fsUser.setIsShow(1);
         }else {
             fsUser.setIsShow(0);
         }
+
         return fsUserMapper.updateFsUser(fsUser);
     }
 

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

@@ -0,0 +1,146 @@
+package com.fs.his.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+public class AppWatchLogReportVO {
+
+    @Excel(name = "会员id")
+    private Long userId;
+    /**
+     * 昵称
+     */
+    @Excel(name = "会员昵称")
+    private String nickName;
+
+
+    /**
+     * app会员数
+     */
+    @Excel(name = "app会员数")
+    private  Integer AppUserCount;
+
+    /**
+     * 新注册app会员数
+     */
+    @Excel(name = "新注册app会员数")
+    private  Integer AppNewUser;
+
+    /**
+     * 登录渠道
+     */
+    @Excel(name = "登录渠道")
+    private  String loginChannel;
+
+    /**
+     * 销售数
+     */
+    @Excel(name = "销售数")
+    private  Integer salesCount;
+
+
+    /**
+     * 所属销售数
+     */
+    @Excel(name = "所属销售")
+    private  String salesName;
+
+    /**
+     * 所属销售部门
+     */
+    @Excel(name = "销售部门")
+    private  String salesDept;
+
+    /**
+     * 所属销售公司
+     */
+    @Excel(name = "所属销售公司")
+    private  String salesCompany;
+
+    /**
+     * 培训营名称
+     */
+    @Excel(name = "训练营")
+    private String trainingCampName;
+
+    /**
+     * 营期
+     */
+    @Excel(name = "营期")
+    private  String periodName;
+
+    /**
+     * 视频名称
+     */
+    @Excel(name = "小节名称")
+    private  String videoTitle;
+
+
+    /**
+     * 公开课播放时长
+     */
+    @Excel(name = "公开课播放时长")
+    private  String publicCourseDuration;
+
+    /**
+     * 私欲看课状态
+     */
+    @Excel(name = "私域课看课状态")
+    private  String privateWatchStatus;
+
+    /**
+     * 私欲课播放时长
+      */
+    @Excel(name = "私域课播放时长")
+    private  String privateWatchDuration;
+
+    /**
+     * 观看完成时间
+     */
+    @Excel(name = "完课时间",dateFormat = "yyyy-MM-dd")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private Date finishTime;
+
+    /**
+     * 回答状态
+     */
+    @Excel(name = "答题状态")
+    private  String answerStatus;
+
+    /**
+     * 领取红包金额
+     */
+    @Excel(name = "红包金额")
+    private BigDecimal redPacketAmount;
+
+    /**
+     * 历史订单数
+     */
+    @Excel(name = "历史疗法订单数")
+    private  Integer historyOrderCount;
+
+
+    /**
+     * 营期id
+     */
+    private  Long periodId;
+
+    /**
+     * 视频id
+     */
+    private  Long videoId;
+
+    /**
+     * 观看记录id
+     */
+    private  Long logId;
+
+    private  Long deptId;
+
+
+}

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

@@ -0,0 +1,177 @@
+package com.fs.his.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+public class WatchLogReportVO {
+
+    @Excel(name = "会员id")
+    private Long userId;
+    /**
+     * 昵称
+     */
+    @Excel(name = "会员昵称")
+    private String nickName;
+
+    /**
+     * 会员数
+     */
+    @Excel(name = "会员数")
+    private  Integer userCount;
+
+
+    /**
+     * 销售数
+     */
+    @Excel(name = "销售数")
+    private  Integer salesCount;
+
+    /**
+     * 所属销售数
+     */
+    @Excel(name = "所属销售")
+    private  String salesName;
+
+    /**
+     * 所属销售部门
+     */
+    @Excel(name = "销售部门")
+    private  String salesDept;
+
+    /**
+     * 所属销售公司
+     */
+    @Excel(name = "所属销售公司")
+    private  String salesCompany;
+
+    /**
+     * 在线会员数
+     */
+    @Excel(name = "当前会员线上数")
+    private  Integer onlineUserCount;
+
+    /**
+     * 培训营名称
+     */
+    @Excel(name = "训练营")
+    private String trainingCampName;
+
+    /**
+     * 营期
+     */
+    @Excel(name = "营期")
+    private  String periodName;
+
+    /**
+     * 视频名称
+     */
+    @Excel(name = "小节名称")
+    private  String videoTitle;
+
+    /**
+     * 观看状态
+     */
+    @Excel(name = "看课状态")
+    private  String watchStatus;
+
+    /**
+     * 观看时长
+     */
+    @Excel(name = "观看时长")
+    private  String duration;
+
+    /**
+     * 观看完成数
+     */
+    @Excel(name = "完课数")
+    private  Integer finishedCount;
+
+    /**
+     * 观看未完成数
+     */
+    @Excel(name = "未完课")
+    private  Integer unfinishedCount;
+
+    /**
+     * 观看完成率
+     */
+    @Excel(name = "完课率")
+    private BigDecimal completionRate;
+
+    /**
+     * 看课时间
+     */
+    @Excel(name = "看课时间",dateFormat = "yyyy-MM-dd")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private Date courseTime;
+
+    /**
+     * 观看完成时间
+     */
+    @Excel(name = "完课时间",dateFormat = "yyyy-MM-dd")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private  Date  finishTime;
+
+    /**
+     * 未看课数
+     */
+    @Excel(name = "未看数")
+    private  Integer notWatchedCount;
+
+    /**
+     * 未回答数
+     */
+    @Excel(name = "未答题人数")
+    private  Integer notAnsweredCount;
+
+    /**
+     * 回答状态
+     */
+    @Excel(name = "答题状态")
+    private  String answerStatus;
+
+    /**
+     * 领取红包金额
+     */
+    @Excel(name = "红包金额")
+    private  BigDecimal redPacketAmount;
+
+    /**
+     * 历史订单数
+     */
+    @Excel(name = "历史疗法订单数")
+    private  Integer historyOrderCount;
+
+    /**
+     * 销售id
+     */
+    private  Long companyUserId;
+
+    /**
+     * 总观看人数
+     */
+    private  Long totalLogCount;
+
+    /**
+     * 营期id
+     */
+    private  Long periodId;
+
+    /**
+     * 视频id
+     */
+    private  Long videoId;
+
+    /**
+     * 观看记录id
+     */
+    private  Long logId;
+
+    private  Long deptId;
+
+}

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

@@ -1382,4 +1382,130 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         </if>
         GROUP BY rpl.company_id
     </select>
+    <select id="selectAppUserBaseData" resultType="com.fs.his.vo.AppWatchLogReportVO">
+        SELECT
+        log.user_id userId,
+        u.nick_name AS nickName,
+        u.source loginChannel,
+        cu.nick_name AS salesName,
+        c.company_name AS salesCompany,
+        cd.dept_name AS salesDept,
+        log.period_id periodId,
+        log.video_id videoId,
+        log.log_id logId,
+        log.create_time courseTime,
+        log.finish_time finishTime,
+        log.duration privateWatchDuration,
+        log.log_type privateWatchStatus,
+        cv.title AS videoTitle
+        FROM
+        fs_course_watch_log log
+        LEFT JOIN fs_user u ON u.user_id = log.user_id
+        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 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
+        AND log.watch_type = 1
+        <include refid="commonConditions"/>
+        group by log.user_id
+        ORDER BY u.register_date DESC
+    </select>
+    <select id="selectCampPeriodByPeriod" resultType="com.fs.his.vo.WatchLogReportVO">
+        SELECT
+        cp.period_id periodId,
+        cp.period_name periodName,
+        camp.training_camp_name
+        FROM
+        fs_user_course_period cp
+        LEFT JOIN fs_user_course_training_camp camp ON camp.training_camp_id = cp.training_camp_id
+        WHERE cp.period_id in
+        <foreach collection="periodIds" item="periodId" open="(" separator="," close=")">
+            #{periodId}
+        </foreach>
+    </select>
+    <select id="selectRedPacketStats" resultType="com.fs.his.vo.WatchLogReportVO">
+        SELECT
+        rp.watch_log_id  as  logId,
+        SUM(rp.amount) AS redPacketAmount
+        FROM fs_course_red_packet_log rp
+        WHERE  rp.watch_log_id IN
+        <foreach collection="logIds" item="logId" open="(" separator="," close=")">
+            #{logId}
+        </foreach>
+        GROUP BY rp.watch_log_id
+    </select>
+    <select id="selectAnswerStats" resultType="com.fs.his.vo.WatchLogReportVO">
+        SELECT
+        l.watch_log_id AS logId,
+        CASE WHEN l.log_id IS NOT NULL THEN '已答题' ELSE '未答题' END AS answerStatus,
+        (
+        SELECT COUNT(1)
+        FROM fs_course_watch_log wl
+        WHERE wl.log_id IN
+        <foreach collection="logIds" item="logId" open="(" separator="," close=")">
+            #{logId}
+        </foreach>
+        AND wl.log_id NOT IN (
+        SELECT watch_log_id
+        FROM fs_course_answer_logs
+        WHERE watch_log_id IS NOT NULL
+        )) AS notAnsweredCount
+        FROM fs_course_answer_logs l
+        WHERE l.watch_log_id IN
+        <foreach collection="logIds" item="logId" open="(" separator="," close=")">
+            #{logId}
+        </foreach>
+        GROUP BY l.watch_log_id
+    </select>
+
+    <sql id="commonConditions">
+        <!-- 销售公司 -->
+        <if test="companyId != null and companyId != ''">
+            AND c.company_id = #{companyId}
+        </if>
+
+        <!-- 销售部门 -->
+        <if test="deptId != null and deptId != ''">
+            AND cd.dept_id = #{deptId}
+        </if>
+
+        <!-- 所属销售 -->
+        <if test="salesId != null and salesId != ''">
+            AND cu.user_id = #{salesId}
+        </if>
+
+        <!-- 项目 -->
+        <if test="project != null and project != ''">
+            AND cuu.project_id = #{project}
+        </if>
+        <!-- 时间范围 -->
+        <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="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>
+
+        <!-- 会员ID -->
+        <if test="userId != null and userId != ''">
+            AND u.user_id = #{userId}
+        </if>
+
+        <!-- 会员手机号 -->
+        <if test="userPhone != null and userPhone != ''">
+            AND u.phone LIKE CONCAT('%', #{userPhone}, '%')
+        </if>
+
+        <!-- 会员昵称 -->
+        <if test="nickName != null and nickName != ''">
+            AND u.nick_name LIKE CONCAT('%', #{nickName}, '%')
+        </if>
+    </sql>
 </mapper>