Переглянути джерело

Merge remote-tracking branch 'origin/master'

Guos 1 тиждень тому
батько
коміт
d93ea69357
29 змінених файлів з 393 додано та 11 видалено
  1. 31 0
      fs-admin/src/main/java/com/fs/course/task/WatchCourseStatistics.java
  2. 2 0
      fs-common/src/main/java/com/fs/common/constant/FsConstants.java
  3. 59 0
      fs-common/src/main/java/com/fs/common/utils/DateUtils.java
  4. 26 0
      fs-common/src/main/java/com/fs/common/utils/model/DateTimeEntity.java
  5. 13 0
      fs-company/src/main/java/com/fs/company/controller/course/FsCourseFinishTempController.java
  6. 20 2
      fs-company/src/main/java/com/fs/company/controller/course/qw/FsQwCourseWatchLogController.java
  7. 3 1
      fs-company/src/main/java/com/fs/framework/service/UserDetailsServiceImpl.java
  8. 3 1
      fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java
  9. 9 0
      fs-service/src/main/java/com/fs/course/domain/FsCourseFinishTemp.java
  10. 12 0
      fs-service/src/main/java/com/fs/course/mapper/FsCourseFinishTempMapper.java
  11. 4 1
      fs-service/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java
  12. 2 1
      fs-service/src/main/java/com/fs/course/param/FsCourseWatchLogListParam.java
  13. 7 0
      fs-service/src/main/java/com/fs/course/service/IFsCourseFinishTempService.java
  14. 1 0
      fs-service/src/main/java/com/fs/course/service/IFsCourseWatchLogService.java
  15. 9 0
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseFinishTempServiceImpl.java
  16. 2 0
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java
  17. 9 0
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  18. 7 2
      fs-service/src/main/java/com/fs/qw/mapper/QwGroupChatUserMapper.java
  19. 39 0
      fs-service/src/main/java/com/fs/qw/mapper/QwWatchLogMapper.java
  20. 3 0
      fs-service/src/main/java/com/fs/qw/service/IQwWatchLogService.java
  21. 24 0
      fs-service/src/main/java/com/fs/qw/service/impl/QwWatchLogServiceImpl.java
  22. 17 1
      fs-service/src/main/java/com/fs/qw/vo/QwWatchLogStatisticsListVO.java
  23. 29 0
      fs-service/src/main/java/com/fs/statis/dto/WatchCourseStatisticsResultDTO.java
  24. 5 0
      fs-service/src/main/java/com/fs/statis/service/IStatisticsService.java
  25. 32 0
      fs-service/src/main/java/com/fs/statis/service/impl/StatisticsServiceImpl.java
  26. 21 0
      fs-service/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml
  27. 1 0
      fs-service/src/main/resources/mapper/his/FsExportTaskMapper.xml
  28. 2 2
      fs-service/src/main/resources/mapper/qw/QwFriendWelcomeMapper.xml
  29. 1 0
      fs-service/src/main/resources/mapper/qw/QwUserMapper.xml

+ 31 - 0
fs-admin/src/main/java/com/fs/course/task/WatchCourseStatistics.java

@@ -0,0 +1,31 @@
+package com.fs.course.task;
+
+import com.fs.statis.service.IStatisticsService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * @description: 看客相关统计定时任务
+ * @author: Xgb
+ * @createDate: 2025/10/27
+ * @version: 1.0
+ */
+@Component("watchCourseStatistics")
+public class WatchCourseStatistics {
+
+    @Autowired
+    private IStatisticsService statisticsService;
+
+    /**
+     * @Description: 统计统计按TimeType 0-今天,1-昨天,2-本周,3-本月,4-上月;各公司的观看人数和完播人数,
+     * 各公司的观看人数和完播人数, 存到redis中,定时任务每15分钟执行一次
+     *
+     * @Param:
+     * @Return:
+     * @Author xgb
+     * @Date 2025/10/27 11:47
+     */
+    public void watchCourseStatisticsGroupByCompany() {
+        statisticsService.watchCourseStatisticsGroupByCompany();
+    }
+}

+ 2 - 0
fs-common/src/main/java/com/fs/common/constant/FsConstants.java

@@ -16,4 +16,6 @@ public interface FsConstants {
     String COMPANY_MONEY_KEY = "company:money:";
     // 公司余额redis 锁
     String COMPANY_MONEY_LOCK = "company_money_lock:";
+    // 看客统计  按公司分组 按TimeType 0-今天,1-昨天,2-本周,3-本月,4-上月;
+    String WATCH_COURSE_STATISTICS_GROUP_COMPANY = "watch_course_statistics:group_company:";
 }

+ 59 - 0
fs-common/src/main/java/com/fs/common/utils/DateUtils.java

