瀏覽代碼

feat:会员发课接口、一键催课等接口

caoliqin 1 周之前
父節點
當前提交
0c2cfa4d60
共有 19 個文件被更改,包括 645 次插入90 次删除
  1. 8 0
      fs-company-app/src/main/java/com/fs/app/controller/CompanyUserController.java
  2. 71 10
      fs-company-app/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java
  3. 3 0
      fs-service/src/main/java/com/fs/course/dto/BatchSendCourseDTO.java
  4. 32 0
      fs-service/src/main/java/com/fs/course/dto/BatchUrgeCourseDTO.java
  5. 3 0
      fs-service/src/main/java/com/fs/course/mapper/FsCourseWatchLogMapper.java
  6. 5 3
      fs-service/src/main/java/com/fs/course/param/FsCourseWatchLogListParam.java
  7. 5 5
      fs-service/src/main/java/com/fs/course/param/newfs/FsCourseWatchAppParam.java
  8. 21 2
      fs-service/src/main/java/com/fs/course/service/impl/FsCourseWatchLogServiceImpl.java
  9. 5 0
      fs-service/src/main/java/com/fs/course/vo/FsCourseWatchLogListVO.java
  10. 52 0
      fs-service/src/main/java/com/fs/course/vo/newfs/FsImSendLogVO.java
  11. 19 0
      fs-service/src/main/java/com/fs/im/domain/FsImMsgSendLog.java
  12. 21 7
      fs-service/src/main/java/com/fs/im/mapper/FsImMsgSendLogMapper.java
  13. 32 7
      fs-service/src/main/java/com/fs/im/service/IFsImMsgSendLogService.java
  14. 11 0
      fs-service/src/main/java/com/fs/im/service/OpenIMService.java
  15. 71 0
      fs-service/src/main/java/com/fs/im/service/impl/FsImMsgSendLogServiceImpl.java
  16. 115 47
      fs-service/src/main/java/com/fs/im/service/impl/OpenIMServiceImpl.java
  17. 66 0
      fs-service/src/main/java/com/fs/im/vo/FsImMsgSendLogVO.java
  18. 21 2
      fs-service/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml
  19. 84 7
      fs-service/src/main/resources/mapper/im/FsImMsgSendLogMapper.xml

+ 8 - 0
fs-company-app/src/main/java/com/fs/app/controller/CompanyUserController.java

@@ -394,4 +394,12 @@ public class CompanyUserController extends AppBaseController {
                 .collect(Collectors.toList());
         return R.ok().put("data",filteredDictVOS);
     }
+
+    @ApiOperation("查询所有项目")
+    @GetMapping("/getDictProject")
+    public R getDictProject(){
+        List<DictVO> dictVOS = dictDataService.selectDictDataListByType("sys_course_project");
+        return R.ok().put("data",dictVOS);
+    }
+
 }

+ 71 - 10
fs-company-app/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java

@@ -1,15 +1,19 @@
 package com.fs.app.controller;
 
+import cn.hutool.core.date.DateUtil;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fs.app.annotation.Login;
 import com.fs.app.config.ImageStorageConfig;
+import com.fs.common.annotation.Log;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
+import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.StringUtils;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.course.domain.FsUserCoursePeriod;
 import com.fs.course.dto.BatchSendCourseDTO;
+import com.fs.course.dto.BatchUrgeCourseDTO;
 import com.fs.course.param.FsCourseLinkCreateParam;
 import com.fs.course.param.FsCourseWatchLogListParam;
 import com.fs.course.param.FsWatchCourseTimeParam;
@@ -20,12 +24,13 @@ import com.fs.course.param.newfs.UserCourseVideoPageParam;
 import com.fs.course.service.*;
 import com.fs.course.vo.FsCourseWatchLogListVO;
 import com.fs.course.vo.FsUserCourseParticipationRecordVO;
-import com.fs.course.vo.newfs.FsUserCourseListVO;
-import com.fs.course.vo.newfs.FsUserCourseVideoDetailsVO;
-import com.fs.course.vo.newfs.FsUserCourseVideoPageListVO;
-import com.fs.course.vo.newfs.FsUserVideoListVO;
+import com.fs.course.vo.newfs.*;
+import com.fs.im.domain.FsImMsgSendLog;
 import com.fs.im.dto.OpenImResponseDTO;
+import com.fs.im.service.IFsImMsgSendDetailService;
+import com.fs.im.service.IFsImMsgSendLogService;
 import com.fs.im.service.OpenIMService;
+import com.fs.im.vo.FsImMsgSendLogVO;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.Api;
@@ -36,10 +41,7 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
 
 import java.io.InputStream;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
+import java.util.*;
 import java.util.stream.Collectors;
 
 
@@ -72,6 +74,9 @@ public class FsUserCourseVideoController extends AppBaseController {
     @Autowired
     private OpenIMService openIMService;
 
+    @Autowired
+    private IFsImMsgSendLogService imMsgSendLogService;
+
     @Login
     @GetMapping("/pageList")
     @ApiOperation("课程分页列表")
@@ -267,19 +272,75 @@ public class FsUserCourseVideoController extends AppBaseController {
         return openIMService.batchSendCourse(batchSendCourseDTO);
     }
 
+    @ApiOperation("会员一键催课")
+    @PostMapping("/batchUrgeCourse")
+    public OpenImResponseDTO batchUrgeCourse(@RequestBody BatchUrgeCourseDTO batchUrgeCourseDTO) throws JsonProcessingException {
+        // 查询生成短链需要的内容
+        Map<String, Object> params = new HashMap<>();
+        params.put("logDetailIds", batchUrgeCourseDTO.getImMsgSendDetailId());
+        List<FsImMsgSendLog> fsImMsgSendLogs = imMsgSendLogService.selectSendLogListByDetailId(params);
+        OpenImResponseDTO openImResponseDTO = null;
+        for (FsImMsgSendLog fsImMsgSendLog : fsImMsgSendLogs) {
+            FsCourseLinkCreateParam fsCourseLinkCreateParam = new FsCourseLinkCreateParam();
+            BeanUtils.copyProperties(fsImMsgSendLog, fsCourseLinkCreateParam);
+            fsCourseLinkCreateParam.setId(fsImMsgSendLog.getPeriodDaysId());
+            R courseSortLink = fsUserCourseService.createAppCourseSortLink(fsCourseLinkCreateParam);
+            String url = courseSortLink.get("url").toString();
+            openImResponseDTO = openIMService.batchUrgeCourse(batchUrgeCourseDTO, fsImMsgSendLog, url);
+        }
+        return openImResponseDTO;
+    }
+
     @Login
-    @ApiOperation("app-看课记录")
+    @ApiOperation("app-看课记录(包含今日完课、今日催课)")
     @GetMapping("/courseWatchLog")
-    public ResponseResult<PageInfo<FsCourseWatchLogListVO>> list(FsCourseWatchAppParam fsCourseWatchAppParam)
+    public ResponseResult<PageInfo<FsCourseWatchLogListVO>> getCourseWatchLog(FsCourseWatchAppParam fsCourseWatchAppParam)
     {
         FsCourseWatchLogListParam param = new FsCourseWatchLogListParam();
         BeanUtils.copyProperties(fsCourseWatchAppParam, param);
         param.setCompanyUserId(Long.parseLong(getUserId()));
         param.setCompanyId(getCompanyId());
+        param.setSTime(DateUtil.beginOfDay(new Date()));
+        param.setETime(DateUtil.endOfDay(new Date()));
 //        startPage();
         PageHelper.startPage(fsCourseWatchAppParam.getPageNum (), fsCourseWatchAppParam.getPageSize());
         List<FsCourseWatchLogListVO> list = fsCourseWatchLogService.selectFsCourseWatchLogListVO(param);
         PageInfo<FsCourseWatchLogListVO> pageInfo = new PageInfo<>(list);
         return ResponseResult.ok(pageInfo);
     }
+
+    @Login
+    @ApiOperation("任务列表")
+    @GetMapping("/im/sendLog")
+    public ResponseResult<PageInfo<FsImSendLogVO>> imSendLog(@RequestParam(defaultValue = "1") Integer pageNum,
+                                                             @RequestParam(defaultValue = "10") Integer pageSize) {
+        Map<String, Object> params = new HashMap<>();
+        params.put("companyId", getCompanyId());
+        params.put("companyUserId", Long.parseLong(getUserId()));
+
+        PageHelper.startPage(pageNum, pageSize);
+        List<FsImSendLogVO> list = imMsgSendLogService.selectFsImSendLogList(params);
+        PageInfo<FsImSendLogVO> pageInfo = new PageInfo<>(list);
+        return ResponseResult.ok(pageInfo);
+    }
+
+//    @Login
+    @ApiOperation("任务详情")
+    @GetMapping("/im/sendLog/detail")
+    public ResponseResult<FsImMsgSendLogVO> imSendLogDetail(@RequestParam Long logId) {
+        FsImMsgSendLog fsImMsgSendLog = imMsgSendLogService.selectFsImMsgSendLogByLogId(logId);
+        FsImMsgSendLogVO fsImMsgSendLogVO = new FsImMsgSendLogVO();
+        BeanUtils.copyProperties(fsImMsgSendLog, fsImMsgSendLogVO);
+
+        return ResponseResult.ok(fsImMsgSendLogVO);
+    }
+
+    @Login
+    @ApiOperation("删除任务")
+    @DeleteMapping("/im/sendLog")
+    @Log(title = "任务-删除任务", businessType = BusinessType.DELETE)
+    public ResponseResult<Boolean> deleteImSendLog(@RequestParam Long logId) {
+        return imMsgSendLogService.deleteFsImMsgSendLogAndDetail(logId);
+    }
+
 }

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

