Jelajahi Sumber

feat:迁移

xdd 1 Minggu lalu
induk
melakukan
ae2e1bb819

+ 47 - 0
fs-admin/src/main/java/com/fs/his/task/FsCourseTask.java

@@ -0,0 +1,47 @@
+package com.fs.his.task;
+
+import com.fs.course.service.IFsCourseWatchLogService;
+import com.fs.qw.service.IHyWorkTaskService;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * 后台统计相关 定时任务
+ */
+@Slf4j
+@Service("fsCourseTask")
+public class FsCourseTask {
+    @Autowired
+    private IFsCourseWatchLogService fsCourseWatchLogService;
+    @Autowired
+    private IHyWorkTaskService hyWorkTaskService;
+    /**
+     * 添加会员观看日志
+     * @throws Exception
+     */
+    public void addHyWatchLog() throws Exception
+    {
+        fsCourseWatchLogService.addCourseWatchLogDayNew();
+    }
+
+    /**
+     * 删除过期数据
+     * @throws Exception
+     */
+    public void hyWorkTask4() throws Exception
+    {
+        // 更新已完课的催课看板
+        hyWorkTaskService.delHyWorkTaskByOver();
+    }
+
+    /**
+     * 会员查看催课面板 获取看课中断和待看的先导课
+     * @throws Exception
+     */
+    public void hyWorkTask(){
+
+        hyWorkTaskService.hyWorkTask();
+    }
+
+}

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

@@ -12,6 +12,7 @@ import org.apache.ibatis.annotations.Update;
 
 import javax.validation.constraints.NotNull;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.List;
 
 /**
@@ -299,4 +300,17 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
             ") dd ON ds.report_date = dd.log_date\n" +
             "ORDER BY ds.report_date ASC  ")
     List<Integer> selectFsCourseWatchLog7DayByExtId(Long extId);
+
+    @Select("select l.user_id,l.company_id,l.company_user_id,l.log_type,ext.`level`,l.qw_user_id FROM fs_course_watch_log l LEFT JOIN fs_user ext ON ext.user_id =l.user_id  where l.sop_id=#{SopId} and  date(l.create_time)= CURDATE() and l.log_type =4 and l.video_id not in (select video_id from fs_user_course_video WHERE is_first=1 )")
+    List<FsCourseWatchLogTaskVO> selectFsCourseWatchLogByDaySopIdFsUser4(@Param("SopId")String SopId);
+
+    @Select("select l.qw_external_contact_id,l.company_id,l.company_user_id,l.log_type,ext.`level`,l.qw_user_id,ext.last_watch_time FROM fs_course_watch_log l LEFT JOIN qw_external_contact ext ON ext.id =l.qw_external_contact_id  where l.sop_id=#{SopId} and  date(l.create_time)= CURDATE() and l.log_type =3 and l.video_id not in (select video_id from fs_user_course_video WHERE is_first=1 ) and ext.last_watch_time < #{lastTime} and ext.last_watch_time !=0  ")
+    List<FsCourseWatchLogTaskVO> selectFsCourseWatchLogByDaySopId3LastTime(@Param("SopId")String SopId,@Param("lastTime")Integer lastTime);
+
+    @Select("SELECT l.qw_external_contact_id,l.project,l.video_id,l.course_id,l.log_type,l.qw_user_id,l.create_time lineTime,l.company_user_id as company_user_id,l.user_id as user_id,l.company_id as company_id FROM fs_course_watch_log l" +
+            " WHERE l.send_type=1 AND DATE(l.create_time) = DATE_SUB(CURDATE(), INTERVAL 1 DAY) and l.video_id =#{videoId}")
+    List<FsQwCourseWatchLogVO> selectFsCourseBeforeMonthWatchLogByVideoId(Long videoId);
+
+    @Select("SELECT min(create_time) FROM fs_course_watch_log WHERE user_id=#{userId} limit 1")
+    Date queryFirstWatchDateLogByVideoId(Long userId);
 }

+ 79 - 0
fs-service/src/main/java/com/fs/course/mapper/HyWatchLogMapper.java

@@ -0,0 +1,79 @@
+package com.fs.course.mapper;
+
+import com.fs.course.vo.HyWatchLog;
+import org.apache.ibatis.annotations.*;
+
+import java.util.List;
+
+/**
+ * 企微看课表数据库访问接口
+ */
+@Mapper
+public interface HyWatchLogMapper {
+
+    /**
+     * 新增记录
+     *
+     * @param hyWatchLog 记录对象
+     * @return 影响行数
+     */
+    @Insert("INSERT INTO hy_watch_log (ext_id, qw_user_id, status, day, project, create_time, line_time, fs_user_id, company_id, company_user_id, course_id, video_id) " +
+            "VALUES (#{extId}, #{qwUserId}, #{status}, #{day}, #{project}, #{createTime}, #{lineTime}, #{fsUserId}, #{companyId}, #{companyUserId}, #{courseId}, #{videoId})")
+    @Options(useGeneratedKeys = true, keyProperty = "id")
+    int insert(HyWatchLog hyWatchLog);
+
+    /**
+     * 根据ID查询记录
+     *
+     * @param id 主键ID
+     * @return 记录对象,如果不存在则返回 null
+     */
+    @Select("SELECT id, ext_id, qw_user_id, status, day, project, create_time, line_time, fs_user_id, company_id, company_user_id, course_id, video_id " +
+            "FROM hy_watch_log WHERE id = #{id}")
+    HyWatchLog selectById(@Param("id") Long id);
+
+    /**
+     * 根据ID更新记录
+     * 注意:这里是全量更新,会更新所有字段。如果需要部分更新,请自行调整 SQL。
+     *
+     * @param hyWatchLog 包含更新信息的记录对象 (ID 必须存在)
+     * @return 影响行数
+     */
+    @Update("UPDATE hy_watch_log SET " +
+            "ext_id = #{extId}, " +
+            "qw_user_id = #{qwUserId}, " +
+            "status = #{status}, " +
+            "day = #{day}, " +
+            "project = #{project}, " +
+            "create_time = #{createTime}, " +
+            "line_time = #{lineTime}, " +
+            "fs_user_id = #{fsUserId}, " +
+            "company_id = #{companyId}, " +
+            "company_user_id = #{companyUserId}, " +
+            "course_id = #{courseId}, " +
+            "video_id = #{videoId} " +
+            "WHERE id = #{id}")
+    int updateById(HyWatchLog hyWatchLog);
+
+    /**
+     * 根据外部联系人ID查询记录列表
+     *
+     * @param extId 外部联系人ID
+     * @return 记录列表
+     */
+    @Select("SELECT id, ext_id, qw_user_id, status, day, project, create_time, line_time, fs_user_id, company_id, company_user_id, course_id, video_id " +
+            "FROM hy_watch_log WHERE ext_id = #{extId}")
+    List<HyWatchLog> selectByExtId(@Param("extId") Long extId);
+
+    /**
+     * 根据状态查询记录列表
+     *
+     * @param status 状态码
+     * @return 记录列表
+     */
+    @Select("SELECT id, ext_id, qw_user_id, status, day, project, create_time, line_time, fs_user_id, company_id, company_user_id, course_id, video_id " +
+            "FROM hy_watch_log WHERE status = #{status}")
+    List<HyWatchLog> selectByStatus(@Param("status") Integer status);
+
+    void insertHyWatchLogBatch(@Param("hyWatchLogs") List<HyWatchLog> hyWatchLogs);
+}

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