@@ -6,8 +6,12 @@ import java.text.SimpleDateFormat;
 import java.time.*;
 import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
+import java.time.temporal.TemporalAdjusters;
 import java.util.Calendar;
 import java.util.Date;
+import java.util.Map;
+
+import com.fs.common.utils.model.DateTimeEntity;
 import org.apache.commons.lang3.time.DateFormatUtils;
 
 /**
@@ -296,4 +300,59 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils
         return calendar.getTimeInMillis();
     }
 
+
+    /**
+     * @Description: 根据类型返回开始时间和结束时间 最大时间是今天23:59:59 如本月 是1号00:00:00到今天的23:59:59
+     * @Param:  type: 0-今天 1-昨天 2-本周 3-本月 4-上月
+     * @Param:
+     * @Return:
+     * @Author xgb
+     * @Date 2025/10/27 14:09
+     */
+    public static DateTimeEntity getBetweenTime(int type){
+        // 根据type计算出时间范围
+        String startDate = "";
+        String endDate = "";
+
+        LocalDateTime now = LocalDateTime.now();
+        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+        LocalTime startOfDayTime = LocalTime.MIN;
+        LocalTime endOfDayTime = LocalTime.of(23, 59, 59);
+        if(0 == type){
+            LocalDateTime startOfDay = now.with(startOfDayTime);
+            LocalDateTime endOfDay = now.with(endOfDayTime);
+            startDate = startOfDay.format(formatter);
+            endDate = endOfDay.format(formatter);
+        } else if(1 == type){
+            LocalDateTime yesterday = now.minusDays(1);
+            LocalDateTime startOfYesterday = yesterday.with(startOfDayTime);
+            LocalDateTime endOfYesterday = yesterday.with(endOfDayTime);
+            startDate = startOfYesterday.format(formatter);
+            endDate = endOfYesterday.format(formatter);
+        } else if(2 == type) {
+            LocalDateTime startOfWeek = now.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY));
+            LocalDateTime startOfStartOfWeek = startOfWeek.with(startOfDayTime);
+            LocalDateTime endOfToday = now.with(endOfDayTime);
+            startDate = startOfStartOfWeek.format(formatter);
+            endDate = endOfToday.format(formatter);
+        } else if(3 == type) {
+            LocalDateTime startOfMonth = now.withDayOfMonth(1);
+            LocalDateTime startOfStartOfMonth = startOfMonth.with(startOfDayTime);
+            LocalDateTime endOfToday = now.with(endOfDayTime);
+            startDate = startOfStartOfMonth.format(formatter);
+            endDate = endOfToday.format(formatter);
+        } else if(4 == type) {
+            LocalDateTime firstDayOfPreviousMonth = now.minusMonths(1).withDayOfMonth(1);
+            LocalDateTime lastDayOfPreviousMonth = now.withDayOfMonth(1).minusDays(1);
+
+            LocalDateTime startOfPrevMonthStart = firstDayOfPreviousMonth.with(startOfDayTime);
+            LocalDateTime endOfPrevMonthEnd = lastDayOfPreviousMonth.with(endOfDayTime);
+
+            startDate = startOfPrevMonthStart.format(formatter);
+            endDate = endOfPrevMonthEnd.format(formatter);
+        }
+
+        return new DateTimeEntity(startDate, endDate);
+    }
+
 }

+ 26 - 0
fs-common/src/main/java/com/fs/common/utils/model/DateTimeEntity.java

@@ -0,0 +1,26 @@
+package com.fs.common.utils.model;
+
+import lombok.Data;
+
+/**
+ * @description: 时间对象
+ * @author: Xgb
+ * @createDate: 2025/10/27
+ * @version: 1.0
+ */
+
+@Data
+public class DateTimeEntity {
+
+    private String startTime;
+
+    private String endTime;
+
+    public DateTimeEntity(String startTime, String endTime) {
+        this.startTime = startTime;
+        this.endTime = endTime;
+    }
+
+    public DateTimeEntity() {
+    }
+}

+ 13 - 0
fs-company/src/main/java/com/fs/company/controller/course/FsCourseFinishTempController.java

@@ -1,10 +1,12 @@
 package com.fs.company.controller.course;
 
+import com.alibaba.fastjson.JSON;
 import com.fs.common.annotation.Log;
 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.enums.BusinessType;
+import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.course.domain.FsCourseFinishTemp;
 import com.fs.course.service.IFsCourseFinishTempService;
@@ -104,4 +106,15 @@ public class FsCourseFinishTempController extends BaseController
     {
         return toAjax(fsCourseFinishTempService.deleteFsCourseFinishTempByIds(ids));
     }
