xdd 1 周之前
父节点
当前提交
86f099467f

+ 64 - 0
fs-admin/src/main/java/com/fs/qw/FsCourseTask.java

@@ -0,0 +1,64 @@
+package com.fs.qw;
+
+import com.fs.course.service.IFsCourseWatchLogService;
+import com.fs.qw.service.IQwWorkTaskService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * 后台统计相关 定时任务
+ */
+@Slf4j
+@Service("qwFsCourseTask")
+public class FsCourseTask {
+    @Autowired
+    private IFsCourseWatchLogService fsCourseWatchLogService;
+    @Autowired
+    private IQwWorkTaskService qwWorkTaskService;
+
+    /**
+     * 添加企微观看日志
+     * @throws Exception
+     */
+    public void addQwWatchLog() throws Exception
+    {
+        fsCourseWatchLogService.addCourseWatchLogDay();
+    }
+
+    /**
+     * 企微任务定时更新
+     * @throws Exception
+     */
+    public void qwWorkTask1() throws Exception
+    {
+        qwWorkTaskService.addQwWorkByCourse4();
+        qwWorkTaskService.addQwWorkByCourseLastTime();
+    }
+    /**
+     * 企微待看课和先导课
+     * @throws Exception
+     */
+    public void qwWorkTask2() throws Exception
+    {
+        qwWorkTaskService.addQwWorkByCourse();
+        qwWorkTaskService.addQwWorkByFirstCourse();
+    }
+    /**
+     * 用户大小转
+     * @throws Exception
+     */
+    public void qwWorkTask3() throws Exception
+    {
+        qwWorkTaskService.addQwWorkByConversionDay();
+    }
+    /**
+     * 删除过期数据
+     * @throws Exception
+     */
+    public void qwWorkTask4() throws Exception
+    {
+        qwWorkTaskService.delQwWorkTaskByOver();
+    }
+
+}

+ 240 - 0
fs-company/src/main/java/com/fs/company/controller/company/IndexStatisticsController.java

@@ -0,0 +1,240 @@
+package com.fs.company.controller.company;
+
+import com.fs.common.core.domain.R;
+import com.fs.common.core.redis.RedisCache;
+import com.fs.common.utils.ServletUtils;
+import com.fs.framework.security.LoginUser;
+import com.fs.framework.service.TokenService;
+import com.fs.statis.StatisticsRedisConstant;
+import com.fs.statis.dto.*;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.fs.statis.StatisticsRedisConstant.*;
+
+/**
+ * 首页-统计
+ */
+@RestController
+@RequestMapping("/index/statistics")
+public class IndexStatisticsController {
+    @Autowired
+    private RedisCache redisCache;
+
+    @Autowired
+    private TokenService tokenService;
+    /**
+     * 分析概览
+     */
+    @PostMapping("/analysisPreview")
+    public R analysisPreview(@RequestBody AnalysisPreviewQueryDTO param){
+        AnalysisPreviewDTO analysisPreviewDTO = null;
+        Integer type = param.getType();
+        Integer userType = param.getUserType();
+
+        if(type == null) {
+            type = 0;
+        }
+
+        if(userType == null) {
+            userType = 0;
+        }
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        param.setCompanyId(loginUser.getCompany().getCompanyId());
+        analysisPreviewDTO = redisCache.getCacheObject(String.format("%s:%d:%d:%d",DATA_OVERVIEW_DEALER_ANALYSISPREVIEW,type,userType,param.getCompanyId()));
+
+        return R.ok().put("data",analysisPreviewDTO);
+    }
+
+
+    /**
+     * 消费余额
+     */
+    @GetMapping("/rechargeComsumption")
+    public R rechargeComsumption(){
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long companyId = loginUser.getCompany().getCompanyId();
+
+        ConsumptionBalanceDataDTO consumptionBalanceDataDTO = redisCache.getCacheObject(String.format("%s:%d",StatisticsRedisConstant.DATA_OVERVIEW_DEALER_BALANCE,companyId));
+        return R.ok().put("data", consumptionBalanceDataDTO);
+    }
+
+    /**
+     * 获取统计流量
+     * @return
+     */
+    @GetMapping("/trafficLog")
+    public R getTrafficLog(){
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long companyId = loginUser.getCompany().getCompanyId();
+        TrafficLogDTO trafficLogDTO = redisCache.getCacheObject(String.format("%s:%d",DATA_OVERVIEW_TRAFFIC_LOG,companyId));
+        return R.ok().put("data",trafficLogDTO);
+    }
+
+    /**
+     * 观看趋势
+     */
+    @PostMapping("/watchEndPlayTrend")
+    public R watchEndPlayTrend(@RequestBody AnalysisPreviewQueryDTO param){
+        Integer type = param.getType();
+        Integer userType = param.getUserType();
+
+        if(type == null) {
+            type = 0;
+        }
+        if(userType == null){
+            userType = 0;
+        }
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long companyId = loginUser.getCompany().getCompanyId();
+        param.setCompanyId(companyId);
+
+        String key = String.format("%s:%d:%d:%d", DATA_OVERVIEW_DEALER_CHARTS, type,userType,param.getCompanyId());
+        List<DeaMemberTopTenDTO> deaMemberTopTenDTOS = redisCache.getCacheObject(key);
+        return R.ok().put("data", deaMemberTopTenDTOS);
+    }
+
+    /**
+     * 经销商会员观看
+     */
+    @PostMapping("/deaMemberTopTen")
+    public R deaMemberTopTen(@RequestBody AnalysisPreviewQueryDTO param){
+        Integer type = param.getType();
+        Integer statisticalType = param.getStatisticalType();
+        Integer userType = param.getUserType();
+
+        if(type == null) {
+            type = 0;
+        }
+        if(userType == null){
+            userType = 0;
+        }
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long companyId = loginUser.getCompany().getCompanyId();
+        param.setCompanyId(companyId);
+
+        List<DeaMemberTopTenDTO> deaMemberTopTenDTOS = redisCache.getCacheObject(String.format("%s:%d:%d:%d:%d", CHARTS_MEMBER_TOP_TEN_WATCH, type, statisticalType,userType,param.getCompanyId()));
+        if(deaMemberTopTenDTOS == null){
+            deaMemberTopTenDTOS = new ArrayList<>();
+        }
+        return R.ok().put("data", deaMemberTopTenDTOS);
+    }
+
+    /**
+     * 奖励金额top10
+     */
+    @PostMapping("/rewardMoneyTopTen")
+    public R rewardMoneyTopTen(@RequestBody AnalysisPreviewQueryDTO param){
+        Integer type = param.getType();
+        Integer dataType = param.getDataType();
+        Integer userType = param.getUserType();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long companyId = loginUser.getCompany().getCompanyId();
+        param.setCompanyId(companyId);
+
+        List<RewardMoneyTopTenDTO> rewardMoneyTopTenDTOS = redisCache.getCacheObject( String.format("%s:%d:%d:%d:%d", CHARTS_REWARD_MONEY_TOP_TEN, type,dataType,userType,param.getCompanyId()));
+        return R.ok().put("data", rewardMoneyTopTenDTOS);
+    }
+
+    /**
+     * 答题红包金额趋势图
+     */
+    @PostMapping("/rewardMoneyTrend")
+    public R rewardMoneyTrend(@RequestBody AnalysisPreviewQueryDTO param){
+        Integer type = param.getType();
+        Integer userType = param.getUserType();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long companyId = loginUser.getCompany().getCompanyId();
+        param.setCompanyId(companyId);
+
+        List<RewardMoneyTrendDTO> rewardMoneyTrendDTOS = redisCache.getCacheObject( String.format("%s:%d:%d:%d", CHARTS_REWARD_MONEY_TREND, type,userType,param.getCompanyId()));
+        return R.ok().put("data", rewardMoneyTrendDTOS);
+    }
+
+    /**
+     * 课程观看top10
+     */
+    @PostMapping("/watchCourseTopTen")
+    public R watchCourseTopTen(@RequestBody AnalysisPreviewQueryDTO param){
+        Integer type = param.getType();
+        String sort = param.getSort();
+        Integer statisticalType = param.getStatisticalType();
+        Integer userType = param.getUserType();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long companyId = loginUser.getCompany().getCompanyId();
+        param.setCompanyId(companyId);
+
+        List<CourseStatsDTO> courseStatsDTOS = redisCache.getCacheObject(String.format("%s:%d:%d:%d:%s:%d", CHARTS_WATCH_TOP_TEN, type,statisticalType,userType,sort,param.getCompanyId()));
+        return R.ok().put("data", courseStatsDTOS);
+    }
+
+    /**
+     * 数据概览
+     */
+    @GetMapping("/dealerAggregated")
+    public R dealerAggregated(){
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long companyId = loginUser.getCompany().getCompanyId();
+
+        DealerAggregatedDTO dealerAggregatedDTO = redisCache.getCacheObject(String.format("%s:%d",StatisticsRedisConstant.DATA_OVERVIEW_DEALER_AGGREGATED,companyId));
+
+        return R.ok().put("data",dealerAggregatedDTO);
+    }
+
+    /**
+     * 短信余额
+     */
+    @GetMapping("/smsBalance")
+    public R smsBalance(){
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long companyId = loginUser.getCompany().getCompanyId();
+
+        Long smsBalance = redisCache.getCacheObject(String.format("%s:%d",StatisticsRedisConstant.DATA_OVERVIEW_DEALER_SMS_BALANCE,companyId));
+
+        return R.ok().put("data", smsBalance);
+    }
+
+
+    /**
+     * 授权信息
+     */
+    @GetMapping("/authorizationInfo")
+    public R authorizationInfo(){
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long companyId = loginUser.getCompany().getCompanyId();
+
+        AuthorizationInfoDTO authorizationInfoDTO = redisCache.getCacheObject(String.format("%s:%d",StatisticsRedisConstant.DATA_OVERVIEW_DEALER_AUTHORIZATION_INFO,companyId));
+
+        return R.ok().put("data", authorizationInfoDTO);
+    }
+
+
+    /**
+     * 当月订单数统计
+     * @return
+     */
+    @GetMapping("/thisMonthOrderCount")
+    public R thisMonthOrderCount(){
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long companyId = loginUser.getCompany().getCompanyId();
+
+        R result = redisCache.getCacheObject(String.format("%s:%d",StatisticsRedisConstant.THIS_MONTH_ORDER_COUNT,companyId));
+        return result;
+    }
+
+    /**
+     * 当月收益统计
+     * @return
+     */
+    @GetMapping("/thisMonthRecvCount")
+    public R thisMonthRecvCount(){
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long companyId = loginUser.getCompany().getCompanyId();
+
+        R result = redisCache.getCacheObject(String.format("%s:%d",StatisticsRedisConstant.THIS_MONTH_RECV_COUNT,companyId));
+        return result;
+    }
+}