@@ -108,4 +108,7 @@ public interface IFsCourseWatchLogService extends IService<FsCourseWatchLog> {
 
     long selectFsCourseWatchLogStatisticsListVONewCount(FsCourseWatchLogStatisticsListParam param);
 
+    void addCourseWatchLogDayNew();
+
+
 }

+ 88 - 4
fs-service/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java

@@ -18,16 +18,14 @@ import com.fs.course.domain.FsCourseFinishTemp;
 import com.fs.course.domain.FsCourseWatchLog;
 import com.fs.course.domain.FsUserCourse;
 import com.fs.course.domain.FsUserCourseVideo;
-import com.fs.course.mapper.FsCourseFinishTempMapper;
-import com.fs.course.mapper.FsCourseWatchLogMapper;
-import com.fs.course.mapper.FsUserCourseMapper;
-import com.fs.course.mapper.FsUserCourseVideoMapper;
+import com.fs.course.mapper.*;
 import com.fs.course.param.*;
 import com.fs.course.service.IFsCourseWatchLogService;
 import com.fs.course.service.cache.IFsUserCourseVideoCacheService;
 import com.fs.course.vo.*;
 import com.fs.his.config.FsSysConfig;
 import com.fs.his.domain.FsUser;
+import com.fs.his.service.IFsUserService;
 import com.fs.his.utils.ConfigUtil;
 import com.fs.his.vo.OptionsVO;
 import com.fs.qw.Bean.MsgBean;
@@ -55,6 +53,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.time.Duration;
@@ -116,6 +115,13 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
 
     @Autowired
     private IQwUserCacheService qwUserCacheService;
+
+    @Autowired
+    private IFsUserService fsUserService;
+
+    @Autowired
+    private HyWatchLogMapper hyWatchLogMapper;
+
     /**
      * 查询短链课程看课记录
      *
@@ -259,6 +265,84 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
         return fsCourseWatchLogMapper.selectFsCourseWatchLogStatisticsListVONewCount(param);
     }
 
+    @Override
+    @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)
+    public void addCourseWatchLogDayNew() {
+
+        List<FsUserCourse> courses = fsUserCourseMapper.selectFsUserCourseAllCourse();
+        for (FsUserCourse course : courses) {
+            Long project = course.getProject();
+            List<FsUserCourseVideo> fsUserCourseVideos = fsUserCourseVideoMapper.selectVideoByCourseId(course.getCourseId());
+            for (FsUserCourseVideo fsUserCourseVideo : fsUserCourseVideos) {
+                try {
+                    ArrayList<HyWatchLog> QwWatchLogs = new ArrayList<>();
+                    List<FsQwCourseWatchLogVO> watchLogs = fsCourseWatchLogMapper.selectFsCourseBeforeMonthWatchLogByVideoId(fsUserCourseVideo.getVideoId());
+
+                    for (FsQwCourseWatchLogVO fsCourseWatchLog : watchLogs) {
+
+                        // 获取进线时间
+                        FsUser fsUser = fsUserService.selectFsUserById(fsCourseWatchLog.getUserId());
+                        if (fsUser!=null){
+                            fsCourseWatchLog.setLineTime(fsUser.getCreateTime());
+                        }
+                        // 查询首次观看时间
+                        Date date = fsCourseWatchLogMapper.queryFirstWatchDateLogByVideoId(fsCourseWatchLog.getUserId());
+                        if (date!=null){
+                            fsCourseWatchLog.setFirstTime(date);
+                        }
+
+                        Date firstTime = fsCourseWatchLog.getFirstTime();
+                        Long day=1L;
+                        if (fsCourseWatchLog.getLineTime()==null){
+                            continue;
+                        }
+                        //不是第一次 看课程
+                        if (firstTime!=null){
+                            LocalDate firstLocalDate = firstTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
+                            LocalDate currentDate = LocalDate.now();
+                            day = ChronoUnit.DAYS.between(firstLocalDate, currentDate);
+                        }else {
+                            //是先导课
+                            if (fsUserCourseVideo.getIsFirst()!=null&&fsUserCourseVideo.getIsFirst()==1){
+                                // 如果存在第一次记录就跳过
+                                int count = qwWatchLogMapper.selectQwWatchLogIsFirstByUserId(fsCourseWatchLog.getUserId());
+                                if (count>0){
+                                    continue;
+                                }
+                                day=0L;
+                                //不是先导课
+                            }
+                        }
+                        HyWatchLog qwWatchLog = new HyWatchLog();
+                        qwWatchLog.setExtId(fsCourseWatchLog.getQwExternalContactId());
+                        qwWatchLog.setLineTime(fsCourseWatchLog.getLineTime());
+                        if(fsCourseWatchLog.getQwUserId() != null) {
+                            qwWatchLog.setQwUserId(Long.parseLong(fsCourseWatchLog.getQwUserId()));
+                        }
+                        qwWatchLog.setDay(day);
+                        qwWatchLog.setStatus(fsCourseWatchLog.getLogType()==3?0:fsCourseWatchLog.getLogType()==2?2:1);
+                        qwWatchLog.setProject(project);
+                        qwWatchLog.setCreateTime(fsCourseWatchLog.getCreateTime());
+                        qwWatchLog.setCompanyId(fsCourseWatchLog.getCompanyId());
+                        qwWatchLog.setCompanyUserId(fsCourseWatchLog.getCompanyUserId());
+                        qwWatchLog.setFsUserId(fsCourseWatchLog.getUserId());
+                        qwWatchLog.setCourseId(fsCourseWatchLog.getCourseId());
+                        qwWatchLog.setVideoId(fsCourseWatchLog.getVideoId());
+                        QwWatchLogs.add(qwWatchLog);
+                    }
+                    if (!QwWatchLogs.isEmpty()){
+                        hyWatchLogMapper.insertHyWatchLogBatch(QwWatchLogs);
+                    }
+                }catch (Exception e){
+                    log.error("看课记录add异常:{}",course.getCourseId(),e);
+                    throw new RuntimeException(e);
+                }
+
+
+            }
+        }
+    }
+
     @Override
     public FsCourseWatchLog getWatchCourseLogVideoBySop(Long videoId,String qwUserId,Long externalId )
     {

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

@@ -17,4 +17,6 @@ public class FsCourseWatchLogTaskVO {
     private Integer level;
 
     private Long qwUserId;
+
+    private Long userId;
 }

+ 61 - 0
fs-service/src/main/java/com/fs/qw/domain/HyWorkTask.java

@@ -0,0 +1,61 @@
+package com.fs.qw.domain;
+
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 个微任务看板对象 hy_work_task
+ *
+ * @author fs
+ * @date 2025-03-18
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class HyWorkTask extends BaseEntity{
+
+    /** id */
+    private Long id;
+
+    /** 外部联系人id */
+    @Excel(name = "外部联系人id")
+    private Long extId;
+
+    /** 企微用户id */
+    @Excel(name = "企微用户id")
+    private Long qwUserId;
+
+    private Long userId;
+
+    /** 类别 1先导 2 课程 3 大小转 4 转人工 */
+    @Excel(name = "类别 1先导 2 课程 3 大小转 4 转人工")
+    private Integer type;
+
+    /** 状态 0 待处理 1 已处理 3 过期 */
+    @Excel(name = "状态 0 待处理 1 已处理 3 过期")
+    private Integer status;
+
+    /** 分值 */
+    @Excel(name = "分值")
+    private Integer score;
+
+    /** sopid */
+    @Excel(name = "sopid")
+    private String sopId;
+
+    /** 公司id */
+    @Excel(name = "公司id")
+    private Long companyId;
+
+    /** 用户id */
+    @Excel(name = "用户id")
+    private Long companyUserId;
+
+    private String title;
+
+    /**
+     * 营期id
+     */
+    private String userLogsId;
+}