+
+
+    @Log(title = "完课模板", businessType = BusinessType.UPDATE)
+    @PostMapping("/updateStatusBatch")
+    public AjaxResult updateStatusBatch(@RequestBody FsCourseFinishTemp fsCourseFinishTemp)
+    {
+        fsCourseFinishTemp.setUpdateTime(DateUtils.getNowDate());
+
+        return toAjax(fsCourseFinishTempService.updateFsCourseFinishTempBatch(fsCourseFinishTemp));
+    }
+
 }

+ 20 - 2
fs-company/src/main/java/com/fs/company/controller/course/qw/FsQwCourseWatchLogController.java

@@ -19,6 +19,7 @@ import com.fs.framework.service.TokenService;
 import com.fs.qw.param.QwWatchLogStatisticsListParam;
 import com.fs.qw.service.IQwWatchLogService;
 import com.fs.qw.vo.QwWatchLogAllStatisticsListVO;
+import com.fs.qw.vo.QwWatchLogStatisticsListVO;
 import com.fs.sop.mapper.SopUserLogsMapper;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
@@ -81,6 +82,21 @@ public class FsQwCourseWatchLogController extends BaseController
         List<FsCourseWatchLogStatisticsListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogStatisticsListVO(param);
         return getDataTable(list);
     }
+    @PreAuthorize("@ss.hasPermi('course:courseWatchLog:statisticsList')")
+    @GetMapping("/statisticsExport")
+    public AjaxResult statisticsExport(FsCourseWatchLogStatisticsListParam param)
+    {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        param.setCompanyId( loginUser.getCompany().getCompanyId());
+        if (param.getSTime()==null||param.getETime()==null){
+            return AjaxResult.error("请选择时间");
+        }
+        param.setSendType(2);
+        List<FsCourseWatchLogStatisticsListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogStatisticsListVO(param);
+        ExcelUtil<FsCourseWatchLogStatisticsListVO> util = new ExcelUtil<FsCourseWatchLogStatisticsListVO>(FsCourseWatchLogStatisticsListVO.class);
+        return util.exportExcel(list, "企微看课统计");
+
+    }
 
     @GetMapping("/qwWatchLogStatisticsList")
     public TableDataInfo qwWatchLogStatisticsList(QwWatchLogStatisticsListParam param)
@@ -196,8 +212,10 @@ public class FsQwCourseWatchLogController extends BaseController
     {
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
         param.setCompanyId( loginUser.getCompany().getCompanyId());
-        List<FsCourseWatchLogListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogListVO(param);
-        ExcelUtil<FsCourseWatchLogListVO> util = new ExcelUtil<FsCourseWatchLogListVO>(FsCourseWatchLogListVO.class);
+
+        List<QwWatchLogStatisticsListVO> list = qwWatchLogService.selectQwWatchLogStatisticsListVOExport(param);
+
+        ExcelUtil<QwWatchLogStatisticsListVO> util = new ExcelUtil<QwWatchLogStatisticsListVO>(QwWatchLogStatisticsListVO.class);
         return util.exportExcel(list, "短链课程看课记录数据");
     }
 

+ 3 - 1
fs-company/src/main/java/com/fs/framework/service/UserDetailsServiceImpl.java

@@ -9,6 +9,7 @@ import com.fs.company.domain.CompanyUser;
 import com.fs.company.service.ICompanyService;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.framework.security.LoginUser;
+import com.fs.framework.security.SecurityUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -20,7 +21,7 @@ import org.springframework.stereotype.Service;
 /**
  * 用户验证处理
  *
- 
+
  */
 @Service
 public class UserDetailsServiceImpl implements UserDetailsService
@@ -45,6 +46,7 @@ public class UserDetailsServiceImpl implements UserDetailsService
 
 
         CompanyUser user = userService.selectUserByUserName(username);