+ 1 - 0
fs-qw-task/src/main/java/com/fs/app/task/qwTask.java

@@ -206,6 +206,7 @@ public class qwTask {
         }
     }
 
+
     // 定义一个方法来批量处理插入逻辑,支持每 500 条数据一次的批量插入
     private void processAndInsertQwSopLogs(List<QwSopLogsDoSendListTVO> logsByJsApiNotExtId) {
         // 定义批量插入的大小

+ 2 - 0
fs-service/src/main/java/com/fs/qw/service/IQwWorkTaskService.java

@@ -75,4 +75,6 @@ public interface IQwWorkTaskService extends IService<QwWorkTask>{
 
     void delQwWorkTaskByOver();
 
+    void addQwWorkByCourseLastTime();
+
 }

+ 69 - 0
fs-service/src/main/java/com/fs/qw/service/impl/QwWorkTaskServiceImpl.java

@@ -20,6 +20,7 @@ import com.fs.sop.mapper.SopUserLogsInfoMapper;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import java.text.SimpleDateFormat;
 import java.time.LocalDate;
 import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
@@ -319,6 +320,74 @@ public class QwWorkTaskServiceImpl extends ServiceImpl<QwWorkTaskMapper, QwWorkT
 
     }
 
+    @Override
+    public void addQwWorkByCourseLastTime() {
+        List<QwSop> qwSops = qwSopMapper.selectQwSopByIsRating();
+        List<Long> extIds = qwWorkTaskMapper.selectQwWorkTaskByType();
+        SimpleDateFormat sdf = new SimpleDateFormat("HHmm"); // 24小时制,如 1100
+        String timeStr = sdf.format(new Date());
+        int lastTime = Integer.parseInt(timeStr);
+        Set<Long> extIdSet = new HashSet<>(extIds);
+        LocalDate today = LocalDate.now();
+        for (QwSop qwSop : qwSops) {
+            if (qwSop.getCourseDay()==null){
+                continue;
+            }
+            Integer courseDay=qwSop.getCourseDay()-1;
+            List<SopUserLogsInfo> qwSopLogs = sopUserLogsInfoMapper.selectSopUserLogsInfoBySopId(qwSop.getId());
+            if (qwSopLogs==null || qwSopLogs.isEmpty()) {
+                continue;
+            }
+
+            List<FsCourseWatchLogTaskVO> fsCourseWatchLogs = courseWatchLogMapper.selectFsCourseWatchLogByDaySopId3LastTime(qwSop.getId(),lastTime);
+            if (fsCourseWatchLogs==null || fsCourseWatchLogs.isEmpty()) {
+                continue;
+            }
+            List<QwWorkTask> qwWorkTasks = new ArrayList<>();
+            for (SopUserLogsInfo qwSopLog : qwSopLogs) {
+                Map<Long, FsCourseWatchLogTaskVO> map = fsCourseWatchLogs.stream()
+                        .collect(Collectors.toMap(FsCourseWatchLogTaskVO::getQwExternalContactId, data -> data,(oldValue, newValue) -> newValue ));
+                FsCourseWatchLogTaskVO fsCourseWatchLog = map.get(qwSopLog.getExternalId());
+
+                if (fsCourseWatchLog == null) {
+                    continue;
+                }
+                if (extIdSet.contains(fsCourseWatchLog.getQwExternalContactId())) {
+                    continue;
+                }
+                String createTime = qwSopLog.getCreateTime();
+                LocalDate createDate = LocalDate.parse(createTime.substring(0, 10), DateTimeFormatter.ofPattern("yyyy-MM-dd"));
+
+                Integer day = (Math.toIntExact(ChronoUnit.DAYS.between(createDate, today))) + 1 - courseDay;
+                if (day<=7){
+                    continue;
+                }
+                Integer score = getQwWorkCourseScore(fsCourseWatchLog.getLogType(), day,fsCourseWatchLog.getLevel());
+                if (score==0){
+                    continue;
+                }
+                QwWorkTask qwWorkTask = new QwWorkTask();
+                qwWorkTask.setCreateTime(DateUtils.getNowDate());
+                qwWorkTask.setExtId(fsCourseWatchLog.getQwExternalContactId());
+                qwWorkTask.setCompanyId(fsCourseWatchLog.getCompanyId());
+                qwWorkTask.setCompanyUserId(fsCourseWatchLog.getCompanyUserId());
+                qwWorkTask.setSopId(qwSop.getId());
+                qwWorkTask.setQwUserId(fsCourseWatchLog.getQwUserId());
+                qwWorkTask.setType(2);
+                qwWorkTask.setStatus(0);
+                qwWorkTask.setTitle("第"+day+"天"+"待看课");
+                qwWorkTask.setScore(score);
+                qwWorkTasks.add(qwWorkTask);
+
+            }
+            if (!qwWorkTasks.isEmpty()){
+
+                qwWorkTaskMapper.insertQwWorkTaskBatch(qwWorkTasks);
+            }
+
+        }
+    }
+
     private void addQwWorkTask(LocalDate today, Integer day, QwSop qwSop, String title,Map<Integer, Integer> map) {
         if (day>7){
             DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");

+ 394 - 0
fs-service/src/main/resources/mapper/statis/ConsumptionBalanceMapper.xml

@@ -0,0 +1,394 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.statis.mapper.ConsumptionBalanceMapper">
+
+
+    <select id="rechargeConsumption" resultType="com.fs.statis.dto.ConsumptionBalanceDataDTO">
+        select
+            14371277 AS balance,
+            103100 AS today_comsumption,
+            195280 AS yesterday_comsumption
+    </select>
+    <select id="dealerAggregated" resultType="com.fs.statis.dto.DealerAggregatedDTO">
+        SELECT
+                (SELECT COUNT(*) FROM COMPANY) AS dealder_count,
+                (SELECT COUNT(*) FROM COMPANY_USER) AS group_mgr_count,
+                (SELECT COUNT(*) FROM FS_USER) AS member_count,
+                (SELECT COUNT(*) FROM FS_USER WHERE STATUS=1) AS normal_num,
+                (SELECT COUNT(*) FROM FS_USER WHERE STATUS=0) AS black_num,
+                (select COUNT(*) FROM qw_user) AS qw_member_num
+    </select>
+    <select id="analysisPreview" resultType="com.fs.statis.dto.AnalysisPreviewDTO">
+        -- 观看人数
+        select (select count(*) from fs_course_watch_log where create_time between #{startTime} and #{endTime} group by user_id) as watch_count,
+-- 完播人数
+               (select count(*) from fs_course_watch_log where finish_time is not null and create_time between #{startTime} and #{endTime} group by user_id)
+                                                                                                                                                     as completed_user_count,
+-- 观看次数
+               (select count(*) from fs_course_watch_log where create_time between #{startTime} and #{endTime}) as watch_count,
+-- 完播次数
+               (select count(*) from fs_course_watch_log where create_time between #{startTime} and #{endTime}) as completed_count,
+-- 答题人数
+               (select count(*) from fs_course_answer_logs where create_time between #{startTime} and #{endTime}) as answer_member_count,
+-- 正确人数
+               (select count(*) from fs_course_answer_logs where is_right=1 AND create_time between #{startTime} and #{endTime}) as correct_user_count,
+-- 答题红包个数
+               (select 0) as reward_type,
+               (select 0) as reward_money
+    </select>
+    <select id="queryWatchCount" resultType="java.lang.Long">
+        select count(log_id) from fs_course_watch_log
+        <where>
+            <if test="userType != null">
+                and send_type=${userType}
+            </if>
+            <if test="startTime != null and endTime != null">
+                and create_time between #{startTime} and #{endTime}
+            </if>
+            <if test="companyId != null">
+                and company_id = #{companyId}
+            </if>
+        </where>
+    </select>
+    <select id="queryCompletedUserCount" resultType="java.lang.Long">
+        select count(DISTINCT user_id) from fs_course_watch_log
+        <where>
+            finish_time is not null
+            <if test="userType != null">
+                and send_type=${userType}
+            </if>
+            <if test="startTime != null and endTime != null">
+                and create_time between #{startTime} and #{endTime}
+            </if>
+            <if test="companyId != null">
+                and company_id = #{companyId}
+            </if>
+        </where>
+    </select>
+    <select id="queryWatchUserCount" resultType="java.lang.Long">
+        select count(distinct user_id) from fs_course_watch_log
+        <where>
+            <if test="userType != null">
+                and send_type=${userType}
+            </if>
+            <if test="startTime != null and endTime != null">
+                and create_time between #{startTime} and #{endTime}
+            </if>
+            <if test="companyId != null">
+                and company_id = #{companyId}
+            </if>
+
+        </where>
+    </select>
+    <select id="queryCompletedCount" resultType="java.lang.Long">
+        SELECT COUNT(log_id) FROM fs_course_watch_log
+         <where>
+             finish_time IS NOT NULL
+             <if test="startTime != null and endTime != null">
+                 AND create_time BETWEEN #{startTime} AND #{endTime}
+             </if>
+             <if test="userType != null">
+                 and send_type=${userType}
+             </if>
+             <if test="companyId != null">
+                 and company_id = #{companyId}
+             </if>
+         </where>
+    </select>
+    <select id="queryAnswerMemberCount" resultType="java.lang.Long">
+        SELECT COUNT(DISTINCT user_id) FROM fs_course_answer_logs
+       <where>
+           <if test="startTime != null and endTime != null">
+               create_time BETWEEN #{startTime} AND #{endTime}
+           </if>
+           <if test="companyId != null">
+               and company_id = #{companyId}
+           </if>
+       </where>
+    </select>
+    <select id="queryCorrectUserCount" resultType="java.lang.Long">
+        SELECT COUNT(DISTINCT user_id) FROM fs_course_answer_logs
+        <where>
+            is_right = 1
+            <if test="startTime != null and endTime != null">
+                and create_time BETWEEN #{startTime} AND #{endTime}
+            </if>
+            <if test="companyId != null">
+                and company_id = #{companyId}
+            </if>
+        </where>
+    </select>
+    <select id="queryRewardCount" resultType="java.lang.Long">
+        select count(*) from company_red_package_logs rpl
+            left join fs_course_watch_log log
+            on rpl.watch_log_id=log.log_id
+        <where>
+            rpl.operate_type=1
+            <if test="startTime != null and endTime != null">
+                and rpl.create_time BETWEEN #{startTime} AND #{endTime}
+            </if>
+            <if test="userType != null">
+                and log.send_type = ${userType}
+            </if>
+            <if test="companyId != null">
+                and log.company_id = #{companyId}
+            </if>
+        </where>
+    </select>
+    <select id="queryRewardMoney" resultType="java.math.BigDecimal">
+        select sum(up_money) from company_red_package_logs rpl
+        left join fs_course_watch_log log
+        on rpl.watch_log_id=log.log_id
+        <where>
+            rpl.operate_type=1
+            <if test="startTime != null and endTime != null">
+                and rpl.create_time BETWEEN #{startTime} AND #{endTime}
+            </if>
+            <if test="userType != null">
+                and log.send_type = ${userType}
+            </if>
+            <if test="companyId != null">
+                and log.company_id = #{companyId}
+            </if>
+        </where>
+    </select>
+    <select id="smsBalance" resultType="java.lang.Long">
+        select sum(remain_sms_count) from company_sms
+    </select>
+    <select id="smsBalanceCompany" resultType="java.lang.Long">
+        select sum(remain_sms_count) from company_sms
+        <where>
+            <if test="companyId != null">
+                company_id = #{companyId}
+            </if>
+        </where>
+    </select>
+    <select id="authorizationInfo" resultType="com.fs.statis.dto.AuthorizationInfoDTO">
+        select version_limit,today_watch_user_count from fs_statistics_index limit 1
+    </select>
+    <select id="watchEndPlayTrend" resultType="com.fs.statis.dto.WatchEndPlayTrendDTO">
+        SELECT
+--             今日/昨日 小时
+            <if test="type == 0 or type == 1">
+                DATE_FORMAT(create_time, '%H') AS start_date,
+            </if>
+--                 本周/本月/上月 天
+            <if test="type == 2 or type == 3 or type == 4">
+                DATE_FORMAT(create_time, '%Y-%m-%d') AS start_date,
+            </if>
+        COUNT(DISTINCT user_id) AS watch_user_count,
+        COUNT(DISTINCT CASE WHEN log_type = 2 THEN user_id END) AS completed_user_count
+        FROM
+        fs_course_watch_log
+        <where>
+            <if test="startTime != null">
+                create_time <![CDATA[>=]]> #{startTime}
+            </if>
+            <if test="endTime != null">
+                AND create_time <![CDATA[<]]> #{endTime}
+            </if>
+            <if test="userType != null">
+                AND send_type = ${userType}
+            </if>
+        </where>
+        GROUP BY
+        start_date
+        ORDER BY
+        start_date
+    </select>
+    <select id="deaMemberTopTen" resultType="com.fs.statis.dto.DeaMemberTopTenDTO">
+        SELECT
+        company_id,
+        <if test="statisticalType == 0">
+            count(DISTINCT user_id) AS watch_user_count
+        </if>
+        <if test="statisticalType == 1">
+            COUNT(DISTINCT CASE WHEN log_type = 2 THEN user_id END) AS watch_user_count
+        </if>
+        FROM
+        fs_course_watch_log
+        <where>
+            <if test="startTime != null">
+                create_time <![CDATA[>=]]> #{startTime}
+            </if>
+            <if test="endTime != null">
+                AND create_time <![CDATA[<]]> #{endTime}
+            </if>
+            <if test="userType != null">
+                AND send_type = ${userType}
+            </if>
+            <if test="companyId != null">
+                AND company_id = ${companyId}
+            </if>
+        </where>
+        GROUP BY company_id
+        limit 10
+    </select>
+    <select id="watchCourseTopTen" resultType="com.fs.statis.dto.CourseStatsDTO">
+        SELECT
+        w.course_id AS course_id,
+        COUNT(DISTINCT w.user_id) AS watch_user_count,
+        COUNT(DISTINCT CASE WHEN w.log_type = 2 THEN w.user_id END) AS completed_user_count,
+        COUNT(DISTINCT a.user_id) AS answer_user_count,
+        COUNT(DISTINCT CASE WHEN a.is_right = 1 THEN a.user_id END) AS correct_user_count
+        FROM
+        fs_course_watch_log w
+        LEFT JOIN
+        fs_course_answer_logs a ON w.video_id = a.video_id AND w.user_id = a.user_id
+        <where>
+            <if test="startTime != null">
+                w.create_time <![CDATA[>=]]> #{startTime}
+            </if>
+            <if test="endTime != null">
+                AND w.create_time <![CDATA[<]]> #{endTime}
+            </if>
+            <if test="userType != null">
+                AND send_type = ${userType}
+            </if>
+            <if test="companyId != null">
+                AND w.company_id = ${companyId}
+            </if>
+        </where>
+        GROUP BY
+        w.course_id
+        ORDER BY
+            -- 观看人数
+            <if test="statisticalType == 0">
+                COUNT(DISTINCT w.user_id)
+            </if>
+            <if test="statisticalType == 1">
+                COUNT(DISTINCT CASE WHEN w.log_type = 2 THEN w.user_id END)
+            </if>
+            <if test="statisticalType == 2">
+                COUNT(DISTINCT a.user_id)
+            </if>
+            <if test="statisticalType == 3">
+                COUNT(DISTINCT CASE WHEN a.is_right = 1 THEN a.user_id END)
+            </if>
+        ${sort}
+        LIMIT 10
+    </select>
+    <select id="rewardMoneyTopTen" resultType="com.fs.statis.dto.RewardMoneyTopTenDTO">
+        SELECT
+            -- 按公司
+            <if test="dataType == 0">
+                rpl.company_id as company_id,
+            </if>
+            -- 按课程
+            <if test="dataType == 1">
+                rpl.course_id as course_id,
+            </if>
+            SUM(rpl.money) as rewardMoney
+        FROM
+            company_red_package_logs rpl
+        left join fs_course_watch_log log
+        on rpl.watch_log_id=log.log_id
+        <where>
+            rpl.operate_type = 1
+            AND rpl.status = 1
+            <if test="startTime != null">
+                AND rpl.create_time <![CDATA[>=]]> #{startTime}
+            </if>
+            <if test="endTime != null">
+                AND rpl.create_time <![CDATA[<]]> #{endTime}
+            </if>
+            <if test="userType != null">
+                and log.send_type = ${userType}
+            </if>
+            <if test="companyId != null">
+                and log.company_id = ${companyId}
+            </if>
+        </where>
+        GROUP BY
+            <if test="dataType == 0">
+                rpl.company_id
+            </if>
+            <if test="dataType == 1">
+                rpl.course_id
+            </if>
+        ORDER BY
+            rewardMoney DESC
+            LIMIT 10
+    </select>
+    <select id="rewardMoneyTrendDTO" resultType="com.fs.statis.dto.RewardMoneyTrendDTO">
+        select
+        --             今日/昨日 小时
+        <if test="type == 0 or type == 1">
+            DATE_FORMAT(rpl.create_time, '%H') AS start_date,
+        </if>
+        --                 本周/本月/上月 天
+        <if test="type == 2 or type == 3 or type == 4">
+            DATE_FORMAT(rpl.create_time, '%Y-%m-%d') AS start_date,
+        </if>
+               SUM(rpl.money) as rewardMoney
+        from company_red_package_logs rpl
+        left join fs_course_watch_log log
+        on rpl.watch_log_id=log.log_id
+        <where>
+            <if test="startTime != null">
+                rpl.create_time <![CDATA[>=]]> #{startTime}
+            </if>
+            <if test="endTime != null">
+                AND rpl.create_time <![CDATA[<]]> #{endTime}
+            </if>
+            <if test="userType != null">
+                and log.send_type = ${userType}
+            </if>
+            <if test="companyId != null">
+                and log.company_id = ${companyId}
+            </if>
+            AND rpl.operate_type = 1
+            AND rpl.status = 1
+        </where>
+        group by start_date
+    </select>
+    <select id="getCurrentBalance" resultType="java.math.BigDecimal">
+        select sum(money) from company
+    </select>
+    <select id="dealerAggregatedCompanyId" resultType="com.fs.statis.dto.DealerAggregatedDTO">
+        SELECT
+                1 AS dealder_count,
+                (SELECT COUNT(*) FROM COMPANY_USER
+                                 <where>
+                                     <if test="companyId != null">
+                                         company_id = #{companyId}
+                                     </if>
+                                 </where>
+                                 ) AS group_mgr_count,
+                (SELECT COUNT(*) FROM FS_USER
+                                <where>
+                                    <if test="companyId != null">
+                                        company_id = #{companyId}
+                                    </if>
+                                </where>
+                                ) AS member_count,
+                (SELECT COUNT(*) FROM FS_USER
+                                <where>
+                                    STATUS=1
+                                    <if test="companyId != null">
+                                        AND company_id = #{companyId}
+                                    </if>
+                                </where>
+                                ) AS normal_num,
+                (SELECT COUNT(*) FROM FS_USER
+                                <where>
+                                    STATUS=0
+                                    <if test="companyId != null">
+                                        AND company_id = #{companyId}
+                                    </if>
+                                </where>
+                ) AS black_num,
+                (select COUNT(*) FROM qw_user) AS qw_member_num
+    </select>
+    <select id="getCurrentBalanceCompanyId" resultType="java.math.BigDecimal">
+        select sum(money) from company
+        <where>
+            <if test="companyId != null">
+                company_id = #{companyId}
+            </if>
+        </where>
+    </select>
+
+</mapper>

+ 162 - 0
fs-service/src/main/resources/mapper/statis/FsStatisEveryDayMapper.xml

@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.statis.mapper.FsStatisEveryDayWatchMapper">
+
+    <!-- 结果集映射 -->
+    <resultMap id="BaseResultMap" type="com.fs.statis.domain.FsStatisEveryDayWatch">
+        <id column="id" jdbcType="INTEGER" property="id" />
+        <result column="data_date" jdbcType="TIMESTAMP" property="dataDate" />
+        <result column="period_id" jdbcType="INTEGER" property="periodId" />
+        <result column="period_num" jdbcType="INTEGER" property="periodNum" />
+        <result column="not_reg_num" jdbcType="INTEGER" property="notRegNum" />
+        <result column="registered_num" jdbcType="INTEGER" property="registeredNum" />
+        <result column="reg_rate" jdbcType="REAL" property="regRate" /> <!-- Use REAL or FLOAT for float type -->
+        <result column="completed_rate" jdbcType="REAL" property="completedRate" />
+        <result column="offline_total" jdbcType="INTEGER" property="offlineTotal" />
+        <result column="offline_not_reg_num" jdbcType="INTEGER" property="offlineNotRegNum" />
+        <result column="offline_not_watch_num" jdbcType="INTEGER" property="offlineNotWatchNum" />
+        <result column="online_total" jdbcType="INTEGER" property="onlineTotal" />
+        <result column="online_rate" jdbcType="REAL" property="onlineRate" />
+        <result column="online_completed_rate" jdbcType="REAL" property="onlineCompletedRate" />
+        <result column="online_not_comp_rate_num" jdbcType="INTEGER" property="onlineNotCompRateNum" />
+        <result column="online_completed_num" jdbcType="INTEGER" property="onlineCompletedNum" />
+        <result column="company_user_id" jdbcType="INTEGER" property="companyUserId" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, data_date, period_id, period_num, not_reg_num, registered_num, reg_rate,
+    completed_rate, offline_total, offline_not_reg_num, offline_not_watch_num,
+    online_total, online_rate, online_completed_rate, online_not_comp_rate_num,
+    online_completed_num, company_user_id
+    </sql>
+
+    <!-- 根据主键查询 -->
+    <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
+        select
+        <include refid="Base_Column_List" />
+        from fs_statis_every_day_watch
+        where id = #{id,jdbcType=INTEGER}
+    </select>
+
+    <!-- 根据主键删除 -->
+    <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
+        delete from fs_statis_every_day_watch
+        where id = #{id,jdbcType=INTEGER}
+    </delete>
+
+    <!-- 选择性插入记录 (只插入非空字段) -->
+    <insert id="insertSelective" parameterType="com.fs.statis.domain.FsStatisEveryDayWatch" useGeneratedKeys="true" keyProperty="id">
+        insert into fs_statis_every_day_watch
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="dataDate != null">data_date,</if>
+            <if test="periodId != null">period_id,</if>
+            <if test="periodNum != null">period_num,</if>
+            <if test="notRegNum != null">not_reg_num,</if>
+            <if test="registeredNum != null">registered_num,</if>
+            <if test="regRate != null">reg_rate,</if>
+            <if test="completedRate != null">completed_rate,</if>
+            <if test="offlineTotal != null">offline_total,</if>
+            <if test="offlineNotRegNum != null">offline_not_reg_num,</if>
+            <if test="offlineNotWatchNum != null">offline_not_watch_num,</if>
+            <if test="onlineTotal != null">online_total,</if>
+            <if test="onlineRate != null">online_rate,</if>
+            <if test="onlineCompletedRate != null">online_completed_rate,</if>
+            <if test="onlineNotCompRateNum != null">online_not_comp_rate_num,</if>
+            <if test="onlineCompletedNum != null">online_completed_num,</if>
+            <if test="companyUserId != null">company_user_id,</if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="dataDate != null">#{dataDate,jdbcType=TIMESTAMP},</if>
+            <if test="periodId != null">#{periodId,jdbcType=INTEGER},</if>
+            <if test="periodNum != null">#{periodNum,jdbcType=INTEGER},</if>
+            <if test="notRegNum != null">#{notRegNum,jdbcType=INTEGER},</if>
+            <if test="registeredNum != null">#{registeredNum,jdbcType=INTEGER},</if>
+            <if test="regRate != null">#{regRate,jdbcType=REAL},</if>
+            <if test="completedRate != null">#{completedRate,jdbcType=REAL},</if>
+            <if test="offlineTotal != null">#{offlineTotal,jdbcType=INTEGER},</if>
+            <if test="offlineNotRegNum != null">#{offlineNotRegNum,jdbcType=INTEGER},</if>
+            <if test="offlineNotWatchNum != null">#{offlineNotWatchNum,jdbcType=INTEGER},</if>
+            <if test="onlineTotal != null">#{onlineTotal,jdbcType=INTEGER},</if>
+            <if test="onlineRate != null">#{onlineRate,jdbcType=REAL},</if>
+            <if test="onlineCompletedRate != null">#{onlineCompletedRate,jdbcType=REAL},</if>
+            <if test="onlineNotCompRateNum != null">#{onlineNotCompRateNum,jdbcType=INTEGER},</if>
+            <if test="onlineCompletedNum != null">#{onlineCompletedNum,jdbcType=INTEGER},</if>
+            <if test="companyUserId != null">#{companyUserId,jdbcType=INTEGER},</if>
+        </trim>
+    </insert>
+
+    <!-- 根据主键选择性更新 (只更新非空字段) -->
+    <update id="updateByPrimaryKeySelective" parameterType="com.fs.statis.domain.FsStatisEveryDayWatch">
+        update fs_statis_every_day_watch
+        <set>
+            <if test="dataDate != null">data_date = #{dataDate,jdbcType=TIMESTAMP},</if>
+            <if test="periodId != null">period_id = #{periodId,jdbcType=INTEGER},</if>
+            <if test="periodNum != null">period_num = #{periodNum,jdbcType=INTEGER},</if>
+            <if test="notRegNum != null">not_reg_num = #{notRegNum,jdbcType=INTEGER},</if>
+            <if test="registeredNum != null">registered_num = #{registeredNum,jdbcType=INTEGER},</if>
+            <if test="regRate != null">reg_rate = #{regRate,jdbcType=REAL},</if>
+            <if test="completedRate != null">completed_rate = #{completedRate,jdbcType=REAL},</if>
+            <if test="offlineTotal != null">offline_total = #{offlineTotal,jdbcType=INTEGER},</if>
+            <if test="offlineNotRegNum != null">offline_not_reg_num = #{offlineNotRegNum,jdbcType=INTEGER},</if>
+            <if test="offlineNotWatchNum != null">offline_not_watch_num = #{offlineNotWatchNum,jdbcType=INTEGER},</if>
+            <if test="onlineTotal != null">online_total = #{onlineTotal,jdbcType=INTEGER},</if>
+            <if test="onlineRate != null">online_rate = #{onlineRate,jdbcType=REAL},</if>
+            <if test="onlineCompletedRate != null">online_completed_rate = #{onlineCompletedRate,jdbcType=REAL},</if>
+            <if test="onlineNotCompRateNum != null">online_not_comp_rate_num = #{onlineNotCompRateNum,jdbcType=INTEGER},</if>
+            <if test="onlineCompletedNum != null">online_completed_num = #{onlineCompletedNum,jdbcType=INTEGER},</if>
+            <if test="companyUserId != null">company_user_id = #{companyUserId,jdbcType=INTEGER},</if>
+        </set>
+        where id = #{id,jdbcType=INTEGER}
+    </update>
+
+    <!-- 根据主键更新 (所有字段) -->
+    <update id="updateByPrimaryKey" parameterType="com.fs.statis.domain.FsStatisEveryDayWatch">
+        update fs_statis_every_day_watch
+        set data_date = #{dataDate,jdbcType=TIMESTAMP},
+            period_id = #{periodId,jdbcType=INTEGER},
+            period_num = #{periodNum,jdbcType=INTEGER},
+            not_reg_num = #{notRegNum,jdbcType=INTEGER},
+            registered_num = #{registeredNum,jdbcType=INTEGER},
+            reg_rate = #{regRate,jdbcType=REAL},
+            completed_rate = #{completedRate,jdbcType=REAL},
+            offline_total = #{offlineTotal,jdbcType=INTEGER},
+            offline_not_reg_num = #{offlineNotRegNum,jdbcType=INTEGER},
+            offline_not_watch_num = #{offlineNotWatchNum,jdbcType=INTEGER},
+            online_total = #{onlineTotal,jdbcType=INTEGER},
+            online_rate = #{onlineRate,jdbcType=REAL},
+            online_completed_rate = #{onlineCompletedRate,jdbcType=REAL},
+            online_not_comp_rate_num = #{onlineNotCompRateNum,jdbcType=INTEGER},
+            online_completed_num = #{onlineCompletedNum,jdbcType=INTEGER},
+            company_user_id = #{companyUserId,jdbcType=INTEGER}
+        where id = #{id,jdbcType=INTEGER}
+    </update>
+
+    <!-- 查询所有记录 -->
+    <select id="selectAll" resultMap="BaseResultMap">
+        select
+        <include refid="Base_Column_List" />
+        from fs_statis_every_day_watch
+    </select>
+    <select id="queryList" resultType="com.fs.statis.domain.FsStatisSalerWatch">
+        select * from fs_statis_every_day_watch
+        <where>
+            <if test="userIds != null and userIds.length > 0">
+                AND company_user_id IN
+                <foreach collection="userIds" open="(" close=")" separator="," item="item">
+                    ${item}
+                </foreach>
+            </if>
+            <if test="periodList != null and periodList.length > 0">
+                AND period_id IN
+                <foreach collection="periodList" open="(" close=")" separator="," item="item">
+                    ${item}
+                </foreach>
+            </if>
+            <if test="startDate != null and endDate != null">
+                AND data_date BETWEEN #{startDate} AND #{endDate}
+            </if>
+        </where>
+    </select>
+
+</mapper>

+ 60 - 0
fs-service/src/main/resources/mapper/statis/FsStatisPeriodWatchMapper.xml

@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.statis.mapper.FsStatisPeriodWatchMapper">
+
+    <!-- 通用查询结果映射 -->
+    <resultMap id="BaseResultMap" type="com.fs.statis.domain.FsStatisPeriodWatch">
+        <id column="id" property="id" jdbcType="INTEGER" />
+        <result column="period_id" property="periodId" jdbcType="INTEGER" />
+        <result column="period_num" property="periodNum" jdbcType="INTEGER" />
+        <result column="not_reg_num" property="notRegNum" jdbcType="INTEGER" />
+        <result column="registered_num" property="registeredNum" jdbcType="INTEGER" />
+        <result column="reg_rate" property="regRate" jdbcType="DECIMAL" />
+        <result column="watch_completed_rate" property="watchCompletedRate" jdbcType="DECIMAL" />
+        <result column="offline_total" property="offlineTotal" jdbcType="INTEGER" />
+        <result column="offline_not_reg_num" property="offlineNotRegNum" jdbcType="INTEGER" />
+        <result column="offline_not_watch_num" property="offlineNotWatchNum" jdbcType="INTEGER" />
+        <result column="online_total" property="onlineTotal" jdbcType="INTEGER" />
+        <result column="online_rate" property="onlineRate" jdbcType="DECIMAL" />
+        <result column="online_watch_completed_rate" property="onlineWatchCompletedRate" jdbcType="DECIMAL" />
+        <result column="online_watch_not_completed" property="onlineWatchNotCompleted" jdbcType="INTEGER" />
+        <result column="online_watch_completed" property="onlineWatchCompleted" jdbcType="INTEGER" />
+        <result column="data_date" property="dataDate" jdbcType="DATE" />
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, period_id, period_num, not_reg_num, registered_num, reg_rate,
+        watch_completed_rate, offline_total, offline_not_reg_num, offline_not_watch_num,
+        online_total, online_rate, online_watch_completed_rate, online_watch_not_completed,
+        online_watch_completed, data_date
+    </sql>
+
+    <!-- 查询所有数据 -->
+    <select id="selectAll" resultMap="BaseResultMap">
+        SELECT
+        <include refid="Base_Column_List" />
+        FROM fs_statis_period_watch
+    </select>
+    <select id="queryList" resultType="com.fs.statis.domain.FsStatisPeriodWatch">
+        select * from fs_statis_period_watch
+        <where>
+            <if test="userIds != null and userIds.length > 0">
+                AND company_user_id IN
+                <foreach collection="userIds" open="(" close=")" separator="," item="item">
+                    ${item}
+                </foreach>
+            </if>
+            <if test="periodList != null and periodList.length > 0">
+                AND period_id IN
+                <foreach collection="periodList" open="(" close=")" separator="," item="item">
+                    ${item}
+                </foreach>
+            </if>
+            <if test="startDate != null and endDate != null">
+                AND data_date BETWEEN #{startDate} AND #{endDate}
+            </if>
+        </where>
+    </select>
+
+</mapper>

+ 86 - 0
fs-service/src/main/resources/mapper/statis/FsStatisSalerWatchMapper.xml

@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.statis.mapper.FsStatisSalerWatchMapper"> <!-- 请替换为你的Mapper接口的完整路径 -->
+
+    <!-- 结果映射 -->
+    <resultMap id="BaseResultMap" type="com.fs.statis.domain.FsStatisSalerWatch"> <!-- 请替换为你的Entity的完整路径 -->
+        <id column="id" property="id" jdbcType="INTEGER"/>
+        <result column="dept_id" property="deptId" jdbcType="INTEGER"/>
+        <result column="company_user_id" property="companyUserId" jdbcType="INTEGER"/>
+        <result column="train_camp_num" property="trainCampNum" jdbcType="INTEGER"/>
+        <result column="not_registered_num" property="notRegisteredNum" jdbcType="INTEGER"/>
+        <result column="registered_num" property="registeredNum" jdbcType="INTEGER"/>
+        <result column="reg_rate" property="regRate" jdbcType="FLOAT"/>
+        <result column="finished_rate" property="finishedRate" jdbcType="FLOAT"/>
+        <result column="offline_total" property="offlineTotal" jdbcType="INTEGER"/>
+        <result column="offline_not_part" property="offlineNotPart" jdbcType="INTEGER"/> <!-- 对应修正后的 offlineNotPart -->
+        <result column="offline_not_watched" property="offlineNotWatched" jdbcType="INTEGER"/>
+        <result column="online_total" property="onlineTotal" jdbcType="INTEGER"/>
+        <result column="online_online_rate" property="onlineOnlineRate" jdbcType="FLOAT"/>
+        <result column="online_playback_comple_rate" property="onlinePlaybackCompleRate" jdbcType="FLOAT"/>
+        <result column="online_incomplete_playback" property="onlineIncompletePlayback" jdbcType="INTEGER"/>
+        <result column="online_complete_playback" property="onlineCompletePlayback" jdbcType="INTEGER"/>
+    </resultMap>
+
+    <!-- 通用查询结果列 -->
+    <sql id="Base_Column_List">
+        id, dept_id, company_user_id, train_camp_num, not_registered_num, registered_num,
+        reg_rate, finished_rate, offline_total, offline_not_part, offline_not_watched,
+        online_total, online_online_rate, online_playback_comple_rate,
+        online_incomplete_playback, online_complete_playback
+    </sql>
+
+    <select id="queryList" resultType="com.fs.statis.domain.FsStatisSalerWatch">
+        select * from fs_statis_saler_watch
+        <where>
+            <if test="userIds != null and userIds.length > 0">
+                AND company_user_id IN
+                 <foreach collection="userIds" open="(" close=")" separator="," item="item">
+                     ${item}
+                </foreach>
+            </if>
+            <if test="periodList != null and periodList.length > 0">
+                AND period_id IN
+                <foreach collection="periodList" open="(" close=")" separator="," item="item">
+                    ${item}
+                </foreach>
+            </if>
+            <if test="startDate != null and endDate != null">
+                AND data_date BETWEEN #{startDate} AND #{endDate}
+            </if>
+        </where>
+    </select>
+
+    <insert id="batchSave">
+        INSERT INTO fs_statis_saler_watch (
+            dept_id, company_user_id, train_camp_num,
+            not_registered_num, registered_num, reg_rate,
+            finished_rate, offline_total, offline_not_part,
+            offline_not_watched, online_total, online_online_rate,
+            online_playback_comple_rate, online_incomplete_playback,
+            online_complete_playback,period_id,data_date
+        ) VALUES
+        <foreach collection="list" item="item" separator=",">
+            (
+                #{item.deptId,jdbcType=INTEGER},
+                #{item.companyUserId,jdbcType=INTEGER},
+                #{item.trainCampNum,jdbcType=INTEGER},
+                #{item.notRegisteredNum,jdbcType=INTEGER},
+                #{item.registeredNum,jdbcType=INTEGER},
+                #{item.regRate,jdbcType=FLOAT},
+                #{item.finishedRate,jdbcType=FLOAT},
+                #{item.offlineTotal,jdbcType=INTEGER},
+                #{item.offlineNotPart,jdbcType=INTEGER},
+                #{item.offlineNotWatched,jdbcType=INTEGER},
+                #{item.onlineTotal,jdbcType=INTEGER},
+                #{item.onlineOnlineRate,jdbcType=FLOAT},
+                #{item.onlinePlaybackCompleRate,jdbcType=FLOAT},
+                #{item.onlineIncompletePlayback,jdbcType=INTEGER},
+                #{item.onlineCompletePlayback,jdbcType=INTEGER},
+                #{item.periodId,jdbcType=INTEGER},
+                #{item.dataDate,jdbcType=VARCHAR}
+            )
+        </foreach>
+    </insert>
+
+</mapper>

+ 161 - 0
fs-service/src/main/resources/mapper/statis/FsStatsMemberDailyMapper.xml

@@ -0,0 +1,161 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.statis.mapper.FsStatsMemberDailyMapper">
+
+    <update id="refreshMemberDailyData">
+        insert into fs_stats_member_daily
+            (
+            stat_date,user_id,nick_name,real_name,phone,tag,company_group_id,company_id,company_name,company_user_id,company_user_name,
+            train_camp_id,train_camp_name,period_id,period_name,course_id,course_name,video_id,video_name,is_over,watch_count,watch_duration,
+            answer_count,answer_correct_count,red_packet_count,red_packet_amount
+            )
+        select
+            #{date}					    as stat_date,
+            u.user_id				    as user_id,
+            u.nickname				    as nick_name,
+            u.real_name				    as real_name,
+            u.phone					    as phone,
+            concat_ws(',', t.tag) 	    as tag,
+            null					    as company_group_id,
+            c.company_id			    as company_id,
+            c.company_name			    as company_name,
+            cu.user_id				    as company_user_id,
+            cu.nick_name			    as company_user_name,
+            uctc.training_camp_id 	    as train_camp_id,
+            uctc.training_camp_name	    as train_camp_name,
+            ucp.period_id			    as period_id,
+            ucp.period_name			    as period_name,
+            uc.course_id			    as course_id,
+            uc.course_name			    as course_name,
+            ucv.video_id			    as video_id,
+            ucv.title				    as video_name,
+            max(case when cwl.log_type = 2 then 1 else 0 end)
+                                        as is_over,
+            count(cwl.log_id)		    as watch_count,
+            sum(cwl.duration)		    as watch_duration,
+            count(cal.log_id)		    as answer_count,
+            count(case when cal.is_right = 1 then cal.log_id end)
+                                        as answer_correct_count,
+            count(crpl.log_id)		    as red_packet_count,
+            ifnull(sum(crpl.amount),0)	as red_packet_amount
+        from fs_course_watch_log cwl
+        inner join fs_user u 									on u.user_id = cwl.user_id
+        left join company_tag_user ctu 							on ctu.company_id = cwl.company_id and ctu.company_user_id = cwl.company_user_id and ctu.user_id = cwl.user_id
+        left join company_tag t 								on FIND_IN_SET(t.tag_id, ctu.tag_ids)
+        left join fs_user_course_period ucp 				    on ucp.period_id = cwl.period_id
+        left join fs_user_course_training_camp uctc             on uctc.training_camp_id = ucp.training_camp_id
+        left join company c 									on c.company_id = cwl.company_id
+        left join company_user cu 								on cu.company_id = cwl.company_id and cu.user_id = cwl.company_user_id
+        left join fs_user_course uc 							on uc.course_id = cwl.course_id
+        left join fs_user_course_video ucv 					    on ucv.course_id = cwl.course_id and ucv.video_id = cwl.video_id
+        left join fs_course_answer_logs cal 				    on cal.watch_log_id = cwl.log_id
+        left join fs_course_red_packet_log crpl 		        on crpl.watch_log_id = cwl.log_id
+        <![CDATA[
+        where cwl.create_time >= #{date} and cwl.create_time < date_add(#{date}, interval 1 day)
+        ]]>
+        group by
+            cwl.user_id,
+            cwl.company_id,
+            cwl.company_user_id,
+            ucp.training_camp_id,
+            cwl.period_id,
+            cwl.course_id,
+            cwl.video_id
+        on duplicate key update
+            nick_name = values(nick_name),
+            real_name = values(real_name),
+            phone = values(phone),
+            tag = values(tag),
+            company_name = values(company_name),
+            company_user_name = values(company_user_name),
+            train_camp_name = values(train_camp_name),
+            period_name = values(period_name),
+            course_name = values(course_name),
+            video_name = values(video_name),
+            is_over = values(is_over),
+            watch_count = values(watch_count),
+            watch_duration = values(watch_duration),
+            answer_count = values(answer_count),
+            answer_correct_count = values(answer_correct_count),
+            red_packet_count = values(red_packet_count),
+            red_packet_amount = values(red_packet_amount)
+    </update>
+
+    <select id="selectDailyData" resultType="com.fs.statis.vo.FsStatsMemberDailyVO">
+        select
+            <choose>
+                <when test="params.type == 1">
+                    fsmd.stat_date,
+                </when>
+                <otherwise>
+                    date_format(fsmd.stat_date, '%Y-%m') as stat_date,
+                </otherwise>
+            </choose>
+            fsmd.user_id,
+            fsmd.nick_name,
+            fsmd.real_name,
+            fsmd.phone,
+            fsmd.tag,
+            fsmd.company_id,
+            fsmd.company_name,
+            fsmd.company_user_id,
+            fsmd.company_user_name,
+            count(fsmd.id) as count,
+            sum(fsmd.is_over) as overCount,
+            sum(fsmd.watch_count) as watchCount,
+            sum(fsmd.watch_duration) as watchDuration,
+            sum(fsmd.answer_count) as answerCount,
+            sum(fsmd.answer_correct_count) as anserCorrectCount,
+            sum(fsmd.red_packet_count) as redPacketCount,
+            sum(fsmd.red_packet_amount) redPacketAmount
+        from fs_stats_member_daily fsmd
+        <where>
+            <if test="params.startDate != null">
+                and fsmd.stat_date >= #{params.startDate}
+            </if>
+            <if test="params.endDate != null">
+            <![CDATA[
+                and fsmd.stat_date < #{params.endDate}
+            ]]>
+            </if>
+            <if test="params.companyId != null">
+                and fsmd.company_id = #{params.companyId}
+            </if>
+            <if test="params.companyUserId != null">
+                and fsmd.company_user_id = #{params.companyUserId}
+            </if>
+            <if test="params.userId != null">
+                and fsmd.user_id = #{params.userId}
+            </if>
+            <if test="params.phone != null and params.phone != ''">
+                and fsmd.phone like concat('%', #{params.phone}, '%')
+            </if>
+            <if test="params.trainCampId != null">
+                and fsmd.train_camp_id = #{params.trainCampId}
+            </if>
+            <if test="params.periodId != null">
+                and fsmd.period_id = #{params.periodId}
+            </if>
+            <if test="params.courseId != null">
+                and fsmd.course_id = #{params.courseId}
+            </if>
+            <if test="params.videoId != null">
+                and fsmd.video_id = #{params.videoId}
+            </if>
+        </where>
+        group by
+            <choose>
+                <when test="params.type == 1">
+                    stat_date,
+                </when>
+                <otherwise>
+                    date_format(stat_date, '%Y-%m'),
+                </otherwise>
+            </choose>
+            user_id,
+            company_id,
+            company_user_id
+    </select>
+</mapper>