+ 119 - 0
fs-service/src/main/java/com/fs/qw/mapper/HyWorkTaskMapper.java

@@ -0,0 +1,119 @@
+package com.fs.qw.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.course.domain.FsCourseWatchLog;
+import com.fs.qw.domain.HyWorkTask;
+import com.fs.qw.domain.QwWorkTask;
+import com.fs.qw.param.QwWorkTaskListParam;
+import com.fs.qw.vo.QwWorkTaskListVO;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Select;
+import org.apache.ibatis.annotations.Update;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 企微任务看板Mapper接口
+ *
+ * @author fs
+ * @date 2025-03-18
+ */
+public interface HyWorkTaskMapper extends BaseMapper<HyWorkTask>{
+    /**
+     * 查询企微任务看板
+     *
+     * @param id 企微任务看板主键
+     * @return 企微任务看板
+     */
+    HyWorkTask selectHyWorkTaskById(Long id);
+
+    /**
+     * 查询企微任务看板列表
+     *
+     * @param qwWorkTask 企微任务看板
+     * @return 企微任务看板集合
+     */
+    List<HyWorkTask> selectHyWorkTaskList(HyWorkTask qwWorkTask);
+
+    /**
+     * 新增企微任务看板
+     *
+     * @param qwWorkTask 企微任务看板
+     * @return 结果
+     */
+    int insertHyWorkTask(HyWorkTask qwWorkTask);
+
+    /**
+     * 修改企微任务看板
+     *
+     * @param qwWorkTask 企微任务看板
+     * @return 结果
+     */
+    int updateHyWorkTask(HyWorkTask qwWorkTask);
+
+    /**
+     * 删除企微任务看板
+     *
+     * @param id 企微任务看板主键
+     * @return 结果
+     */
+    int deleteHyWorkTaskById(Long id);
+
+    /**
+     * 批量删除企微任务看板
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteHyWorkTaskByIds(Long[] ids);
+
+    void insertQwWorkTaskBatch(@Param("qwWorkTasks")List<HyWorkTask> qwWorkTasks);
+
+    List<QwWorkTaskListVO> selectHyWorkTaskListVONew(QwWorkTaskListParam qwWorkTask);
+
+    @Select("select ext_id from hy_work_task where type=2 and DATE(create_time) = CURDATE() ")
+    List<Long> selectHyWorkTaskByType();
+
+    @Select("select id,ext_id from hy_work_task where type=2 and status=0 and DATE(create_time) = CURDATE() ")
+    List<QwWorkTask> selectHyWorkTaskByTypeStatus();
+    @Update({
+            "<script>",
+            "UPDATE hy_work_task",
+            "SET status = 2",
+            "WHERE id IN",
+            "<foreach item='id' collection='overIds' open='(' separator=',' close=')'>",
+            "   #{id}",
+            "</foreach>",
+            "</script>"
+    })
+    void updateHyWorkTaskStatus(@Param("overIds")List<Long> overIds);
+
+    @Select("select id,ext_id from hy_work_task where type=2 and status=1 and DATE(create_time) = CURDATE() ")
+    List<QwWorkTask> selectHyWorkTaskByTypeStatus1();
+    @Update({
+            "<script>",
+            "UPDATE hy_work_task",
+            "SET status = 3",
+            "WHERE id IN",
+            "<foreach item='id' collection='overIds' open='(' separator=',' close=')'>",
+            "   #{id}",
+            "</foreach>",
+            "</script>"
+    })
+    void updateHyWorkTaskStatus1(List<Long> overIds1);
+
+    /**
+     * 查询会员任务看板
+     * @param params 参数
+     * @return list
+     */
+    List<HyWorkTask> selectHyWorkTaskListByMap(@Param("params") Map<String, Object> params);
+
+    Long selectHyWorkTaskListVONewCount(QwWorkTaskListParam qwWorkTask);
+
+    void hyWorkTask();
+
+    List<FsCourseWatchLog> hyWorkTaskGetInterruptedAndFirst();
+
+}

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

@@ -231,4 +231,8 @@ public interface QwWatchLogMapper extends BaseMapper<QwWatchLog>{
 
     Long selectQwExtCountByDayAndCount(QwWatchLogStatisticsListParam param);
 
+
+    @Select("SELECT count(1) from qw_watch_log where fs_user_id=#{userId} and `day`=0")
+    int selectQwWatchLogIsFirstByUserId(Long userId);
+
 }

+ 112 - 0
fs-service/src/main/java/com/fs/qw/service/IHyWorkTaskService.java