+
         if (StringUtils.isNull(user))
         {
             log.info("登录用户:{} 不存在.", username);

+ 3 - 1
fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java

@@ -38,6 +38,7 @@ import com.fs.store.config.CompanyMenuConfig;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.mapper.SysConfigMapper;
 import com.fs.system.service.ISysConfigService;
+import com.github.pagehelper.PageHelper;
 import com.google.gson.Gson;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.ObjectUtils;
@@ -840,7 +841,8 @@ public class CompanyServiceImpl implements ICompanyService
     @Override
     public List<DeptDataVO> getDeptData(Long companyId, Long currentCompanyUserId, Long currentDeptId) {
         List<DeptDataVO> result = new ArrayList<>();
-
+        // 线程中可能会残留的分页信息,这里清除,解决报错
+        PageHelper.clearPage();
         Long isAdmin = companyUserRoleMapper.companyUserIsAdmin(currentCompanyUserId);
         logger.info("当前用户 {} 是公司admin 返回公司所有部门树",currentDeptId);
         if(isAdmin!=null){

+ 9 - 0
fs-service/src/main/java/com/fs/course/domain/FsCourseFinishTemp.java

@@ -1,9 +1,12 @@
 package com.fs.course.domain;
 
+import com.baomidou.mybatisplus.annotation.TableField;
 import com.fs.common.annotation.Excel;
 import com.fs.common.core.domain.BaseEntity;
 import lombok.Data;
 
+import java.util.List;
+
 /**
  * 完课模板对象 fs_course_finish_temp
  *
@@ -57,4 +60,10 @@ public class FsCourseFinishTemp extends BaseEntity
 
     @Excel(name = "全选销售标志")
     private Integer isAllCompanyUser;
+
+    /**
+     * 用于批量更新状态
+     */
+    @TableField(exist = false)
+    private List<Long> ids;
 }

+ 12 - 0
fs-service/src/main/java/com/fs/course/mapper/FsCourseFinishTempMapper.java

@@ -6,6 +6,7 @@ import com.fs.course.vo.FsCourseFinishTempListVO;
 import com.fs.course.vo.FsCourseFinishTempVO;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.Update;
 
 /**
  * 完课模板Mapper接口
@@ -111,4 +112,15 @@ public interface FsCourseFinishTempMapper
     public FsCourseFinishTempVO selectFsCourseFinishTempByIdVO(Long id);
 
     void deleteByParentIds(@Param("ids") Long[] ids);
+
+    @Update({"<script> " +
+            " update fs_course_finish_temp " +
+            " set status = #{data.status} ," +
+            " update_time = #{data.updateTime} " +
+            " where id in " +
+            " <foreach collection='data.ids' item='id' open='(' separator=',' close=')'>" +
+              " #{id} " +
+            " </foreach> " +
+            "</script>"})
+    int updateFsCourseFinishTempBatch(@Param("data") FsCourseFinishTemp fsCourseFinishTemp);
 }

+ 4 - 1
fs-service/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java

@@ -5,10 +5,10 @@ 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.im.dto.OpenImBatchResponseDataDTO;
 import com.fs.qw.domain.QwExternalContact;
 import com.fs.qw.param.QwSidebarStatsParam;
 import com.fs.sop.vo.QwRatingVO;
+import com.fs.statis.dto.WatchCourseStatisticsResultDTO;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 import org.apache.ibatis.annotations.Update;
@@ -539,4 +539,7 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
      * 看课统计
      * */
     List<FsCourseWatchLogStatisticsListVO> selectQwFsCourseWatchLogStatisticsListVO(QwSidebarStatsParam param);
+
+    // 统计当天各公司的观看人数和完播人数, 存到redis中,定时任务每 ? 分钟执行一次
+    List<WatchCourseStatisticsResultDTO> watchCourseStatisticsGroupByCompany(@Param("params") Map<String, Object> params);
 }

+ 2 - 1
fs-service/src/main/java/com/fs/course/param/FsCourseWatchLogListParam.java

@@ -102,5 +102,6 @@ public class FsCourseWatchLogListParam implements Serializable {
      * 企微名称
      */
     private String qwUserName;
-
+    private Long deptId;
+    private String ids;
 }

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

@@ -77,4 +77,11 @@ public interface IFsCourseFinishTempService
      * 完课用户打备注
      */
     public void finishCourseExtContactIdByRemark(FsCourseWatchLog watchLog);
+
+    /**
+     * 批量更新完课模板状态
+     * @param fsCourseFinishTemp
+     * @return
+     */
+    int updateFsCourseFinishTempBatch(FsCourseFinishTemp fsCourseFinishTemp);
 }

+ 1 - 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.qw.param.QwSidebarStatsParam;
+import com.fs.qw.vo.QwWatchLogStatisticsListVO;
 
 import java.time.LocalDateTime;
 import java.util.List;

+ 9 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsCourseFinishTempServiceImpl.java

@@ -497,4 +497,13 @@ public class FsCourseFinishTempServiceImpl implements IFsCourseFinishTempService
             log.error("保存重试记录失败", e);
         }
     }
+
+    /**
+     * 批量更新完课模板状态
+     * @param fsCourseFinishTemp
+     * @return
+     */
+    public int updateFsCourseFinishTempBatch(FsCourseFinishTemp fsCourseFinishTemp){
+        return fsCourseFinishTempMapper.updateFsCourseFinishTempBatch(fsCourseFinishTemp);
+    }
 }

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

@@ -42,6 +42,7 @@ import com.fs.qw.param.QwSidebarStatsParam;
 import com.fs.qw.param.SendSopParamDetails;
 import com.fs.qw.vo.QwSopCourseFinishTempSetting;
 import com.fs.qw.vo.QwSopTempSetting;