@@ -41,6 +41,9 @@ public class BatchSendCourseDTO implements Serializable {
     @ApiModelProperty(value = "标签ids,如果没有companyUserId或者userIds则必传")
     private List<Long> tagIds;
 
+    @ApiModelProperty(value = "标签名称")
+    private List<String> tagNames;
+
     @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     @ApiModelProperty(value = "发送消息时间,定时发送需要传")
     private Date sendTime;

+ 32 - 0
fs-service/src/main/java/com/fs/course/dto/BatchUrgeCourseDTO.java

@@ -0,0 +1,32 @@
+package com.fs.course.dto;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 批量催课-入参
+ */
+@Data
+public class BatchUrgeCourseDTO implements Serializable {
+
+    @NotBlank
+    @ApiModelProperty(value = "任务详情id", required = true)
+    private List<Long> imMsgSendDetailId;
+
+    @NotNull
+    @ApiModelProperty(value = "是否需要发课", required = true)
+    private Boolean isSendCourse;
+
+    @NotNull
+    @ApiModelProperty(value = "发课内容", required = true)
+    private String title;
+
+    @NotBlank
+    @ApiModelProperty(value = "催课内容")
+    private String urgeContent;
+}

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

@@ -490,4 +490,7 @@ public interface FsCourseWatchLogMapper extends BaseMapper<FsCourseWatchLog> {
     Long selectByWatchlxDay(@Param("userId") Long userId,@Param("projectId")  Long projectId);
 
     List<FsCourseWatchLogListVO> selectFsCourseWatchLogListVOexport(@Param("maps") FsCourseWatchLogListParam param);
+
+    List<FsCourseWatchLog> getWatchCourseByVideoId(@Param("videoId") Long videoId, @Param("userIds") List<Long> userIds);
+
 }

+ 5 - 3
fs-service/src/main/java/com/fs/course/param/FsCourseWatchLogListParam.java

@@ -1,9 +1,6 @@
 package com.fs.course.param;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
-import com.fs.common.annotation.Excel;
-import com.fs.common.core.domain.BaseEntity;
-import io.swagger.models.auth.In;
 import lombok.Data;
 
 import java.io.Serializable;
@@ -78,4 +75,9 @@ public class FsCourseWatchLogListParam implements Serializable {
 
     //是否是会员
     private Integer isVip;
+
+    /**
+     * 所属项目
+     */
+    private Integer project;
 }

+ 5 - 5
fs-service/src/main/java/com/fs/course/param/newfs/FsCourseWatchAppParam.java

@@ -16,16 +16,16 @@ public class FsCourseWatchAppParam implements Serializable {
     @ApiModelProperty(value = "页大小,默认为10", required = true)
     private Integer pageSize = 10;
 
+    /**
+     * 类型,1-看课中,2-完课,3-待看课,4-看课中断
+     */
     @ApiModelProperty(value = "类型,1-看课中,2-完课,3-待看课,4-看课中断", required = true)
     private Integer logType;
 
     @ApiModelProperty(value = "发送方式:1-个微,2-企微", required = true)
     private Integer sendType;
 
-    @ApiModelProperty(value = "发送方式:1-个微,2-企微", required = true)
-    private Long companyId;
-
-    @ApiModelProperty(value = "发送方式:1-个微,2-企微", required = true)
-    private Long companyUserId;
+    @ApiModelProperty(value = "所属项目")
+    private Integer project;
 
 }

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

@@ -49,7 +49,9 @@ import com.fs.sop.mapper.SopUserLogsMapper;
 import com.fs.sop.service.IQwSopLogsService;
 import com.fs.store.service.cache.IFsUserCacheService;
 import com.fs.store.service.cache.IFsUserCourseCacheService;
+import com.fs.system.mapper.SysDictDataMapper;
 import com.fs.system.service.ISysConfigService;
+import com.fs.system.vo.DictVO;
 import com.hc.openapi.tool.util.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -126,6 +128,9 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
     @Autowired
     private HyWatchLogMapper hyWatchLogMapper;
 
+    @Autowired
+    private SysDictDataMapper sysDictDataMapper;
+
     /**
      * 查询短链课程看课记录
      *
@@ -423,7 +428,7 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
             log.error("key中id为S:{}", videoDuration);
         }
 
-        
+
         if (videoDuration==null){
             FsUserCourseVideo video = courseVideoMapper.selectFsUserCourseVideoByVideoId(videoId);
             videoDuration=video.getDuration();
@@ -618,7 +623,21 @@ public class FsCourseWatchLogServiceImpl extends ServiceImpl<FsCourseWatchLogMap
 
     @Override
     public List<FsCourseWatchLogListVO> selectFsCourseWatchLogListVO(FsCourseWatchLogListParam param) {
-        return fsCourseWatchLogMapper.selectFsCourseWatchLogListVO(param);
+        List<FsCourseWatchLogListVO> list = fsCourseWatchLogMapper.selectFsCourseWatchLogListVO(param);
+
+        List<DictVO> dictVOS = sysDictDataMapper.selectDictDataListByType("sys_course_project");
+        if(!dictVOS.isEmpty()){
+            Map<String, String> projectMap = dictVOS.stream().collect(Collectors.toMap(DictVO::getDictValue, DictVO::getDictLabel));
+            for (FsCourseWatchLogListVO watchLog : list) {
+                if (watchLog.getProject() != null) {
+                    String projectName = projectMap.get(watchLog.getProject().toString());
+                    if (projectName != null) {
+                        watchLog.setProjectName(projectName);
+                    }
+                }
+            }
+        }
+        return list;
     }
 
     @Override

+ 5 - 0
fs-service/src/main/java/com/fs/course/vo/FsCourseWatchLogListVO.java

@@ -118,5 +118,10 @@ public class FsCourseWatchLogListVO extends BaseEntity
     private Long videoId;
     private Integer isVip;
 
+    /**
+     * im发送消息详情id
+     */
+    private Long imMsgSendDetailId;
+
 
 }