@@ -0,0 +1,112 @@
+package com.fs.qw.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.qw.domain.HyWorkTask;
+import com.fs.qw.param.QwWorkTaskListParam;
+import com.fs.qw.vo.QwWorkTaskListVO;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 企微任务看板Service接口
+ *
+ * @author fs
+ * @date 2025-03-18
+ */
+public interface IHyWorkTaskService extends IService<HyWorkTask>{
+    /**
+     * 查询企微任务看板
+     *
+     * @param id 企微任务看板主键
+     * @return 企微任务看板
+     */
+    HyWorkTask selectHyWorkTaskById(Long id);
+
+    /**
+     * 查询企微任务看板列表
+     *
+     * @param qwWorkTask 企微任务看板
+     * @return 企微任务看板集合
+     */
+    List<HyWorkTask> selectHyWorkTaskList(HyWorkTask qwWorkTask);
+
+    /**
+     * 新增企微任务看板
+     *
+     * @param qwWorkTask 企微任务看板
+     * @return 结果
+     */
+    int insertHyWorkTask(HyWorkTask qwWorkTask);
+
+    /**
+     * 修改企微任务看板
+     *
+     * @param qwWorkTask 企微任务看板
+     * @return 结果
+     */
+    int updateHyWorkTask(HyWorkTask qwWorkTask);
+
+    /**
+     * 批量删除企微任务看板
+     *
+     * @param ids 需要删除的企微任务看板主键集合
+     * @return 结果
+     */
+    int deleteHyWorkTaskByIds(Long[] ids);
+
+    /**
+     * 删除企微任务看板信息
+     *
+     * @param id 企微任务看板主键
+     * @return 结果
+     */
+    int deleteHyWorkTaskById(Long id);
+    /**
+     * 根据用户首次上课情况,添加相应的企微任务。
+     * 用于处理首次课程触发的任务创建逻辑。
+     */
+    void addHyWorkByFirstCourse();
+
+    /**
+     * 根据用户上课情况(通用逻辑),添加相应的企微任务。
+     * 用于处理一般课程事件触发的任务创建逻辑。
+     */
+    void addHyWorkByCourse();
+
+    /**
+     * 根据转化日(或特定业务日期节点)情况,添加相应的企微任务。
+     * 用于处理基于特定日期或转化阶段的任务创建逻辑。
+     */
+    void  addHyWorkByConversionDay();
+
+    List<QwWorkTaskListVO> selectHyWorkTaskListVONew(QwWorkTaskListParam qwWorkTask);
+    Long selectHyWorkTaskListVONewCount(QwWorkTaskListParam qwWorkTask);
+
+    /**
+     * 根据特定课程逻辑(可能与课程编号4或第四阶段相关)添加企微任务。
+     * 用于处理与特定课程标识(如'4')相关的任务创建逻辑。
+     */
+    void addHyWorkByCourse4();
+
+    /**
+     * 删除已完成、过期或不再需要的企微任务。
+     * 用于定期清理或处理生命周期结束的任务。
+     */
+    void delHyWorkTaskByOver();
+    /**
+     * 根据用户最后一次上课时间或课程结束时间情况,添加相应的企微任务。
+     * 用于处理基于课程结束或最后活动时间的任务创建逻辑。
+     */
+    void addHyWorkByCourseLastTime();
+
+    /**
+     * 查询企微任务看板
+     * @param params 参数
+     * @return list
+     */
+    List<HyWorkTask> selectHyWorkTaskListByMap(Map<String, Object> params);
+
+    void hyWorkTask();
+
+}

+ 589 - 0
fs-service/src/main/java/com/fs/qw/service/impl/HyWorkTaskServiceImpl.java