+import com.fs.qw.vo.QwWatchLogStatisticsListVO;
 import com.fs.sop.domain.QwSopLogs;
 import com.fs.sop.mapper.SopUserLogsMapper;
 import com.fs.store.service.cache.IFsUserCacheService;
@@ -1241,4 +1242,5 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
         return fsCourseWatchLogMapper.selectQwFsCourseWatchLogStatisticsListVO(param);
     }
 
+
 }

+ 9 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -462,6 +462,10 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
             return R.error(504,"未授权");
         }
 
+        if (StringUtil.strIsNullOrEmpty(fsUser.getMpOpenId())){
+            return R.error(401,"授权后可继续!");
+        }
+
         if (fsUser.getStatus()==0){
             return R.error("会员被停用,无权限,请联系客服!");
         }
@@ -1110,6 +1114,11 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
             if (user.getStatus()==0){
                 return R.error("会员被停用,无权限,请联系客服!");
             }
+
+            if (StringUtil.strIsNullOrEmpty(user.getMpOpenId())){
+                return R.error(401,"授权后可继续!");
+            }
+
             FsCourseWatchLog watchLog = new FsCourseWatchLog();
 
             // 根据链接类型判断是否已发放奖励

+ 7 - 2
fs-service/src/main/java/com/fs/qw/mapper/QwGroupChatUserMapper.java

@@ -97,15 +97,20 @@ public interface QwGroupChatUserMapper
             "</script> ")
     public List<HashMap<String,String>> getDataWeekMonthCount(@Param("map") QwGroupChatUserDataType qwGroupChatUserDataType);
 
-    @Select("select " +
+    @Select(" <script> " +
+            " select " +
             "DATE_FORMAT(join_time,'%Y-%m') as toDay, " +
             "COUNT(join_time) AS dailyJoinCount , " +
             "COUNT(out_time) AS dailyOutCount " +
             "from qw_group_chat_user " +
             "where DATE_FORMAT(join_time,'%Y-%m')> DATE_FORMAT(date_sub(curdate(), interval 12 month),'%Y-%m') " +
+            " <if test='map.chatId != null'>" +
+            " and chat_id = #{map.chatId}" +
+            " </if>" +
             "group  by  DATE_FORMAT(join_time,'%Y-%m') " +
             "ORDER BY  " +
-            "  DATE_FORMAT(join_time,'%Y-%m') asc ")
+            "  DATE_FORMAT(join_time,'%Y-%m') asc " +
+            " </script> ")
     public List<HashMap<String,String>> getDataWeekMonthCountMonth(@Param("map") QwGroupChatUserDataType qwGroupChatUserDataType);
 
 

+ 39 - 0
fs-service/src/main/java/com/fs/qw/mapper/QwWatchLogMapper.java

@@ -1,6 +1,7 @@
 package com.fs.qw.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.course.param.FsCourseWatchLogListParam;
 import com.fs.qw.domain.QwExternalContact;
 import com.fs.qw.domain.QwWatchLog;
 import com.fs.qw.param.QwWatchLogStatisticsListParam;
@@ -103,6 +104,44 @@ public interface QwWatchLogMapper extends BaseMapper<QwWatchLog>{
             "    DATE(qec.create_time) "+
             "</script>"})
     List<QwWatchLogStatisticsListVO> selectQwExtCountByDayAnd(QwWatchLogStatisticsListParam param);