+ 52 - 0
fs-service/src/main/java/com/fs/course/vo/newfs/FsImSendLogVO.java

@@ -0,0 +1,52 @@
+package com.fs.course.vo.newfs;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * @author caolq
+ * im任务发送记录 VO
+ */
+@Data
+public class FsImSendLogVO implements Serializable {
+
+    @ApiModelProperty(value = "发送记录id")
+    private Long logId;
+
+    @ApiModelProperty(value = "视频id")
+    private Long videoId;
+
+    @ApiModelProperty(value = "视频标题")
+    private String videoName;
+
+    @ApiModelProperty(value = "计划发课时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date planSendTime;
+
+    @ApiModelProperty(value = "标签ids")
+    private String[] tagIds;
+
+    @ApiModelProperty(value = "标签名称")
+    private String[] tagNames;
+
+    @ApiModelProperty(value = "用户id,多个用逗号隔开")
+    private String userIds;
+
+    @ApiModelProperty(value = "用户昵称,多个用逗号隔开")
+    private String nickNames;
+
+    @ApiModelProperty(value = "是否催课,1-是;0-否")
+    private Integer isUrgeCourse;
+
+    @ApiModelProperty(value = "催课文案")
+    private String urgeSendTitle;
+
+    @ApiModelProperty(value = "发送状态,1-已发送;2-待发送")
+    private Integer sendStatus;
+
+
+}

+ 19 - 0
fs-service/src/main/java/com/fs/im/domain/FsImMsgSendLog.java

@@ -83,5 +83,24 @@ public class FsImMsgSendLog extends BaseEntity{
     @Excel(name = "消息类型,1-发课;2-催课")
     private Integer msgType;
 
+    /** 关联id,发课和催课记录关联id */
+    @Excel(name = "关联id,发课和催课记录关联id")
+    private String sendUnionId;
+
+    /** 关联标签id,仅用于展示 */
+    @Excel(name = "关联标签id,仅用于展示")
+    private String tagIds;
+
+    /** 关联标签名称,用于展示 */
+    @Excel(name = "关联标签名称,用于展示")
+    private String tagNames;
+
+    /** 课程所属项目id */
+    @Excel(name = "课程所属项目id")
+    private Long projectId;
+
+    /** 营期课程表id */
+    @Excel(name = "营期课程表id")
+    private Long periodDaysId;
 
 }

+ 21 - 7
fs-service/src/main/java/com/fs/im/mapper/FsImMsgSendLogMapper.java

@@ -1,19 +1,23 @@
 package com.fs.im.mapper;
 
 import java.util.List;
+import java.util.Map;
+
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.course.vo.newfs.FsImSendLogVO;
 import com.fs.im.domain.FsImMsgSendLog;
+import org.apache.ibatis.annotations.Param;
 
 /**
  * openim消息记录主Mapper接口
- * 
+ *
  * @author fs
  * @date 2025-08-21
  */
 public interface FsImMsgSendLogMapper extends BaseMapper<FsImMsgSendLog>{
     /**
      * 查询openim消息记录主
-     * 
+     *
      * @param logId openim消息记录主主键
      * @return openim消息记录主
      */
@@ -21,7 +25,7 @@ public interface FsImMsgSendLogMapper extends BaseMapper<FsImMsgSendLog>{
 
     /**
      * 查询openim消息记录主列表
-     * 
+     *
      * @param fsImMsgSendLog openim消息记录主
      * @return openim消息记录主集合
      */
@@ -29,7 +33,7 @@ public interface FsImMsgSendLogMapper extends BaseMapper<FsImMsgSendLog>{
 
     /**
      * 新增openim消息记录主
-     * 
+     *
      * @param fsImMsgSendLog openim消息记录主
      * @return 结果
      */
@@ -37,7 +41,7 @@ public interface FsImMsgSendLogMapper extends BaseMapper<FsImMsgSendLog>{
 
     /**
      * 修改openim消息记录主
-     * 
+     *
      * @param fsImMsgSendLog openim消息记录主
      * @return 结果
      */
@@ -45,7 +49,7 @@ public interface FsImMsgSendLogMapper extends BaseMapper<FsImMsgSendLog>{
 
     /**
      * 删除openim消息记录主
-     * 
+     *
      * @param logId openim消息记录主主键
      * @return 结果
      */
@@ -53,9 +57,19 @@ public interface FsImMsgSendLogMapper extends BaseMapper<FsImMsgSendLog>{
 
     /**
      * 批量删除openim消息记录主
-     * 
+     *
      * @param logIds 需要删除的数据主键集合
      * @return 结果
      */
     int deleteFsImMsgSendLogByLogIds(Long[] logIds);
+
+    /**
+     *  查询当前销售的所有发消息任务
+     * @param params 参数
+     * @return
+     */
+    List<FsImSendLogVO> selectFsImSendLogList(@Param("params") Map<String, Object> params);
+
+    List<FsImMsgSendLog> selectSendLogListByDetailId(@Param("params") Map<String, Object> params);
+
 }

+ 32 - 7
fs-service/src/main/java/com/fs/im/service/IFsImMsgSendLogService.java

@@ -1,19 +1,23 @@
 package com.fs.im.service;
 
 import java.util.List;
+import java.util.Map;
+
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.common.core.domain.ResponseResult;
+import com.fs.course.vo.newfs.FsImSendLogVO;
 import com.fs.im.domain.FsImMsgSendLog;
 
 /**
  * openim消息记录主Service接口
- * 
+ *
  * @author fs
  * @date 2025-08-21
  */
 public interface IFsImMsgSendLogService extends IService<FsImMsgSendLog>{
     /**
      * 查询openim消息记录主
-     * 
+     *
      * @param logId openim消息记录主主键
      * @return openim消息记录主
      */
@@ -21,7 +25,7 @@ public interface IFsImMsgSendLogService extends IService<FsImMsgSendLog>{
 
     /**
      * 查询openim消息记录主列表
-     * 
+     *
      * @param fsImMsgSendLog openim消息记录主
      * @return openim消息记录主集合
      */
@@ -29,7 +33,7 @@ public interface IFsImMsgSendLogService extends IService<FsImMsgSendLog>{
 
     /**
      * 新增openim消息记录主
-     * 
+     *
      * @param fsImMsgSendLog openim消息记录主
      * @return 结果
      */
@@ -37,7 +41,7 @@ public interface IFsImMsgSendLogService extends IService<FsImMsgSendLog>{
 
     /**
      * 修改openim消息记录主
-     * 
+     *
      * @param fsImMsgSendLog openim消息记录主
      * @return 结果
      */
@@ -45,7 +49,7 @@ public interface IFsImMsgSendLogService extends IService<FsImMsgSendLog>{
 
     /**
      * 批量删除openim消息记录主
-     * 
+     *
      * @param logIds 需要删除的openim消息记录主主键集合
      * @return 结果
      */
@@ -53,9 +57,30 @@ public interface IFsImMsgSendLogService extends IService<FsImMsgSendLog>{
 
     /**
      * 删除openim消息记录主信息
-     * 
+     *
      * @param logId openim消息记录主主键
      * @return 结果
      */
     int deleteFsImMsgSendLogByLogId(Long logId);
+
+    /**
+     * 查询当前销售的所有发消息任务
+     * @return FsImSendLogVO
+     */
+    List<FsImSendLogVO> selectFsImSendLogList(Map<String, Object> params);
+
+    /**
+     * 根据日志详情id获取日志主表信息
+     * @param params 参数
+     * @return
+     */
+    List<FsImMsgSendLog> selectSendLogListByDetailId(Map<String, Object> params);
+
+    /**
+     * 删除消息记录及详情和缓存
+     * @param logId 消息记录id
+     * @return
+     */
+    ResponseResult<Boolean> deleteFsImMsgSendLogAndDetail(Long logId);
+
 }

+ 11 - 0
fs-service/src/main/java/com/fs/im/service/OpenIMService.java

@@ -4,7 +4,9 @@ import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fs.common.core.domain.R;
 import com.fs.company.domain.CompanyUser;
 import com.fs.course.dto.BatchSendCourseDTO;
+import com.fs.course.dto.BatchUrgeCourseDTO;
 import com.fs.im.domain.FsImMsgSendDetail;
+import com.fs.im.domain.FsImMsgSendLog;
 import com.fs.im.dto.OpenImBatchMsgDTO;
 import com.fs.im.dto.OpenImEditConversationDTO;
 import com.fs.im.dto.OpenImMsgDTO;
@@ -70,4 +72,13 @@ public interface OpenIMService {
      */
     OpenImResponseDTO batchUrgeCourseTask(OpenImBatchMsgDTO openImBatchMsgDTO, List<FsImMsgSendDetail> imMsgSendDetailList);
 
+    /**
+     * 会员一键催课
+     * @param batchUrgeCourseDTO
+     * @param fsImMsgSendLog
+     * @return
+     * @throws JsonProcessingException
+     */
+    OpenImResponseDTO batchUrgeCourse(BatchUrgeCourseDTO batchUrgeCourseDTO, FsImMsgSendLog fsImMsgSendLog, String url) throws JsonProcessingException;
+
 }

+ 71 - 0
fs-service/src/main/java/com/fs/im/service/impl/FsImMsgSendLogServiceImpl.java

@@ -1,12 +1,23 @@
 package com.fs.im.service.impl;
 
 import java.util.List;
+import java.util.Map;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.fs.common.core.domain.ResponseResult;
+import com.fs.common.core.redis.RedisCache;
 import com.fs.common.utils.DateUtils;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.course.dto.BatchSendCourseAllDTO;
+import com.fs.course.vo.newfs.FsImSendLogVO;
+import com.fs.im.domain.FsImMsgSendDetail;
+import com.fs.im.mapper.FsImMsgSendDetailMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Service;
 import com.fs.im.mapper.FsImMsgSendLogMapper;
 import com.fs.im.domain.FsImMsgSendLog;
 import com.fs.im.service.IFsImMsgSendLogService;
+import org.springframework.transaction.annotation.Transactional;
 
 /**
  * openim消息记录主Service业务层处理
@@ -17,6 +28,15 @@ import com.fs.im.service.IFsImMsgSendLogService;
 @Service
 public class FsImMsgSendLogServiceImpl extends ServiceImpl<FsImMsgSendLogMapper, FsImMsgSendLog> implements IFsImMsgSendLogService {
 
+    @Autowired
+    private FsImMsgSendDetailMapper fsImMsgSendDetailMapper;
+
+    @Autowired
+    private RedisCache redisCache;
+
+    @Autowired
+    public RedisTemplate redisTemplate;
+
     /**
      * 查询openim消息记录主
      *
@@ -90,4 +110,55 @@ public class FsImMsgSendLogServiceImpl extends ServiceImpl<FsImMsgSendLogMapper,
     {
         return baseMapper.deleteFsImMsgSendLogByLogId(logId);
     }
+
+    @Override
+    public List<FsImSendLogVO> selectFsImSendLogList(Map<String, Object> params) {
+        return baseMapper.selectFsImSendLogList(params);
+    }
+
+    @Override
+    public List<FsImMsgSendLog> selectSendLogListByDetailId(Map<String, Object> params) {
+        return baseMapper.selectSendLogListByDetailId(params);
+    }
+
+    @Override
+    @Transactional
+    public ResponseResult<Boolean> deleteFsImMsgSendLogAndDetail(Long logId) {
+        FsImMsgSendLog fsImMsgSendLog = baseMapper.selectFsImMsgSendLogByLogId(logId);
+        if(fsImMsgSendLog == null){
+            return ResponseResult.fail(400, "当前任务不存在");
+        }
+        if(fsImMsgSendLog.getSendStatus() == 1){
+            return ResponseResult.fail(400, "当前任务已完成,不允许删除");
+        }
+        baseMapper.deleteFsImMsgSendLogByLogId(logId);
+
+        fsImMsgSendDetailMapper.delete(Wrappers.<FsImMsgSendDetail>query().lambda()
+                .eq(FsImMsgSendDetail::getLogId, fsImMsgSendLog.getLogId()));
+
+        // 删除redis缓存
+        if(fsImMsgSendLog.getMsgType() == 1){
+            String redisKey = "openIm:batchSendMsg:sendCourse";
+            Map<String, BatchSendCourseAllDTO> cacheMap = redisCache.getCacheMap(redisKey);
+
+            if(cacheMap != null && !cacheMap.isEmpty()){
+                // 组合key
+                if(fsImMsgSendLog.getPlanSendTime() != null) {
+                    String key = fsImMsgSendLog.getCourseId() + ":" + fsImMsgSendLog.getVideoId() + ":" + fsImMsgSendLog.getPlanSendTime().getTime();
+                    redisTemplate.opsForHash().delete(redisKey, key);
+                }
+            }
+        } else {
+            String redisKey = "openIm:batchSendMsg:urgeCourse";
+            Map<String, BatchSendCourseAllDTO> cacheMap = redisCache.getCacheMap(redisKey);
+            if(cacheMap != null && !cacheMap.isEmpty()){
+                // 组合key
+                if(fsImMsgSendLog.getPlanSendTime() != null) {
+                    String key = fsImMsgSendLog.getCourseId() + ":" + fsImMsgSendLog.getVideoId() + ":" + fsImMsgSendLog.getPlanSendTime().getTime();
+                    redisTemplate.opsForHash().delete(redisKey, key);
+                }
+            }
+        }
+        return ResponseResult.ok();
+    }
 }

+ 115 - 47
fs-service/src/main/java/com/fs/im/service/impl/OpenIMServiceImpl.java

@@ -20,6 +20,7 @@ import com.fs.course.domain.FsUserCompanyUser;
 import com.fs.course.domain.FsUserCourse;
 import com.fs.course.dto.BatchSendCourseAllDTO;
 import com.fs.course.dto.BatchSendCourseDTO;
+import com.fs.course.dto.BatchUrgeCourseDTO;
 import com.fs.course.mapper.FsCourseWatchLogMapper;
 import com.fs.course.mapper.FsUserCompanyUserMapper;
 import com.fs.course.mapper.FsUserCourseMapper;
@@ -44,6 +45,7 @@ import com.fs.im.vo.OpenImMsgCallBackVO;
 import com.fs.im.vo.OpenImResponseDTOTest;
 import com.fs.qw.mapper.QwExternalContactMapper;
 import com.github.pagehelper.util.StringUtil;
+import com.google.common.base.Joiner;
 import lombok.Data;
 import lombok.extern.slf4j.Slf4j;
 import org.jetbrains.annotations.NotNull;
@@ -97,6 +99,8 @@ public class OpenIMServiceImpl implements OpenIMService {
     private FsImMsgSendDetailMapper fsImMsgSendDetailMapper;
     @Autowired
     private FsImMsgSendDetailServiceImpl fsImMsgSendDetailServiceImpl;
+    @Autowired
+    private FsCourseWatchLogMapper fsCourseWatchLogMapper;
 
 
 //    @Value("${openIM.prefix}")
@@ -1100,7 +1104,8 @@ public class OpenIMServiceImpl implements OpenIMService {
             planSendTimeStamp = System.currentTimeMillis();
         }
 
-        OpenImBatchMsgDTO openImBatchMsgDTO = makeOpenImBatchMsgDTO(batchSendCourseDTO, fsUserCourse, objectMapper, userIds, planSendTimeStamp, "发课");
+        String courseUrl = fsUserCourse != null ? fsUserCourse.getImgUrl() : null;
+        OpenImBatchMsgDTO openImBatchMsgDTO = makeOpenImBatchMsgDTO(batchSendCourseDTO, courseUrl, objectMapper, userIds, planSendTimeStamp, "发课");
 
         int sendType;
         if(batchSendCourseDTO.getSendType() == 1 && batchSendCourseDTO.getSendTime() != null && batchSendCourseDTO.getSendTime().compareTo(new Date()) > 0) {
@@ -1110,22 +1115,21 @@ public class OpenIMServiceImpl implements OpenIMService {
         }
 
         // 创建消息发送记录
-        List<FsImMsgSendDetail> imMsgSendDetailList = createImMsgSendLog("发课",batchSendCourseDTO, planSendTimeStamp, sendType, userIds);
+        String sendUnionId = UUID.randomUUID().toString();
+        List<FsImMsgSendDetail> imMsgSendDetailList = createImMsgSendLog("发课",batchSendCourseDTO, planSendTimeStamp, sendType, userIds, sendUnionId);
 
         OpenImResponseDTO openImResponseDTO = new OpenImResponseDTO();
         if(sendType == 1) {
             // 定时发送
-//            for (String userId : userIds) {
-                // 缓存定时发课消息
-                String redisKey = "openIm:batchSendMsg:sendCourse";
-                Map<String, Object> redisMap = new HashMap<>();
-                BatchSendCourseAllDTO batchSendCourseAllDTO = new BatchSendCourseAllDTO();
-                batchSendCourseAllDTO.setBatchSendCourseDTO(batchSendCourseDTO).setOpenImBatchMsgDTO(openImBatchMsgDTO).setProject(project)
-                        .setImMsgSendDetailList(imMsgSendDetailList);
-                redisMap.put(batchSendCourseDTO.getCourseId()+":"+batchSendCourseDTO.getVideoId()+":"+batchSendCourseDTO.getSendTime().getTime()
-                        , batchSendCourseAllDTO);
-                redisCache.setCacheMap(redisKey, redisMap);
-//            }
+            // 缓存定时发课消息
+            String redisKey = "openIm:batchSendMsg:sendCourse";
+            Map<String, Object> redisMap = new HashMap<>();
+            BatchSendCourseAllDTO batchSendCourseAllDTO = new BatchSendCourseAllDTO();
+            batchSendCourseAllDTO.setBatchSendCourseDTO(batchSendCourseDTO).setOpenImBatchMsgDTO(openImBatchMsgDTO).setProject(project)
+                    .setImMsgSendDetailList(imMsgSendDetailList);
+            redisMap.put(batchSendCourseDTO.getCourseId()+":"+batchSendCourseDTO.getVideoId()+":"+batchSendCourseDTO.getSendTime().getTime()
+                    , batchSendCourseAllDTO);
+            redisCache.setCacheMap(redisKey, redisMap);
             openImResponseDTO.setErrCode(0);
             openImResponseDTO.setErrMsg("计划发送创建成功,待消息发送");
         } else {
@@ -1137,37 +1141,41 @@ public class OpenIMServiceImpl implements OpenIMService {
         //是否催课
         if(batchSendCourseDTO.getIsUrgeCourse()){
             // 组装催课消息数据
-            OpenImBatchMsgDTO openImBatchUrgeCourse = makeOpenImBatchMsgDTO(batchSendCourseDTO, fsUserCourse, objectMapper, userIds, planSendTimeStamp, "催课");
+            OpenImBatchMsgDTO openImBatchUrgeCourse = makeOpenImBatchMsgDTO(batchSendCourseDTO, courseUrl, objectMapper, userIds, planSendTimeStamp, "催课");
 
             //缓存定时催课消息
-            List<FsImMsgSendDetail> imMsgSendDetailUrgeList = createImMsgSendLog("催课", batchSendCourseDTO, planSendTimeStamp, sendType, userIds);
-//            for (String userId : userIds) {
-                String redisKey = "openIm:batchSendMsg:urgeCourse";
-                Map<String, Object> redisMap = new HashMap<>();
-                BatchSendCourseAllDTO batchSendCourseAllDTO = new BatchSendCourseAllDTO();
-                batchSendCourseAllDTO.setOpenImBatchMsgDTO(openImBatchUrgeCourse)
-                        .setImMsgSendDetailList(imMsgSendDetailUrgeList);
-                redisMap.put(batchSendCourseDTO.getCourseId()+":"+batchSendCourseDTO.getVideoId()+":"+batchSendCourseDTO.getUrgeTime().getTime()
-                        , batchSendCourseAllDTO);
-                redisCache.setCacheMap(redisKey, redisMap);
-//            }
+            int urgSendType;
+            if(batchSendCourseDTO.getUrgeTime() != null && batchSendCourseDTO.getUrgeTime().compareTo(new Date()) > 0) {
+                urgSendType = 1; //定时
+            } else {
+                urgSendType = 2; //实时
+            }
+            List<FsImMsgSendDetail> imMsgSendDetailUrgeList = createImMsgSendLog("催课", batchSendCourseDTO, planSendTimeStamp, urgSendType, userIds, sendUnionId);
+            String redisKey = "openIm:batchSendMsg:urgeCourse";
+            Map<String, Object> redisMap = new HashMap<>();
+            BatchSendCourseAllDTO batchSendCourseAllDTO = new BatchSendCourseAllDTO();
+            batchSendCourseAllDTO.setOpenImBatchMsgDTO(openImBatchUrgeCourse)
+                    .setImMsgSendDetailList(imMsgSendDetailUrgeList);
+            redisMap.put(batchSendCourseDTO.getCourseId()+":"+batchSendCourseDTO.getVideoId()+":"+batchSendCourseDTO.getUrgeTime().getTime()
+                    , batchSendCourseAllDTO);
+            redisCache.setCacheMap(redisKey, redisMap);
         }
         return openImResponseDTO;
     }
 
-    private OpenImBatchMsgDTO makeOpenImBatchMsgDTO(BatchSendCourseDTO batchSendCourseDTO, FsUserCourse fsUserCourse, ObjectMapper objectMapper, List<String> userIds, long planSendTimeStamp, String logType) throws JsonProcessingException {
+    private OpenImBatchMsgDTO makeOpenImBatchMsgDTO(BatchSendCourseDTO batchSendCourseDTO, String courseUrl, ObjectMapper objectMapper, List<String> userIds, long planSendTimeStamp, String logType) throws JsonProcessingException {
         PayloadDTO.Extension extension = new PayloadDTO.Extension();
         OpenImBatchMsgDTO openImBatchMsgDTO = new OpenImBatchMsgDTO();
         if("发课".equals(logType)){
             extension.setTitle(batchSendCourseDTO.getTitle());
             extension.setAppRealLink(batchSendCourseDTO.getUrl());
-            extension.setCourseUrl(fsUserCourse != null ? fsUserCourse.getImgUrl() : null);
+            extension.setCourseUrl(courseUrl);
             extension.setSendTime(new Date(planSendTimeStamp));
             openImBatchMsgDTO.setSendTime(planSendTimeStamp);
         } else {
             extension.setTitle(batchSendCourseDTO.getUrgeContent());
-            extension.setSendTime(batchSendCourseDTO.getUrgeTime());
-            openImBatchMsgDTO.setSendTime(batchSendCourseDTO.getUrgeTime().getTime());
+            extension.setSendTime(batchSendCourseDTO.getUrgeTime() != null ? batchSendCourseDTO.getUrgeTime() : new Date());
+            openImBatchMsgDTO.setSendTime(batchSendCourseDTO.getUrgeTime() != null ? batchSendCourseDTO.getUrgeTime().getTime() : System.currentTimeMillis());
         }
 
         PayloadDTO payload = new PayloadDTO();
@@ -1235,6 +1243,60 @@ public class OpenIMServiceImpl implements OpenIMService {
         return openImResponseDTO;
     }
 
+    @Override
+    @Transactional
+    public OpenImResponseDTO batchUrgeCourse(BatchUrgeCourseDTO batchUrgeCourseDTO, FsImMsgSendLog fsImMsgSendLog, String url) throws JsonProcessingException {
+        ObjectMapper objectMapper = new ObjectMapper();
+        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); // 忽略null字段
+
+        FsUserCourse fsUserCourse = fsUserCourseMapper.selectFsUserCourseByCourseId(fsImMsgSendLog.getCourseId());
+        String courseUrl = fsUserCourse != null ? fsUserCourse.getImgUrl() : null;
+
+        FsImMsgSendDetail fsImMsgSendDetail = new FsImMsgSendDetail();
+        fsImMsgSendDetail.setLogId(fsImMsgSendLog.getLogId());
+        List<FsImMsgSendDetail> fsImMsgSendDetails = fsImMsgSendDetailMapper.selectFsImMsgSendDetailList(fsImMsgSendDetail);
+        List<Long> userIdList = fsImMsgSendDetails.stream().map(FsImMsgSendDetail::getUserId).collect(Collectors.toList());
+
+        // 过滤掉已完课的用户,已完课的用户不发送;
+        // 如果全部完课,则提示
+        List<FsCourseWatchLog> watchCourseByVideoList = fsCourseWatchLogMapper.getWatchCourseByVideoId(fsImMsgSendLog.getVideoId(), userIdList);
+        List<String> userIds = watchCourseByVideoList.stream().filter(v -> v.getLogType() != 2).map(v -> "U" + v.getUserId()).collect(Collectors.toList());
+        OpenImResponseDTO openImResponseDTO = new OpenImResponseDTO();
+        if(userIds.isEmpty()){
+            openImResponseDTO.setErrCode(400);
+            openImResponseDTO.setErrMsg("用户都已完课,不需要催课");
+            return openImResponseDTO;
+        }
+
+        // 是否需要发课
+        if(batchUrgeCourseDTO.getIsSendCourse()){
+             // 组装发课数据
+             BatchSendCourseDTO batchSendCourseDTO = new BatchSendCourseDTO();
+             BeanUtils.copyProperties(fsImMsgSendLog, batchSendCourseDTO);
+             batchSendCourseDTO.setTitle(batchUrgeCourseDTO.getTitle());
+             batchSendCourseDTO.setUrl(url);
+             batchSendCourseDTO.setId(fsImMsgSendLog.getPeriodDaysId());
+             OpenImBatchMsgDTO openImBatchMsgDTO = makeOpenImBatchMsgDTO(batchSendCourseDTO, courseUrl, objectMapper, userIds, System.currentTimeMillis(), "发课");
+             log.info("一键催课-发课-批量发送课程消息: \n{}", JSON.toJSONString(openImBatchMsgDTO));
+             this.openIMBatchSendMsg(openImBatchMsgDTO);
+//            List<FsImMsgSendDetail> imMsgSendDetailList = createImMsgSendLog("发课",batchSendCourseDTO, System.currentTimeMillis(), 2, userIds, sendUnionId);
+        }
+
+         // 催课
+        // 组装催课消息
+        BatchSendCourseDTO batchSendCourseDTO = new BatchSendCourseDTO();
+        BeanUtils.copyProperties(fsImMsgSendLog, batchSendCourseDTO);
+        batchSendCourseDTO.setUrgeContent(batchUrgeCourseDTO.getUrgeContent());
+        batchSendCourseDTO.setId(fsImMsgSendLog.getPeriodDaysId());
+        OpenImBatchMsgDTO openImBatchMsgDTO = makeOpenImBatchMsgDTO(batchSendCourseDTO, courseUrl, objectMapper, userIds, System.currentTimeMillis(), "催课");
+        log.info("一键催课-催课-批量催课消息: \n{}", JSON.toJSONString(openImBatchMsgDTO));
+        openImResponseDTO = this.openIMBatchSendMsg(openImBatchMsgDTO);
+
+        List<FsImMsgSendDetail> imMsgSendDetailList = createImMsgSendLog("催课", batchSendCourseDTO, System.currentTimeMillis(), 2, userIds, fsImMsgSendLog.getSendUnionId());
+        this.updateImMsgSendLog(openImBatchMsgDTO, imMsgSendDetailList, openImResponseDTO, "催课");
+        return openImResponseDTO;
+    }
+
     private List<String> getRecvIds(BatchSendCourseDTO batchSendCourseDTO) {
         List<String> userIds;
         if(!batchSendCourseDTO.getUserIds().isEmpty()) {
@@ -1257,30 +1319,37 @@ public class OpenIMServiceImpl implements OpenIMService {
     /**
      * 创建消息发送记录
      */
-    private List<FsImMsgSendDetail> createImMsgSendLog(String logType, BatchSendCourseDTO batchSendCourseDTO, long planSendTimeStamp, int sendType, List<String> userIds) {
+    private List<FsImMsgSendDetail> createImMsgSendLog(String logType, BatchSendCourseDTO batchSendCourseDTO, long planSendTimeStamp, int sendType, List<String> userIds, String sendUnionId) {
         // 记录发送消息记录主表
         FsImMsgSendLog fsImMsgSendLog = new FsImMsgSendLog();
         BeanUtils.copyProperties(batchSendCourseDTO, fsImMsgSendLog);
         fsImMsgSendLog.setCreateBy(batchSendCourseDTO.getCompanyUserId().toString());
         fsImMsgSendLog.setCreateTime(new Date());
+        fsImMsgSendLog.setSendUnionId(sendUnionId)
+                .setProjectId(batchSendCourseDTO
+                .getProjectId())
+                .setPeriodDaysId(batchSendCourseDTO.getId());
+        if(batchSendCourseDTO.getTagIds() != null && !batchSendCourseDTO.getTagIds().isEmpty()){
+            String tagIds = Joiner.on(",").join(batchSendCourseDTO.getTagIds());
+            fsImMsgSendLog.setTagIds(tagIds);
+        }
+        if(batchSendCourseDTO.getTagNames() != null && !batchSendCourseDTO.getTagNames().isEmpty()){
+            String tagNames = Joiner.on(",").join(batchSendCourseDTO.getTagNames());
+            fsImMsgSendLog.setTagNames(tagNames);
+        }
+        if(sendType == 1){
+            fsImMsgSendLog.setSendStatus(2);
+        } else {
+            fsImMsgSendLog.setSendStatus(1);
+        }
         if("发课".equals(logType)){
             // 发课
             fsImMsgSendLog.setPlanSendTime(new Date(planSendTimeStamp));
             fsImMsgSendLog.setSendTitle(batchSendCourseDTO.getTitle()).setMsgType(1);
-            if(sendType == 1){
-                fsImMsgSendLog.setSendStatus(2);
-            } else {
-                fsImMsgSendLog.setSendStatus(1);
-            }
         } else {
             //催课
             fsImMsgSendLog.setPlanSendTime(batchSendCourseDTO.getUrgeTime());
-            fsImMsgSendLog.setSendTitle(batchSendCourseDTO.getUrgeContent()).setMsgType(2).setIsUrgeCourse(1);
-            if(batchSendCourseDTO.getUrgeTime() != null && batchSendCourseDTO.getUrgeTime().compareTo(new Date()) > 0){
-                fsImMsgSendLog.setSendStatus(2);
-            } else{
-                fsImMsgSendLog.setSendStatus(1);
-            }
+            fsImMsgSendLog.setSendTitle(batchSendCourseDTO.getUrgeContent()).setMsgType(2);
         }
         fsImMsgSendLogMapper.insert(fsImMsgSendLog);
 
@@ -1293,16 +1362,15 @@ public class OpenIMServiceImpl implements OpenIMService {
                     .setCreateTime(new Date());
             Long id = Long.parseLong(userId.replaceFirst("^" + "U", ""));
             fsImMsgSendDetail.setUserId(id);
+            if (sendType == 1) {
+                fsImMsgSendDetail.setSendStatus(2);
+            } else {
+                fsImMsgSendDetail.setSendStatus(1);
+            }
             if("发课".equals(logType)) {
                 fsImMsgSendDetail.setPlanSendTime(new Date(planSendTimeStamp));
-                if (sendType == 1) {
-                    fsImMsgSendDetail.setSendStatus(2);
-                } else {
-                    fsImMsgSendDetail.setSendStatus(1);
-                }
             } else {
                 fsImMsgSendDetail.setPlanSendTime(batchSendCourseDTO.getUrgeTime());
-                fsImMsgSendDetail.setSendStatus(2);
             }
             list.add(fsImMsgSendDetail);
         }

+ 66 - 0
fs-service/src/main/java/com/fs/im/vo/FsImMsgSendLogVO.java

@@ -0,0 +1,66 @@
+package com.fs.im.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * openim消息记录主对象VO
+ *
+ * @author caolq
+ */
+@Data
+@Accessors(chain = true)
+public class FsImMsgSendLogVO implements Serializable {
+
+    @ApiModelProperty(value = "发送记录id")
+    private Long logId;
+
+    @ApiModelProperty(value = "课程id")
+    private Long courseId;
+
+    @ApiModelProperty(value = "课程名称")
+    private String courseName;
+
+    @ApiModelProperty(value = "视频id")
+    private Long videoId;
+
+    @ApiModelProperty(value = "视频标题")
+    private String videoName;
+
+    @ApiModelProperty(value = "发课内容")
+    private String sendTitle;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @ApiModelProperty(value = "发送时间")
+    private Date planSendTime;
+
+    @ApiModelProperty(value = "发送类型,1-定时;2-实时")
+    private Integer sendType;
+
+    @ApiModelProperty(value = "发送状态,1-已发送;2-待发送")
+    private Integer sendStatus;
+
+    @ApiModelProperty(value = "是否催课,1-是;0-否")
+    private Integer isUrgeCourse;
+
+    @ApiModelProperty(value = "催课内容")
+    private String urgeContent;
+
+    @ApiModelProperty(value = "消息类型,1-发课;2-催课")
+    private Integer msgType;
+
+    @ApiModelProperty(value = "关联id")
+    private String sendUnionId;
+
+    @ApiModelProperty(value = "关联标签id")
+    private String tagIds;
+
+    @ApiModelProperty(value = "关联标签名称")
+    private String tagNames;
+
+}

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

@@ -59,7 +59,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         l.log_type,SEC_TO_TIME(l.duration) as duration,c.company_name,l.camp_period_time,l.finish_time,
         cu.nick_name as company_user_name ,l.send_type,l.create_time,l.update_time,l.last_heartbeat_time,
         qu.qw_user_name,qec.name as external_user_name,c.company_id,u.avatar as fsAvatar,u.nick_name as fsNickName,qec.create_time as qec_create_time,
-        u.is_vip isVip
+        u.is_vip isVip, l.project, l.im_msg_send_detail_id
          from fs_course_watch_log l
          left join fs_user_course_video v on v.video_id = l.video_id
          left join fs_user_course uc on uc.course_id = l.course_id
@@ -138,6 +138,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                     #{sopId}
                 </foreach>
             </if>
+            <if test ='maps.project !=null'>
+                and l.project = #{maps.project}
+            </if>
         </where>
          order by l.finish_time desc,l.update_time desc,l.create_time desc
     </select>
@@ -344,7 +347,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             )
         </foreach>
         ON DUPLICATE KEY UPDATE
-        update_time = VALUES(update_time)
+        update_time = NOW(),
+        im_msg_send_detail_id = VALUES(im_msg_send_detail_id)
     </insert>
 
 
@@ -830,4 +834,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         order by l.finish_time desc,l.update_time desc,l.create_time desc
     </select>
 
+    <select id="getWatchCourseByVideoId" resultType="com.fs.course.domain.FsCourseWatchLog">
+        SELECT
+            *
+        FROM
+            fs_course_watch_log
+        WHERE
+            send_type = 1
+          AND video_id = #{videoId}
+          AND user_id in
+            <foreach item="userId" index="index" collection="userIds" open="(" separator="," close=")">
+                #{userId}
+            </foreach>
+        ORDER BY
+            log_id DESC
+    </select>
 </mapper>

+ 84 - 7
fs-service/src/main/resources/mapper/im/FsImMsgSendLogMapper.xml

@@ -3,7 +3,7 @@
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.fs.im.mapper.FsImMsgSendLogMapper">
-    
+
     <resultMap type="FsImMsgSendLog" id="FsImMsgSendLogResult">
         <result property="logId"    column="log_id"    />
         <result property="companyUserId"    column="company_user_id"    />
@@ -23,15 +23,21 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="createTime"    column="create_time"    />
         <result property="createBy"    column="create_by"    />
         <result property="updateTime"    column="update_time"    />
+        <result property="sendUnionId"    column="send_union_id"    />
+        <result property="tagIds"    column="tag_ids"    />
+        <result property="tagNames"    column="tag_names"    />
+        <result property="projectId"    column="project_id"    />
+        <result property="periodDaysId"    column="period_days_id"    />
     </resultMap>
 
     <sql id="selectFsImMsgSendLogVo">
-        select log_id, company_user_id, company_id, course_id, course_name, video_id, video_name, period_id, send_title, plan_send_time, send_type, send_mode, send_status, is_urge_course, msg_type, create_time, create_by, update_time from fs_im_msg_send_log
+        select log_id, company_user_id, company_id, course_id, course_name, video_id, video_name, period_id, send_title, plan_send_time, send_type, send_mode, send_status, is_urge_course, msg_type, create_time, create_by, update_time, send_union_id, tag_ids,
+               tag_names, project_id, period_days_id from fs_im_msg_send_log
     </sql>
 
     <select id="selectFsImMsgSendLogList" parameterType="FsImMsgSendLog" resultMap="FsImMsgSendLogResult">
         <include refid="selectFsImMsgSendLogVo"/>
-        <where>  
+        <where>
             <if test="companyUserId != null "> and company_user_id = #{companyUserId}</if>
             <if test="companyId != null "> and company_id = #{companyId}</if>
             <if test="courseId != null "> and course_id = #{courseId}</if>
@@ -46,14 +52,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="sendStatus != null "> and send_status = #{sendStatus}</if>
             <if test="isUrgeCourse != null "> and is_urge_course = #{isUrgeCourse}</if>
             <if test="msgType != null "> and msg_type = #{msgType}</if>
+            <if test="sendUnionId != null and sendUnionId != ''"> and send_union_id = #{sendUnionId}</if>
+            <if test="tagIds != null and tagIds != ''"> and tag_ids = #{tagIds}</if>
+            <if test="tagNames != null and tagNames != ''"> and tag_names = #{tagNames}</if>
+            <if test="projectId != null and projectId != ''"> and project_id = #{projectId}</if>
+            <if test="periodDaysId != null and periodDaysId != ''"> and period_days_id = #{periodDaysId}</if>
         </where>
     </select>
-    
+
     <select id="selectFsImMsgSendLogByLogId" parameterType="Long" resultMap="FsImMsgSendLogResult">
         <include refid="selectFsImMsgSendLogVo"/>
         where log_id = #{logId}
     </select>
-        
+
     <insert id="insertFsImMsgSendLog" parameterType="FsImMsgSendLog" useGeneratedKeys="true" keyProperty="logId">
         insert into fs_im_msg_send_log
         <trim prefix="(" suffix=")" suffixOverrides=",">
@@ -74,6 +85,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="createTime != null">create_time,</if>
             <if test="createBy != null">create_by,</if>
             <if test="updateTime != null">update_time,</if>
+            <if test="sendUnionId != null">send_union_id,</if>
+            <if test="tagIds != null">tag_ids,</if>
+            <if test="tagNames != null">tag_names,</if>
+            <if test="projectId != null">project_id,</if>
+            <if test="periodDaysId != null">period_days_id,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="companyUserId != null">#{companyUserId},</if>
@@ -93,6 +109,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="createTime != null">#{createTime},</if>
             <if test="createBy != null">#{createBy},</if>
             <if test="updateTime != null">#{updateTime},</if>
+            <if test="sendUnionId != null">#{sendUnionId},</if>
+            <if test="tagIds != null">#{tagIds},</if>
+            <if test="tagNames != null">#{tagNames},</if>
+            <if test="projectId != null">#{projectId},</if>
+            <if test="periodDaysId != null">#{periodDaysId},</if>
          </trim>
     </insert>
 
@@ -116,6 +137,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="createTime != null">create_time = #{createTime},</if>
             <if test="createBy != null">create_by = #{createBy},</if>
             <if test="updateTime != null">update_time = #{updateTime},</if>
+            <if test="sendUnionId != null">send_union_id = #{sendUnionId},</if>
+            <if test="tagIds != null">tag_ids = #{tagIds},</if>
+            <if test="tagNames != null">tag_names = #{tagNames},</if>
+            <if test="projectId != null">project_id = #{projectId},</if>
+            <if test="periodDaysId != null">period_days_id = #{periodDaysId},</if>
         </trim>
         where log_id = #{logId}
     </update>
@@ -125,9 +151,60 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </delete>
 
     <delete id="deleteFsImMsgSendLogByLogIds" parameterType="String">
-        delete from fs_im_msg_send_log where log_id in 
+        delete from fs_im_msg_send_log where log_id in
         <foreach item="logId" collection="array" open="(" separator="," close=")">
             #{logId}
         </foreach>
     </delete>
-</mapper>
+
+
+    <select id="selectFsImSendLogList" resultType="com.fs.course.vo.newfs.FsImSendLogVO">
+        SELECT
+            log.log_id,
+            log.company_user_id,
+            log.company_id,
+            log.course_id,
+            log.course_name,
+            log.video_id,
+            log.video_name,
+            log.send_title,
+            log.plan_send_time,
+            log.send_type,
+            log.send_mode,
+            log.send_status,
+            log.is_urge_course,
+            log.msg_type,
+            log.create_time,
+            log.send_union_id,
+            log.tag_ids,
+            log.tag_names,
+            log2.send_title as urgeSendTitle,
+            GROUP_CONCAT( DISTINCT detail.user_id ) AS userIds,
+            GROUP_CONCAT( DISTINCT fs_user.nick_name ) AS nickNames
+        FROM
+            fs_im_msg_send_log log
+                LEFT JOIN fs_im_msg_send_detail detail ON detail.log_id = log.log_id
+                LEFT JOIN fs_user ON fs_user.user_id = detail.user_id
+                LEFT JOIN fs_im_msg_send_log log2 on log2.send_union_id = log.send_union_id and log2.msg_type = 2
+        WHERE
+            log.msg_type = 1
+            AND log.company_user_id = #{params.companyUserId}
+            AND log.company_id = #{params.companyId}
+        GROUP BY
+            log.log_id
+    </select>
+
+    <select id="selectSendLogListByDetailId" resultType="FsImMsgSendLog">
+        SELECT DISTINCT log.*
+        FROM
+            fs_im_msg_send_log log
+                LEFT JOIN fs_im_msg_send_detail detail ON detail.log_id = log.log_id
+        WHERE
+            detail.log_detail_id IN
+            <foreach item="logDetailId" collection="params.logDetailIds" open="(" separator="," close=")">
+                #{logDetailId}
+            </foreach>
+    </select>
+
+
+</mapper>