@@ -0,0 +1,589 @@
+package com.fs.qw.service.impl;
+
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.utils.DateUtils;
+import com.fs.common.utils.DictUtils;
+import com.fs.company.cache.ICompanyCacheService;
+import com.fs.company.cache.ICompanyUserCacheService;
+import com.fs.company.domain.Company;
+import com.fs.company.domain.CompanyUser;
+import com.fs.course.domain.FsCourseWatchLog;
+import com.fs.course.mapper.FsCourseWatchLogMapper;
+import com.fs.course.vo.FsCourseWatchLogTaskVO;
+import com.fs.course.vo.FsQwCourseWatchLogVO;
+import com.fs.qw.domain.HyWorkTask;
+import com.fs.qw.domain.QwExternalContact;
+import com.fs.qw.domain.QwWorkTask;
+import com.fs.qw.mapper.HyWorkTaskMapper;
+import com.fs.qw.mapper.QwExternalContactMapper;
+import com.fs.qw.param.QwWorkTaskListParam;
+import com.fs.qw.service.IHyWorkTaskService;
+import com.fs.qw.vo.QwWorkTaskListVO;
+import com.fs.sop.domain.QwSop;
+import com.fs.sop.domain.SopUserLogsInfo;
+import com.fs.sop.mapper.QwSopMapper;
+import com.fs.sop.mapper.SopUserLogsInfoMapper;
+import com.hc.openapi.tool.util.StringUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.time.temporal.ChronoUnit;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 企微任务看板Service业务层处理
+ *
+ * @author fs
+ * @date 2025-03-18
+ */
+@Service
+@Slf4j
+public class HyWorkTaskServiceImpl extends ServiceImpl<HyWorkTaskMapper, HyWorkTask> implements IHyWorkTaskService {
+    @Autowired
+    private FsCourseWatchLogMapper fsCourseWatchLogMapper;
+    @Autowired
+    private QwSopMapper qwSopMapper;
+    @Autowired
+    private SopUserLogsInfoMapper sopUserLogsInfoMapper;
+    @Autowired
+    private QwExternalContactMapper qwExternalContactMapper;
+    @Autowired
+    private ICompanyCacheService companyCacheService;
+    @Autowired
+    private ICompanyUserCacheService companyUserCacheService;
+    @Autowired
+    private HyWorkTaskMapper hyWorkTaskMapper;
+
+    /**
+     * 查询企微任务看板
+     *
+     * @param id 企微任务看板主键
+     * @return 企微任务看板
+     */
+    @Override
+    public HyWorkTask selectHyWorkTaskById(Long id)
+    {
+        return baseMapper.selectHyWorkTaskById(id);
+    }
+
+    /**
+     * 查询企微任务看板列表
+     *
+     * @param qwWorkTask 企微任务看板
+     * @return 企微任务看板
+     */
+    @Override
+    public List<HyWorkTask> selectHyWorkTaskList(HyWorkTask qwWorkTask)
+    {
+        return baseMapper.selectHyWorkTaskList(qwWorkTask);
+    }
+
+    /**
+     * 新增企微任务看板
+     *
+     * @param qwWorkTask 企微任务看板
+     * @return 结果
+     */
+    @Override
+    public int insertHyWorkTask(HyWorkTask qwWorkTask)
+    {
+        qwWorkTask.setCreateTime(DateUtils.getNowDate());
+        return baseMapper.insertHyWorkTask(qwWorkTask);
+    }
+
+    /**
+     * 修改企微任务看板
+     *
+     * @param qwWorkTask 企微任务看板
+     * @return 结果
+     */
+    @Override
+    public int updateHyWorkTask(HyWorkTask qwWorkTask)
+    {
+        qwWorkTask.setUpdateTime(DateUtils.getNowDate());
+        return baseMapper.updateHyWorkTask(qwWorkTask);
+    }
+
+    /**
+     * 批量删除企微任务看板
+     *
+     * @param ids 需要删除的企微任务看板主键
+     * @return 结果
+     */
+    @Override
+    public int deleteHyWorkTaskByIds(Long[] ids)
+    {
+        return baseMapper.deleteHyWorkTaskByIds(ids);
+    }
+
+    /**
+     * 删除企微任务看板信息
+     *
+     * @param id 企微任务看板主键
+     * @return 结果
+     */
+    @Override
+    public int deleteHyWorkTaskById(Long id)
+    {
+        return baseMapper.deleteHyWorkTaskById(id);
+    }
+
+    @Override
+    public void addHyWorkByFirstCourse() {
+            List<FsQwCourseWatchLogVO> fsQwCourseWatchLogVOS = fsCourseWatchLogMapper.selectFsCourseWatchLogByVideoId();
+        ArrayList<HyWorkTask> qwWorkTasks = new ArrayList<>();
+        for (FsQwCourseWatchLogVO vo : fsQwCourseWatchLogVOS) {
+                HyWorkTask qwWorkTask = new HyWorkTask();
+                qwWorkTask.setCreateTime(DateUtils.getNowDate());
+                qwWorkTask.setExtId(vo.getQwExternalContactId());
+                qwWorkTask.setCompanyId(vo.getCompanyId());
+                qwWorkTask.setCompanyUserId(vo.getCompanyUserId());
+                qwWorkTask.setQwUserId(Long.parseLong(vo.getQwUserId()));
+                qwWorkTask.setType(1);
+                qwWorkTask.setStatus(0);
+                qwWorkTask.setTitle(vo.getLogType()==3?"先导课待看课":"先导课完课");
+                qwWorkTask.setScore(vo.getLogType()==3?4:3);
+                qwWorkTasks.add(qwWorkTask);
+            }
+        if (!qwWorkTasks.isEmpty()) {
+            hyWorkTaskMapper.insertQwWorkTaskBatch(qwWorkTasks);
+        }
+
+    }
+    Integer getHyWorkCourseScore(Integer type, Integer day,Integer leve){
+
+            switch (day){
+                case 1:
+                   return type == 4 ? 12 : 11;
+                case 2:
+                case 3:
+                    return type == 4 ? 9 : 8;
+                case 4:
+                case 5:
+                case 6:
+                case 7:
+                    return type == 4 ? 3 : 2;
+                default:
+
+                    return leve==null?0: leve==1?5:leve==2?3:leve==3?1:0;
+            }
+
+
+    }
+
+    @Override
+    public void addHyWorkByCourse() {
+        List<QwSop> qwSops = qwSopMapper.selectQwSopByIsRating();
+        LocalDate today = LocalDate.now();
+        List<Long> extIds = hyWorkTaskMapper.selectHyWorkTaskByType();
+        Set<Long> extIdSet = new HashSet<>(extIds);
+        // 获取出执行sop的记录数
+        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;
+            }
+            // 每次sop的观看记录
+            List<FsCourseWatchLogTaskVO> fsCourseWatchLogs = fsCourseWatchLogMapper.selectFsCourseWatchLogByDaySopId3(qwSop.getId());
+            if (fsCourseWatchLogs==null || fsCourseWatchLogs.isEmpty()) {
+                continue;
+            }
+            List<HyWorkTask> 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;
+                Integer score = getHyWorkCourseScore(fsCourseWatchLog.getLogType(), day,fsCourseWatchLog.getLevel());
+                if (score==0){
+                    continue;
+                }
+                HyWorkTask qwWorkTask = new HyWorkTask();
+                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()){
+                hyWorkTaskMapper.insertQwWorkTaskBatch(qwWorkTasks);
+            }
+
+        }
+    }
+
+
+
+    @Override
+    public void addHyWorkByConversionDay() {
+        List<QwSop> qwSops = qwSopMapper.selectQwSopByIsRatingNotNull();
+        Map<Integer, Integer> minMap = new HashMap<>();
+        // level->分值映射
+        minMap.put(1, 10);
+        minMap.put(2, 6);
+        minMap.put(3, 2);
+        Map<Integer, Integer> MaxMap = new HashMap<>();
+        // level->分值映射
+        MaxMap.put(1, 25);
+        MaxMap.put(2, 15);
+        MaxMap.put(3, 5);
+        for (QwSop qwSop : qwSops) {
+            Integer min= qwSop.getMinConversionDay();
+            Integer max = qwSop.getMaxConversionDay();
+            LocalDate today = LocalDate.now();
+            addHyWorkTask(today, min, qwSop, "用户小转",minMap);
+            addHyWorkTask(today, max, qwSop, "用户大转",MaxMap);
+        }
+    }
+
+    @Override
+    public List<QwWorkTaskListVO> selectHyWorkTaskListVONew(QwWorkTaskListParam qwWorkTask) {
+        List<QwWorkTaskListVO> list = hyWorkTaskMapper.selectHyWorkTaskListVONew(qwWorkTask);
+        for (QwWorkTaskListVO item : list) {
+            if(ObjectUtils.isNotNull(item.getCompanyId())){
+                Company company = companyCacheService.selectCompanyById(item.getCompanyId());
+                if(ObjectUtils.isNotNull(company)){
+                    item.setCompanyName(String.format("%s_%d",company.getCompanyName(),company.getCompanyId()));
+                }
+            }
+            if(ObjectUtils.isNotNull(item.getCompanyUserId())){
+                CompanyUser companyUser = companyUserCacheService.selectCompanyUserById(item.getCompanyUserId());
+                if(ObjectUtils.isNotNull(companyUser)){
+                    item.setCompanyUserName(String.format("%s_%d", companyUser.getUserName(), companyUser.getUserId()));
+                }
+            }
+            if(ObjectUtils.isNotNull(item.getType())){
+                String kbBusinessType = DictUtils.getDictLabel("sys_qw_work_task_type", String.valueOf(item.getType()));
+                if(StringUtils.isNotBlank(kbBusinessType)){
+                    item.setTypeText(kbBusinessType);
+                }
+            }
+            if(ObjectUtils.isNotNull(item.getStatus())){
+                String kbProcessingStatus = DictUtils.getDictLabel("sys_qw_work_task_status", String.valueOf(item.getStatus()));
+                if(StringUtils.isNotBlank(kbProcessingStatus)){
+                    item.setStatusText(kbProcessingStatus);
+                }
+            }
+        }
+        return list;
+    }
+
+    @Override
+    public Long selectHyWorkTaskListVONewCount(QwWorkTaskListParam qwWorkTask) {
+        return hyWorkTaskMapper.selectHyWorkTaskListVONewCount(qwWorkTask);
+    }
+
+    @Override
+    public void addHyWorkByCourse4() {
+        // 获取sop模板
+        List<QwSop> qwSops = qwSopMapper.selectQwSopByIsRating();
+        // 课程
+        List<Long> extIds = hyWorkTaskMapper.selectHyWorkTaskByType();
+        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.selectFsUserIdSopUserLogsInfoBySopId(qwSop.getId());
+            if (qwSopLogs==null || qwSopLogs.isEmpty()) {
+                continue;
+            }
+            List<FsCourseWatchLogTaskVO> fsCourseWatchLogs = fsCourseWatchLogMapper.selectFsCourseWatchLogByDaySopIdFsUser4(qwSop.getId());
+            if (fsCourseWatchLogs==null || fsCourseWatchLogs.isEmpty()) {
+                continue;
+            }
+            List<HyWorkTask> qwWorkTasks = new ArrayList<>();
+            for (SopUserLogsInfo qwSopLog : qwSopLogs) {
+                Map<Long, FsCourseWatchLogTaskVO> map = fsCourseWatchLogs.stream()
+                        .collect(Collectors.toMap(FsCourseWatchLogTaskVO::getUserId, data -> data,(oldValue, newValue) -> newValue ));
+                FsCourseWatchLogTaskVO fsCourseWatchLog = map.get(qwSopLog.getFsUserId());
+                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 = getHyWorkCourseScore(fsCourseWatchLog.getLogType(), day,fsCourseWatchLog.getLevel());
+                if (score==0){
+                    continue;
+                }
+                HyWorkTask qwWorkTask = new HyWorkTask();
+                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()){
+                hyWorkTaskMapper.insertQwWorkTaskBatch(qwWorkTasks);
+            }
+
+        }
+    }
+
+    @Override
+    public void delHyWorkTaskByOver() {
+        List<Long> longs = fsCourseWatchLogMapper.selectFsCourseWatchLogByFinish();
+
+        List<QwWorkTask> qwWorkTasks = hyWorkTaskMapper.selectHyWorkTaskByTypeStatus();
+
+        Set<Long> targetIds = new HashSet<>(longs);
+
+        List<Long> overIds = qwWorkTasks.stream()
+                .map(QwWorkTask::getId)
+                .filter(targetIds::contains)
+                .collect(Collectors.toList());
+        if (overIds.isEmpty()){
+            return;
+        }
+        hyWorkTaskMapper.updateHyWorkTaskStatus(overIds);
+
+    }
+
+    @Override
+    public void addHyWorkByCourseLastTime() {
+        List<QwSop> qwSops = qwSopMapper.selectQwSopByIsRating();
+        List<Long> extIds = hyWorkTaskMapper.selectHyWorkTaskByType();
+        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 = fsCourseWatchLogMapper.selectFsCourseWatchLogByDaySopId3LastTime(qwSop.getId(),lastTime);
+            if (fsCourseWatchLogs==null || fsCourseWatchLogs.isEmpty()) {
+                continue;
+            }
+            List<HyWorkTask> 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 = getHyWorkCourseScore(fsCourseWatchLog.getLogType(), day,fsCourseWatchLog.getLevel());
+                if (score==0){
+                    continue;
+                }
+                HyWorkTask qwWorkTask = new HyWorkTask();
+                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()){
+
+                hyWorkTaskMapper.insertQwWorkTaskBatch(qwWorkTasks);
+            }
+
+        }
+    }
+
+    /**
+     * 查询企微任务看板
+     * @param params 参数
+     * @return list
+     */
+    @Override
+    public List<HyWorkTask> selectHyWorkTaskListByMap(Map<String, Object> params) {
+        return hyWorkTaskMapper.selectHyWorkTaskListByMap(params);
+    }
+
+    // 定义批处理大小常量
+    private static final int BATCH_SIZE = 1000;
+
+    @Override
+    public void hyWorkTask() {
+
+        // 获取看课中断和待看的先导课
+        List<FsCourseWatchLog> fsCourseWatchLogs = hyWorkTaskMapper.hyWorkTaskGetInterruptedAndFirst();
+        if(CollectionUtils.isEmpty(fsCourseWatchLogs)){
+            log.info("[获取看课中断和待看的先导课] 没有找到数据,已经跳过!");
+            return;
+        }
+        List<HyWorkTask> batchList = new ArrayList<>(BATCH_SIZE);
+        int totalProcessed = 0;
+        int batchCount = 0;
+        for (FsCourseWatchLog fsCourseWatchLog : fsCourseWatchLogs) {
+            HyWorkTask hyWorkTask = new HyWorkTask();
+            hyWorkTask.setCreateTime(DateUtils.getNowDate());
+            hyWorkTask.setExtId(fsCourseWatchLog.getQwExternalContactId());
+            hyWorkTask.setCompanyId(fsCourseWatchLog.getCompanyId());
+            hyWorkTask.setCompanyUserId(fsCourseWatchLog.getCompanyUserId());
+            hyWorkTask.setUserId(fsCourseWatchLog.getUserId());
+            if(fsCourseWatchLog.getPeriodId() != null) {
+                hyWorkTask.setUserLogsId(String.valueOf(fsCourseWatchLog.getPeriodId()));
+            }
+            hyWorkTask.setStatus(0);
+            hyWorkTask.setUserId(fsCourseWatchLog.getUserId());
+
+            // 看课中断
+            if(fsCourseWatchLog.getLogType()==4){
+                hyWorkTask.setType(2);
+                Date createTime = fsCourseWatchLog.getCreateTime();
+                long day = DateUtils.getDaysDifferenceFromNow(createTime);
+                hyWorkTask.setTitle("第"+day+"天"+"看课中断");
+            } else {
+                hyWorkTask.setType(1);
+                hyWorkTask.setTitle(fsCourseWatchLog.getLogType()==3?"先导课待看课":"先导课完课");
+            }
+            hyWorkTask.setScore(fsCourseWatchLog.getLogType()==3?4:3);
+
+            batchList.add(hyWorkTask);
+            totalProcessed++;
+
+            if (batchList.size() >= BATCH_SIZE) {
+                batchCount++;
+                log.info("处理到第 {} 条数据,开始插入第 {} 批 ({} 条)...", totalProcessed, batchCount, batchList.size());
+                try {
+                    hyWorkTaskMapper.insertQwWorkTaskBatch(batchList);
+                    log.info("第 {} 批插入成功.", batchCount);
+                } catch (Exception e) {
+                    log.error("第 {} 批插入时发生错误: {}", batchCount, e.getMessage(), e);
+                }
+                batchList.clear();
+            }
+        }
+        if (!batchList.isEmpty()) {
+            batchCount++;
+            log.info("处理完成,开始插入最后一批 ({} 条)...", batchList.size());
+            try {
+                hyWorkTaskMapper.insertQwWorkTaskBatch(batchList);
+                log.info("最后一批插入成功.");
+            } catch (Exception e) {
+                log.error("最后一批插入时发生错误: {}", e.getMessage(), e);
+            }
+            batchList.clear();
+        }
+        log.info("hyWorkTask 任务执行完毕,共处理 {} 条数据,分 {} 批插入.", totalProcessed, batchCount > 0 ? batchCount : (totalProcessed > 0 ? 1 : 0));
+
+    }
+
+    /**
+     * 根据SOP执行日志和特定条件,为符合要求的外部联系人添加企业微信工作任务。
+     * <p>
+     * 此方法仅在传入的 `day` 参数大于7时执行。
+     * 它会查询指定 `QwSop` 在过去 `day` 天内的用户执行日志 (`SopUserLogsInfo`)。
+     * 遍历日志,获取关联的外部联系人 (`QwExternalContact`)。
+     * 对联系人进行筛选:必须存在,且其级别 (`level`) 不能为 null、0 或 4。
+     * 对于通过筛选的联系人,调用内部的 `insertQwWorkTask` 方法来创建任务。
+     * </p>
+     *
+     * @param today   当前日期,用于计算查询日志的起始日期。
+     * @param day     回溯的天数。只有当 `day` 大于 7 时,才会执行添加任务的逻辑。
+     * @param qwSop   企业微信SOP(标准操作流程)对象,包含需要查询日志的SOP ID。
+     * @param title   要创建的企业微信工作任务的标题。
+     * @param map     一个映射表,键可能是外部联系人的级别 (`level`),值可能是传递给 `insertQwWorkTask` 的参数(例如优先级或特定配置)。
+     *                如果联系人级别在map中不存在,则使用默认值0。
+     * @author xdd
+     * @version 1.0
+     * @since yyyy-MM-dd // 建议替换为实际的编写或修改日期
+     */
+    private void addHyWorkTask(LocalDate today, Integer day, QwSop qwSop, String title,Map<Integer, Integer> map) {
+        if (day>7){
+            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+            String minDay = today.minusDays(day).format(formatter);
+            List<SopUserLogsInfo> qwSopLogs = sopUserLogsInfoMapper.selectDayBySopId(qwSop.getId(), minDay);
+            for (SopUserLogsInfo qwSopLog : qwSopLogs) {
+                QwExternalContact qwExternalContact = qwExternalContactMapper.selectQwExternalContactById(qwSopLog.getExternalId());
+                if (qwExternalContact==null){
+                    continue;
+                }
+                if (qwExternalContact.getLevel()==null||qwExternalContact.getLevel()==4||qwExternalContact.getLevel()==0){
+                    continue;
+                }
+                System.out.println(qwExternalContact.getId()+"ok");
+
+
+                insertHyWorkTask(qwSopLog.getId(),qwExternalContact,3,title,map.getOrDefault(qwExternalContact.getLevel(), 0));
+            }
+        }
+    }
+
+
+    private void insertHyWorkTask(String sopId,QwExternalContact qwExternalContact, Integer type, String title, Integer score) {
+        HyWorkTask qwWorkTask = new HyWorkTask();
+        qwWorkTask.setCreateTime(DateUtils.getNowDate());
+        qwWorkTask.setExtId(qwExternalContact.getId());
+        qwWorkTask.setCompanyId(qwExternalContact.getCompanyId());
+        qwWorkTask.setCompanyUserId(qwExternalContact.getCompanyUserId());
+        qwWorkTask.setQwUserId(qwExternalContact.getQwUserId());
+        qwWorkTask.setSopId(sopId);
+        qwWorkTask.setType(type);
+        qwWorkTask.setStatus(0);
+        qwWorkTask.setTitle(title);
+        qwWorkTask.setScore(score);
+        baseMapper.insertHyWorkTask(qwWorkTask);
+    }
+
+
+}