+
+    @Select({"<script> " +
+            "SELECT\n" +
+            "    qec.qw_user_id id,\n" +
+            "    qu.qw_user_name AS qw_user_name, \n" +
+            "    DATE(qec.create_time) AS create_time, \n" +
+            "    COUNT(1) AS line,\n" +
+            "    COUNT(CASE WHEN qec.is_interact = 1 THEN 1 END) AS interact,\n" +
+            "    COUNT(CASE WHEN qec.`level` = 1 THEN 1 END) AS A,\n" +
+            "    COUNT(CASE WHEN qec.`level` = 2 THEN 1 END) AS B,\n" +
+            "    COUNT(CASE WHEN qec.`level` = 3 THEN 1 END) AS C,\n" +
+            "    COUNT(CASE WHEN qec.`level` = 4 THEN 1 END) AS D,\n" +
+            "    COUNT(CASE WHEN qec.`level` = 5 THEN 1 END) AS E,\n" +
+            "    COUNT(CASE WHEN qec.fs_user_id IS NOT NULL THEN 1 END) AS sign,\n" +
+            "    COUNT(CASE WHEN qec.`status` =3 THEN 1 END) AS los,\n" +
+            "    COUNT(CASE WHEN qec.`status` IN (4, 5,6) THEN 1 END) AS del\n" +
+            "FROM\n" +
+            "    qw_external_contact qec\n" +
+            "JOIN\n" +
+            "    qw_user qu ON qec.qw_user_id = qu.id \n" +
+            "left join company_user cu on qec.company_user_id = cu.user_id "+
+            "WHERE\n" +
+            "    DATE(qec.create_time) &gt;= DATE(#{sTime}) and  DATE(qec.create_time) &lt;= DATE(#{eTime}) and qec.company_id =#{companyId} " +
+            "<if test ='nickName !=null and nickName!=\"\"'>\n" +
+            "   and qu.qw_user_name like concat( #{nickName}, '%')\n" +
+            "</if>" +
+            "<if test ='deptId !=null and deptId!=\"\"'>\n" +
+            "   and cu.dept_id = #{deptId}\n" +
+            "</if>" +
+            "<if test ='ids !=null and ids!=\"\"'>\n" +
+            "   and qec.qw_user_id in (${ids})\n" +
+            "</if>" +
+            "GROUP BY\n" +
+            "    qec.qw_user_id, DATE(qec.create_time) \n" +
+            "ORDER BY\n" +
+            "    DATE(qec.create_time) "+
+            "</script>"})
+    List<QwWatchLogStatisticsListVO> selectQwExtCountByDayAnd(FsCourseWatchLogListParam param);
     @Select("select \n" +
             "COUNT(CASE WHEN day = 0 and status in (1,2) THEN 1 END) AS firstOnline,\n" +
             "COUNT(CASE WHEN day = 0 and status=2 THEN 1 END) AS firstOver,\n" +

+ 3 - 0
fs-service/src/main/java/com/fs/qw/service/IQwWatchLogService.java

@@ -2,6 +2,7 @@ package com.fs.qw.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.fs.common.core.page.TableDataInfo;
+import com.fs.course.param.FsCourseWatchLogListParam;
 import com.fs.qw.domain.QwWatchLog;
 import com.fs.qw.param.QwWatchLogStatisticsListParam;
 import com.fs.qw.vo.QwWatchLogAllStatisticsListVO;
@@ -70,4 +71,6 @@ public interface IQwWatchLogService extends IService<QwWatchLog>{
     TableDataInfo selectQwWatchLogAllStatisticsListVONew(QwWatchLogStatisticsListParam param);
 
     TableDataInfo selectQwWatchLogStatisticsListVONew(QwWatchLogStatisticsListParam param);
+
+    List<QwWatchLogStatisticsListVO> selectQwWatchLogStatisticsListVOExport(FsCourseWatchLogListParam param);
 }

+ 24 - 0
fs-service/src/main/java/com/fs/qw/service/impl/QwWatchLogServiceImpl.java

@@ -16,6 +16,7 @@ import com.fs.company.mapper.CompanyDeptMapper;
 import com.fs.company.mapper.CompanyUserMapper;
 import com.fs.course.domain.FsUserCourse;
 import com.fs.course.domain.FsUserCourseVideo;
+import com.fs.course.param.FsCourseWatchLogListParam;
 import com.fs.course.service.cache.IFsUserCourseVideoCacheService;
 import com.fs.his.domain.FsUser;
 import com.fs.qw.domain.QwWatchLog;
@@ -144,6 +145,29 @@ public class QwWatchLogServiceImpl extends ServiceImpl<QwWatchLogMapper, QwWatch
         return baseMapper.deleteQwWatchLogById(id);
     }
 