+ 1 - 1
fs-service/src/main/java/com/fs/sop/domain/QwSop.java

@@ -92,7 +92,7 @@ public class QwSop implements Serializable
     */
     private Integer isRating;
 
-    private String courseDay;
+    private Integer courseDay;
 
 
     // 是否固定营期

+ 4 - 0
fs-service/src/main/java/com/fs/sop/mapper/SopUserLogsInfoMapper.java

@@ -198,4 +198,8 @@ public interface SopUserLogsInfoMapper {
     List<String> selectSopUserLogsInfoByExtId(@Param("extId")Long extId );
     @DataSource(DataSourceType.SOP)
     void updateSopUserLogsInfoFsUserIdById(@Param("data")List<String> list,  @Param("userId") Long userId);
+
+    @DataSource(DataSourceType.SOP)
+    @Select("SELECT create_time,fs_user_id  FROM sop_user_logs_info where sop_id = #{sopId} ")
+    List<SopUserLogsInfo> selectFsUserIdSopUserLogsInfoBySopId(@Param("sopId")String sopId);
 }

+ 55 - 0
fs-service/src/main/resources/mapper/hy/HyWatchLogMapper.xml

@@ -0,0 +1,55 @@
+<?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.course.mapper.HyWatchLogMapper">
+
+    <resultMap type="com.fs.course.vo.HyWatchLog" id="HyWatchLogResult">
+        <result property="id"    column="id"    />
+        <result property="extId"    column="ext_id"    />
+        <result property="qwUserId"    column="qw_user_id"    />
+        <result property="status"    column="status"    />
+        <result property="day"    column="day"    />
+        <result property="project"    column="project"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="lineTime"    column="line_time"    />
+    </resultMap>
+
+    <sql id="selectQwWatchLogVo">
+        select id, ext_id, qw_user_id,line_time, status, day, project, create_time from hy_watch_log
+    </sql>
+    <insert id="insertHyWatchLogBatch">
+        INSERT INTO hy_watch_log (
+        ext_id,
+        qw_user_id,
+        status,
+        day,
+        project,
+        company_id,
+        company_user_id,
+        create_time,
+        line_time,
+        course_id,
+        video_id,
+        fs_user_id
+        )
+        VALUES
+        <foreach collection="hyWatchLogs" item="log" separator=",">
+            (
+            #{log.extId},
+            #{log.qwUserId},
+            #{log.status},
+            #{log.day},
+            #{log.project},
+            #{log.companyId},
+            #{log.companyUserId},
+            #{log.createTime},
+            #{log.lineTime},
+            #{log.courseId},
+            #{log.videoId},
+            #{log.fsUserId}
+            )
+        </foreach>
+    </insert>
+
+</mapper>