+
+    @Override
+    public List<QwWatchLogStatisticsListVO> selectQwWatchLogStatisticsListVOExport(FsCourseWatchLogListParam param) {
+        CompanyDept companyDept = companyDeptMapper.selectCompanyDeptById(param.getDeptId());
+        if (ObjectUtils.isNotEmpty(companyDept)&&companyDept.getParentId()==0L){
+            param.setDeptId(null);
+        }
+        if (param.getCompanyUserId()!=null){
+            param.setIds(companyUserMapper.selectQwUserIdsByCompany(param.getCompanyUserId()));
+        }
+        List<QwWatchLogStatisticsListVO> vos = qwWatchLogMapper.selectQwExtCountByDayAnd(param);
+        for (QwWatchLogStatisticsListVO vo : vos) {
+            Long id = vo.getId();
+            Date createTime = vo.getCreateTime();
+            QwWatchLogStatisticsListVO stat = qwWatchLogMapper.selectQwWatchLogByQwUserId(id, createTime);
+            vo.setD1Online(stat.getD1Online());
+            vo.setD1Over(stat.getD1Over());
+            vo.setFirstOnline(stat.getFirstOnline());
+            vo.setFirstOver(stat.getFirstOver());
+        }
+        return vos;
+    }
+
     @Override
     public TableDataInfo selectQwWatchLogStatisticsListVO(QwWatchLogStatisticsListParam param) {
         CompanyDept companyDept = companyDeptMapper.selectCompanyDeptById(param.getDeptId());

+ 17 - 1
fs-service/src/main/java/com/fs/qw/vo/QwWatchLogStatisticsListVO.java

@@ -1,6 +1,7 @@
 package com.fs.qw.vo;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
 import lombok.Data;
 
 import java.util.Date;
@@ -9,10 +10,12 @@ import java.util.Date;
 public class QwWatchLogStatisticsListVO {
     private Long id;
     private String qwUserName;
+    @Excel(name = "企微员工名称")
     private String companyUserName;
     private Long companyUserId;
     private Long companyId;
     @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "进线时间",dateFormat = "yyyy-MM-dd")
     private Date createTime;
     /**
      * 项目
@@ -31,20 +34,33 @@ public class QwWatchLogStatisticsListVO {
      */
     private Long videoId;
     private String videoName;
-
+    @Excel(name = "进线数")
     private Long line;//进线数
+    @Excel(name = "先导课上线")
     private Long firstOnline;//先导课上线
+    @Excel(name = "先导课完课")
     private Long firstOver;//先导课完课
+    @Excel(name = "首日上线")
     private Long d1Online;//首日上线
+    @Excel(name = "首日完课")
     private Long d1Over;//首日完课
+    @Excel(name = "综合报名数")
     private Long sign;//综合报名数
+    @Excel(name = "互动数")
     private Long interact;//互动数
+    @Excel(name = "A级用户")
     private Long a;
+    @Excel(name = "B级用户")
     private Long b;
+    @Excel(name = "C级用户")
     private Long c;
+    @Excel(name = "D级用户")
     private Long d;
+    @Excel(name = "E级用户")
     private Long e;
+    @Excel(name = "拉黑数")
     private Long black;
     private Long los;//流失数
+    @Excel(name = "删除数")
     private Long del;//删除数
 }

+ 29 - 0
fs-service/src/main/java/com/fs/statis/dto/WatchCourseStatisticsResultDTO.java

@@ -0,0 +1,29 @@
+package com.fs.statis.dto;
+
+import lombok.Data;
+
+/**
+ * @description: 看客统计实体类 没有统计观看完成人数
+ * @author: Xgb
+ * @createDate: 2025/10/27
+ * @version: 1.0
+ */
+@Data
+public class WatchCourseStatisticsResultDTO {
+
+    // 公司id
+    private String companyId;
+    // 公司名称
+    private String companyName;
+    // 1.个微 手动 2.企微 自动
+    private String sendType;
+    // 观看用户数
+    private Integer watchUserCount;
+    // 观看次数
+    private Integer watchCount;
+    // 观看完成数
+    private Integer finishCount;
+    // 观看完成用户数
+    private Integer finishUserCount;
+
+}

+ 5 - 0
fs-service/src/main/java/com/fs/statis/service/IStatisticsService.java

@@ -115,4 +115,9 @@ public interface IStatisticsService {
      * 查询看课统计
      */
     WatchCourseStatisticsDTO queryWatchCourse(WatchCourseStatisticsParam param);
+
+    /**
+     * 统计今日看课
+     */
+    void watchCourseStatisticsGroupByCompany();
 }

+ 32 - 0
fs-service/src/main/java/com/fs/statis/service/impl/StatisticsServiceImpl.java

@@ -1,10 +1,14 @@
 package com.fs.statis.service.impl;
 
 import com.alibaba.fastjson.JSONObject;
+import com.fs.common.constant.FsConstants;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
+import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.TimeUtils;
+import com.fs.common.utils.model.DateTimeEntity;
 import com.fs.company.cache.ICompanyCacheService;
+import com.fs.course.domain.FsCourseWatchLog;
 import com.fs.course.dto.WatchLogDTO;
 import com.fs.course.mapper.FsCourseTrafficLogMapper;
 import com.fs.course.mapper.FsCourseWatchLogMapper;
@@ -24,6 +28,7 @@ import com.fs.statis.service.utils.TrendDataFiller;
 import com.fs.store.service.cache.IFsUserCourseCacheService;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.service.ISysConfigService;
+import com.hc.openapi.tool.fastjson.JSON;
 import com.hc.openapi.tool.util.ObjectUtils;
 import com.hc.openapi.tool.util.StringUtils;
 import org.apache.http.util.Asserts;
@@ -38,7 +43,9 @@ import java.time.LocalTime;
 import java.time.format.DateTimeFormatter;
 import java.time.temporal.TemporalAdjusters;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.stream.Collectors;
 import com.fs.statistics.dto.WatchCourseStatisticsDTO;
 
@@ -80,6 +87,8 @@ public class StatisticsServiceImpl implements IStatisticsService {
     @Autowired
     private IQwIpadServerService qwIpadServerService;
 
+
+
     @Override
     public void dataOverviewTask() {
         DealerAggregatedDTO dealerAggregatedDTO = this.dealerAggregated();
@@ -998,4 +1007,27 @@ public class StatisticsServiceImpl implements IStatisticsService {
 
         return watchCourseStatisticsDTO;
     }
+
+    /**
+     * @Description: 统计按TimeType 0-今天,1-昨天,2-本周,3-本月,4-上月;各公司的观看人数和完播人数, 存到redis中,定时任务每 ? 分钟执行一次
+     * @Param:
+     * @Return:
+     * @Author xgb
+     * @Date 2025/10/27 13:09
+     */
+    @Override
+    public void watchCourseStatisticsGroupByCompany() {
+        for(int i = 0; i < 5; i++){
+            Map<String,Object> params=new HashMap<>();
+            DateTimeEntity timeEntity =DateUtils.getBetweenTime(i);
+            params.put("startTime",timeEntity.getStartTime());
+            params.put("endTime",timeEntity.getEndTime());
+
+            List<WatchCourseStatisticsResultDTO> watchCourseStatisticsDTOS=fsCourseWatchLogMapper.watchCourseStatisticsGroupByCompany(params);
+            // 存到redis中 按TimeType 0-今天,1-昨天,2-本周,3-本月,4-上月;
+            redisCache.setCacheObject(FsConstants.WATCH_COURSE_STATISTICS_GROUP_COMPANY+i, JSON.toJSONString(watchCourseStatisticsDTOS));
+        }
+    }
+
+
 }

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

@@ -973,4 +973,25 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         o.create_time DESC
     </select>
 
+    <!-- 统计当天各公司的观看人数和完播人数, 存到redis中,定时任务每 ? 分钟执行一次    -->
+    <select id="watchCourseStatisticsGroupByCompany" resultType="com.fs.statis.dto.WatchCourseStatisticsResultDTO">
+        SELECT
+            o.company_id AS companyId,
+            c.company_name AS companyName,
+            o.send_type,
+            COUNT(DISTINCT o.user_id) AS watchUserCount,
+            COUNT(o.log_id) AS watchCount,
+            sum(case when o.log_type = 2 then 1 else 0 end)  AS finishCount,
+            COUNT(DISTINCT CASE WHEN o.log_type = 2 THEN o.user_id END) AS finishUserCount
+        FROM
+            fs_course_watch_log o
+            LEFT JOIN company c ON c.company_id = o.company_id
+        WHERE
+            o.create_time &gt;= #{params.startTime}
+            AND o.create_time &lt;= #{params.endTime}
+        GROUP BY
+            o.company_id,
+            o.send_type
+    </select>
+
 </mapper>

+ 1 - 0
fs-service/src/main/resources/mapper/his/FsExportTaskMapper.xml

@@ -29,6 +29,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="companyUserId != null "> and company_user_id = #{companyUserId}</if>
             <if test="startTime != null "> and start_time = #{startTime}</if>
             <if test="finishTime != null "> and finish_time = #{finishTime}</if>
+            <if test="status != null "> and status = #{status}</if>
             <if test="fileUrl != null  and fileUrl != ''"> and file_url = #{fileUrl}</if>
         </where>
         order by task_id desc

+ 2 - 2
fs-service/src/main/resources/mapper/qw/QwFriendWelcomeMapper.xml

@@ -64,13 +64,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                 and company_id = #{companyId}
             </if>
             <if test="createTime != null ">
-                and create_time = #{createTime}
+                and DATE(create_time) = #{createTime}
             </if>
             <if test="corpId != null ">
                 and corp_id = #{corpId}
             </if>
             <if test="updateTime != null ">
-                and update_time = #{updateTime}
+                and DATE(update_time) = #{updateTime}
             </if>
             ${params.dataScope}
         </where>

+ 1 - 0
fs-service/src/main/resources/mapper/qw/QwUserMapper.xml

@@ -43,6 +43,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <select id="selectQwUserList" parameterType="QwUser" resultMap="QwUserResult">
         <include refid="selectQwUserVo"/>
         <where>
+            1=1 and is_del = 0 and company_user_id is not null
             <if test="qwUserId != null  and qwUserId != ''"> and qw_user_id = #{qwUserId}</if>
             <if test="qwUserName != null  and qwUserName != ''"> and qw_user_name like concat('%', #{qwUserName}, '%')</if>
             <if test="department != null  and department != ''"> and department = #{department}</if>