+ 209 - 0
fs-service/src/main/resources/mapper/qw/HyWorkTaskMapper.xml

@@ -0,0 +1,209 @@
+<?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.qw.mapper.HyWorkTaskMapper">
+
+    <resultMap type="QwWorkTask" id="HyWorkTaskResult">
+        <result property="id"    column="id"    />
+        <result property="extId"    column="ext_id"    />
+        <result property="qwUserId"    column="qw_user_id"    />
+        <result property="type"    column="type"    />
+        <result property="status"    column="status"    />
+        <result property="remark"    column="remark"    />
+        <result property="score"    column="score"    />
+        <result property="sopId"    column="sop_id"    />
+        <result property="companyId"    column="company_id"    />
+        <result property="companyUserId"    column="company_user_id"    />
+        <result property="createTime"    column="create_time"    />
+        <result property="updateTime"    column="update_time"    />
+        <result property="title"    column="title"    />
+    </resultMap>
+
+    <sql id="selectHyWorkTaskVo">
+        select id, ext_id, qw_user_id, type,title, status, remark, score, sop_id, company_id, company_user_id, create_time, update_time from hy_work_task
+    </sql>
+
+    <select id="selectHyWorkTaskList" parameterType="QwWorkTask" resultMap="HyWorkTaskResult">
+        <include refid="selectHyWorkTaskVo"/>
+        <where>
+            <if test="extId != null "> and ext_id = #{extId}</if>
+            <if test="qwUserId != null "> and qw_user_id = #{qwUserId}</if>
+            <if test="type != null "> and type = #{type}</if>
+            <if test="status != null "> and status = #{status}</if>
+            <if test="score != null "> and score = #{score}</if>
+            <if test="sopId != null  and sopId != ''"> and sop_id = #{sopId}</if>
+            <if test="companyId != null "> and company_id = #{companyId}</if>
+            <if test="companyUserId != null "> and company_user_id = #{companyUserId}</if>
+        </where>
+    </select>
+
+    <select id="selectHyWorkTaskById" parameterType="Long" resultMap="HyWorkTaskResult">
+        <include refid="selectHyWorkTaskVo"/>
+        where id = #{id}
+    </select>
+    <select id="selectHyWorkTaskListVONew" resultType="com.fs.qw.vo.QwWorkTaskListVO">
+        SELECT t.*
+        FROM hy_work_task t
+        INNER JOIN (
+        SELECT t_inner.id
+        FROM qw_work_task t_inner
+        <where>
+            DATE(t_inner.create_time) = CURDATE()
+            <if test="extId != null "> and t_inner.ext_id = #{extId}</if>
+            <if test="qwUserId != null "> and t_inner.qw_user_id = #{qwUserId}</if>
+            <if test="type != null "> and t_inner.type = #{type}</if>
+            <if test="status != null "> and t_inner.status = #{status}</if>
+            <if test="score != null "> and t_inner.score = #{score}</if>
+            <if test="sopId != null  and sopId != ''"> and t_inner.sop_id = #{sopId}</if>
+            <if test="companyId != null "> and t_inner.company_id = #{companyId}</if>
+            <if test="companyUserId != null "> and t_inner.company_user_id = #{companyUserId}</if>
+        </where>
+        ORDER BY t_inner.score DESC, t_inner.id DESC
+        LIMIT ${(pageNum-1)*pageSize}, ${pageSize}
+        ) AS filtered_ids ON t.id = filtered_ids.id
+        ORDER BY t.score DESC, t.id DESC
+    </select>
+
+    <select id="selectHyWorkTaskListByMap" resultType="com.fs.qw.domain.QwWorkTask">
+        select
+            qwt.*
+        from hy_work_task qwt
+        where qwt.qw_user_id = #{params.qwUserId}
+          and date(qwt.create_time) = #{params.date}
+          and qwt.company_id = #{params.companyId}
+          and qwt.company_user_id = #{params.companyUserId}
+          and qwt.status = 0
+    </select>
+    <select id="selectHyWorkTaskListVONewCount" resultType="java.lang.Long">
+        select count(1) from hy_work_task t
+        <where>
+            DATE(t.create_time) = CURDATE()
+            <if test="extId != null "> and t.ext_id = #{extId}</if>
+            <if test="qwUserId != null "> and t.qw_user_id = #{qwUserId}</if>
+            <if test="type != null "> and t.type = #{type}</if>
+            <if test="status != null "> and t.status = #{status}</if>
+            <if test="score != null "> and t.score = #{score}</if>
+            <if test="sopId != null  and sopId != ''"> and t.sop_id = #{sopId}</if>
+            <if test="companyId != null "> and t.company_id = #{companyId}</if>
+            <if test="companyUserId != null "> and t.company_user_id = #{companyUserId}</if>
+        </where>
+        order by t.score desc,t.id desc
+    </select>
+    <select id="hyWorkTask">
+
+    </select>
+    <select id="hyWorkTaskGetInterruptedAndFirst" resultType="com.fs.course.domain.FsCourseWatchLog">
+        SELECT
+            fcl.*
+        FROM
+            fs_course_watch_log fcl
+                INNER JOIN
+            fs_user_course_video fucv ON fcl.video_id = fucv.video_id
+        WHERE
+            DATE(fcl.create_time) = DATE_SUB(CURDATE(), INTERVAL 1 DAY)
+          AND (
+            fcl.log_type = 4 -- 看课中断
+           OR
+            (fcl.log_type = 3 AND fucv.is_first = 1) -- 待看的先导课 (根据 is_first 标志判断)
+            )
+    </select>
+
+    <insert id="insertHyWorkTask" parameterType="QwWorkTask" useGeneratedKeys="true" keyProperty="id">
+        insert into hy_work_task
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="extId != null">ext_id,</if>
+            <if test="qwUserId != null">qw_user_id,</if>
+            <if test="type != null">type,</if>
+            <if test="status != null">status,</if>
+            <if test="remark != null">remark,</if>
+            <if test="score != null">score,</if>
+            <if test="sopId != null">sop_id,</if>
+            <if test="companyId != null">company_id,</if>
+            <if test="companyUserId != null">company_user_id,</if>
+            <if test="createTime != null">create_time,</if>
+            <if test="updateTime != null">update_time,</if>
+            <if test="title != null">title,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="extId != null">#{extId},</if>
+            <if test="qwUserId != null">#{qwUserId},</if>
+            <if test="type != null">#{type},</if>
+            <if test="status != null">#{status},</if>
+            <if test="remark != null">#{remark},</if>
+            <if test="score != null">#{score},</if>
+            <if test="sopId != null">#{sopId},</if>
+            <if test="companyId != null">#{companyId},</if>
+            <if test="companyUserId != null">#{companyUserId},</if>
+            <if test="createTime != null">#{createTime},</if>
+            <if test="updateTime != null">#{updateTime},</if>
+            <if test="title != null">#{title},</if>
+         </trim>
+    </insert>
+
+    <update id="updateHyWorkTask" parameterType="QwWorkTask">
+        update hy_work_task
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="extId != null">ext_id = #{extId},</if>
+            <if test="qwUserId != null">qw_user_id = #{qwUserId},</if>
+            <if test="type != null">type = #{type},</if>
+            <if test="status != null">status = #{status},</if>
+            <if test="remark != null">remark = #{remark},</if>
+            <if test="score != null">score = #{score},</if>
+            <if test="sopId != null">sop_id = #{sopId},</if>
+            <if test="companyId != null">company_id = #{companyId},</if>
+            <if test="companyUserId != null">company_user_id = #{companyUserId},</if>
+            <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+            <if test="title != null">title = #{title},</if>
+        </trim>
+        where id = #{id}
+    </update>
+    <insert id="insertQwWorkTaskBatch" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="id">
+        INSERT INTO hy_work_task (
+        ext_id,
+        qw_user_id,
+        status,
+        type,
+        title,
+        remark,
+        score,
+        sop_id,
+        company_id,
+        company_user_id,
+        create_time,
+        update_time,
+        user_logs_id,
+        user_id
+        )
+        VALUES
+        <foreach collection="qwWorkTasks" item="log" separator=",">
+            (
+            #{log.extId},
+            #{log.qwUserId},
+            #{log.status},
+            #{log.type},
+            #{log.title},
+            #{log.remark},
+            #{log.score},
+            #{log.sopId},
+            #{log.companyId},
+            #{log.companyUserId},
+            #{log.createTime},
+            #{log.updateTime},
+            #{log.userLogsId},
+            #{log.userId}
+            )
+        </foreach>
+    </insert>
+    <delete id="deleteHyWorkTaskById" parameterType="Long">
+        delete from hy_work_task where id = #{id}
+    </delete>
+
+    <delete id="deleteHyWorkTaskByIds" parameterType="String">
+        delete from hy_work_task where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>