Browse Source

掉线提醒,和视频号

吴树波 3 weeks ago
parent
commit
174892f130
30 changed files with 745 additions and 273 deletions
  1. 6 0
      fs-company/src/main/java/com/fs/company/controller/qw/QwUserController.java
  2. 37 1
      fs-ipad-task/src/main/java/com/fs/app/service/IpadSendServer.java
  3. 24 2
      fs-ipad-task/src/main/java/com/fs/app/task/SendMsg.java
  4. 1 1
      fs-qw-task/src/main/java/com/fs/FsQwTaskApplication.java
  5. 9 7
      fs-qw-task/src/main/java/com/fs/app/controller/CommonController.java
  6. 1 228
      fs-service/src/main/java/com/fs/company/domain/Company.java
  7. 3 0
      fs-service/src/main/java/com/fs/company/service/impl/CompanyMiniappServiceImpl.java
  8. 63 9
      fs-service/src/main/java/com/fs/ipad/IpadSendUtils.java
  9. 12 0
      fs-service/src/main/java/com/fs/ipad/param/WxGetSessionRoomListParam.java
  10. 11 0
      fs-service/src/main/java/com/fs/ipad/param/WxRoomUserListParam.java
  11. 18 0
      fs-service/src/main/java/com/fs/ipad/param/WxSendAtMsgParam.java
  12. 23 0
      fs-service/src/main/java/com/fs/ipad/vo/WxGetSessionRoomListVo.java
  13. 44 0
      fs-service/src/main/java/com/fs/ipad/vo/WxRoomUserListVo.java
  14. 19 0
      fs-service/src/main/java/com/fs/ipad/vo/WxSendAtMsgVo.java
  15. 22 0
      fs-service/src/main/java/com/fs/ipad/vo/WxVideoVo.java
  16. 1 0
      fs-service/src/main/java/com/fs/qw/domain/QwUser.java
  17. 7 0
      fs-service/src/main/java/com/fs/qw/domain/QwUserVideo.java
  18. 2 0
      fs-service/src/main/java/com/fs/qw/mapper/QwUserMapper.java
  19. 4 0
      fs-service/src/main/java/com/fs/qw/service/IQwUserService.java
  20. 71 0
      fs-service/src/main/java/com/fs/qw/service/impl/QwUserServiceImpl.java
  21. 1 0
      fs-service/src/main/java/com/fs/qw/vo/QwSopCourseFinishTempSetting.java
  22. 38 0
      fs-service/src/main/java/com/fs/wxwork/dto/WxwSendVideoNumberMsgDTO.java
  23. 45 16
      fs-service/src/main/java/com/fs/wxwork/service/WxWorkServiceNew.java
  24. 90 0
      fs-service/src/main/resources/application-config-druid-drk-test.yml
  25. 4 2
      fs-service/src/main/resources/application-config-druid-yjb.yml
  26. 143 0
      fs-service/src/main/resources/application-druid-drk-test.yml
  27. 1 1
      fs-service/src/main/resources/application-druid-yjb.yml
  28. 4 0
      fs-service/src/main/resources/mapper/company/CompanyMapper.xml
  29. 9 1
      fs-service/src/main/resources/mapper/qw/QwUserMapper.xml
  30. 32 5
      fs-service/src/main/resources/mapper/qw/QwUserVideoMapper.xml

+ 6 - 0
fs-company/src/main/java/com/fs/company/controller/qw/QwUserController.java

@@ -837,4 +837,10 @@ public class QwUserController extends BaseController
         return qwUserService.updateSendType(vo);
     }
 
+    @GetMapping("/changeVideoStatus")
+    public R changeVideoStatus(Long id) {
+        qwUserService.changeVideoStatus(id);
+        return R.ok();
+    }
+
 }

+ 37 - 1
fs-ipad-task/src/main/java/com/fs/app/service/IpadSendServer.java

@@ -9,8 +9,12 @@ import com.fs.course.service.IFsCourseWatchLogService;
 import com.fs.ipad.IpadSendUtils;
 import com.fs.ipad.vo.*;
 import com.fs.qw.domain.QwUser;
+import com.fs.qw.domain.QwUserVideo;
 import com.fs.qw.mapper.QwExternalContactMapper;
 import com.fs.qw.mapper.QwUserMapper;
+import com.fs.qw.service.IQwUserService;
+import com.fs.qw.service.IQwUserVideoService;
+import com.fs.qw.service.impl.QwUserServiceImpl;
 import com.fs.qw.vo.QwSopCourseFinishTempSetting;
 import com.fs.qwApi.param.QwExternalContactHParam;
 import com.fs.sop.domain.QwSopLogs;
@@ -30,10 +34,12 @@ import java.util.Map;
 public class IpadSendServer {
 
     private final QwUserMapper qwUserMapper;
+    private final IQwUserService qwUserService;
     private final IpadSendUtils ipadSendUtils;
     private final IQwSopLogsService qwSopLogsService;
     private final QwExternalContactMapper qwExternalContactMapper;
     private final IFsCourseWatchLogService watchLogService;
+    private final IQwUserVideoService qwUserVideoService;
     private final RedisCache redisCache;
 
     private void sendMiniProgram(BaseVo vo, QwSopCourseFinishTempSetting.Setting content, Map<String, CourseMaConfig> miniMap) {
@@ -122,6 +128,28 @@ public class IpadSendServer {
         }
     }
 
+    public void sendWxVideo(BaseVo vo, QwSopCourseFinishTempSetting.Setting content) {
+        QwUserVideo qwUserVideo = qwUserVideoService.selectQwUserVideoById(content.getVideoId());
+        WxVideoVo videoVo = WxVideoVo.builder()
+                .coverUrl(qwUserVideo.getCoverUrl())
+                .thumbUrl(qwUserVideo.getThumbUrl())
+                .avatar(qwUserVideo.getAvatar())
+                .nickname(qwUserVideo.getNickName())
+                .desc(qwUserVideo.getDesc())
+                .url(qwUserVideo.getUrl())
+                .extras(qwUserVideo.getExtras())
+                .objectId(qwUserVideo.getObjectId())
+                .objectNonceId(qwUserVideo.getObjectNonceId())
+                .build();
+        videoVo.setBase(vo);
+        WxWorkResponseDTO<WxwSendVideoNumberRespDTO> resp = ipadSendUtils.sendWxVideo(videoVo);
+        if (resp.getErrcode() != 0) {
+            log.debug("ID:{}-ipad接口请求返回异常:{}", vo.getId(), resp.getErrmsg());
+            content.setSendStatus(2);
+            content.setSendRemarks("发送失败:" + resp.getErrmsg());
+        }
+    }
+
     public void sendVoice(BaseVo vo, QwSopCourseFinishTempSetting.Setting content) {
         if (StringUtils.isEmpty(content.getVoiceUrl()) || StringUtils.isEmpty(content.getVoiceDuration())) {
             log.debug("语音未生成无法发送,转文字发送:{}", vo);
@@ -172,6 +200,7 @@ public class IpadSendServer {
                 updateQwUser.setRemark("AI未初始化");
                 updateQwUser.setIpadStatus(0);
                 qwUserMapper.updateById(updateQwUser);
+                qwUserService.atMsg(qwUser, "掉线提醒(未初始化)");
                 return false;
             }
             if (login.getLoginType() == 1) {
@@ -179,6 +208,7 @@ public class IpadSendServer {
                 updateQwUser.setRemark("未登录");
                 updateQwUser.setIpadStatus(0);
                 qwUserMapper.updateById(updateQwUser);
+                qwUserService.atMsg(qwUser, "掉线提醒(未登录)");
                 return false;
             }
             if (!login.getUser_info().isLogin()) {
@@ -186,15 +216,17 @@ public class IpadSendServer {
                 updateQwUser.setRemark("登录状态异常");
                 updateQwUser.setIpadStatus(0);
                 qwUserMapper.updateById(updateQwUser);
+                qwUserService.atMsg(qwUser, "掉线提醒(登录异常)");
                 return false;
             }
             parentVo.setCorpId(login.getUser_info().getObject().getCorp_id());
             log.debug("QwUserID:{}, AI主机信息:{}", qwUser.getId(), login);
         } catch (Exception e) {
             updateQwUser.setId(qwUser.getId());
-            updateQwUser.setRemark("登录状态异常");
+            updateQwUser.setRemark("登录状态异常" + e.getMessage());
             updateQwUser.setIpadStatus(0);
             qwUserMapper.updateById(updateQwUser);
+            qwUserService.atMsg(qwUser, "掉线提醒(登录信息异常,请联系管理员:"+e.getMessage()+")");
             return false;
         }
 
@@ -318,6 +350,10 @@ public class IpadSendServer {
                     // 语音
                     sendVoice(vo, content);
                     break;
+                case "8":
+                    // 语音
+                    sendWxVideo(vo, content);
+                    break;
                 default:
                     // 未知类型,记录警告
                     log.error("SOP_LOG_ID:{}错误的发送类型: {}", qwSopLogs.getId(), content.getContentType());

+ 24 - 2
fs-ipad-task/src/main/java/com/fs/app/task/SendMsg.java

@@ -15,6 +15,7 @@ import com.fs.qw.mapper.QwIpadServerMapper;
 import com.fs.qw.mapper.QwUserMapper;
 import com.fs.qw.service.impl.AsyncSopTestService;
 import com.fs.qw.vo.QwSopCourseFinishTempSetting;
+import com.fs.qw.vo.QwSopTempSetting;
 import com.fs.sop.domain.QwSopLogs;
 import com.fs.sop.mapper.QwSopLogsMapper;
 import com.fs.sop.service.IQwSopLogsService;
@@ -36,6 +37,7 @@ import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 @Component
 @Slf4j
@@ -100,6 +102,7 @@ public class SendMsg {
             log.error("corpId为空不执行");
             return;
         }
+        // 看课配置文件获取
         SysConfig courseConfig = sysConfigMapper.selectConfigByConfigKey("course.config");
         CourseConfig config = JSON.parseObject(courseConfig.getConfigValue(), CourseConfig.class);
         // 消息发送延迟
@@ -113,12 +116,17 @@ public class SendMsg {
             delayStart = config.getDelayStart();
             delayEnd = config.getDelayEnd();
         }
+        // 小程序配置获取
         Map<String, CourseMaConfig> miniMap = getMiniMap();
+        // 获取 pad 发送的企微
         getQwUserList().forEach(e -> {
+            // 如果没有值就执行后面的方法 并且入值
             qwMap.computeIfAbsent(e.getId(), k -> {
+                // 线程启动
                 CompletableFuture.runAsync(() -> {
                     try {
                         log.info("开始任务:{}", e.getQwUserName());
+                        // 开始任务
                         processUser(e, delayStart, delayEnd, miniMap);
                     } catch (Exception exception) {
                         log.error("发送错误:", exception);
@@ -133,21 +141,32 @@ public class SendMsg {
         });
     }
 
+    /**
+     * 发送任务执行
+     * @param qwUser     发送企微
+     * @param delayStart 随机延迟 最小值
+     * @param delayEnd   随机延迟 最大值
+     * @param miniMap    小程序配置
+     */
     private void processUser(QwUser qwUser, int delayStart, int delayEnd, Map<String, CourseMaConfig> miniMap) {
         long start1 = System.currentTimeMillis();
+        // 获取当前企微待发送记录
         List<QwSopLogs> qwSopLogList = qwSopLogsMapper.selectByQwUserId(qwUser.getId());
         if (qwSopLogList.isEmpty()) {
             return;
         }
+        // 获取企微用户
         QwUser user = qwUserMapper.selectById(qwUser.getId());
         BaseVo parentVo = new BaseVo();
         parentVo.setCorpCode(qwUser.getCorpId());
         long end1 = System.currentTimeMillis();
+        // 判断这个企微是否需要发送
         if (!sendServer.isSend(user, parentVo)) {
             return;
         }
         log.info("销售:{}, 消息:{}, 耗时: {}, 时间:{}", user.getQwUserName(), qwSopLogList.size(), end1 - start1, qwMap.get(qwUser.getId()));
         long start3 = System.currentTimeMillis();
+        // 循环代发送消息
         for (QwSopLogs qwSopLogs : qwSopLogList) {
             long start2 = System.currentTimeMillis();
             QwSopCourseFinishTempSetting setting = JSON.parseObject(qwSopLogs.getContentJson(), QwSopCourseFinishTempSetting.class);
@@ -159,17 +178,20 @@ public class SendMsg {
             log.info("进入发送消息状态:{}", qwSopLogs.getId());
             String key = "qw:logs:pad:send:id:" + qwSopLogs.getId();
             Long time = redisCache.getCacheObject(key);
+            // 判断这个消息有没有进入过发送,如果进了就不要再发了,防止重复发送,,,,, TODO 千万不能动!!!!!
             if (redisCache.getCacheObject(key) != null) {
                 log.error("{}已有发送:{}, :{}", qwUser.getQwUserName(), qwSopLogs.getId(), time);
                 continue;
             }
-            redisCache.setCacheObject(key, System.currentTimeMillis(), 10, TimeUnit.MINUTES);
+            redisCache.setCacheObject(key, System.currentTimeMillis(), 24, TimeUnit.HOURS);
+            // 循环发送消息里面的每一条消息
             for (QwSopCourseFinishTempSetting.Setting content : setting.getSetting()) {
                 long start4 = System.currentTimeMillis();
+                // 发送
                 sendServer.send(content, user, qwSopLogs, miniMap, parentVo);
                 long end4 = System.currentTimeMillis();
                 log.info("请求pad发送完成:{}, {}, 时长4:{}", user.getQwUserName(), qwSopLogs.getId(), end4 - start4);
-                if(content.getSendStatus() == 2 && "请求失败:消息发送过于频繁,请稍后再试".equals(content.getSendRemarks())){
+                if(content.getSendStatus() == 2 && ("请求失败:消息发送过于频繁,请稍后再试".equals(content.getSendRemarks()) || "请求失败:请求频率异常".equals(content.getSendRemarks()))){
                     QwUser update = new QwUser();
                     update.setRemark("请求频率异常,暂停发送,三小时后恢复继续发送");
                     update.setUpdateTime(new Date());

+ 1 - 1
fs-qw-task/src/main/java/com/fs/FsQwTaskApplication.java

@@ -13,7 +13,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
 @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
 @EnableTransactionManagement
 @EnableAsync
-@EnableScheduling
+//@EnableScheduling
 public class FsQwTaskApplication
 {
     public static void main(String[] args){

+ 9 - 7
fs-qw-task/src/main/java/com/fs/app/controller/CommonController.java

@@ -7,13 +7,11 @@ import com.fs.app.taskService.SopLogsChatTaskService;
 import com.fs.app.taskService.SopLogsTaskService;
 import com.fs.app.taskService.SopWxLogsService;
 import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.ResponseResult;
 import com.fs.course.mapper.FsCourseWatchLogMapper;
-import com.fs.course.service.IFsCourseLinkService;
-import com.fs.course.service.IFsCourseWatchLogService;
-import com.fs.course.service.IFsUserVideoService;
-import com.fs.course.service.IHuaweiObsService;
+import com.fs.course.param.newfs.FsUserCourseAddCompanyUserParam;
+import com.fs.course.service.*;
 import com.fs.his.service.IFsInquiryOrderService;
-import com.fs.his.service.IFsPackageOrderService;
 import com.fs.qw.mapper.QwExternalContactMapper;
 import com.fs.qw.service.IQwExternalContactService;
 import com.fs.qw.service.IQwMaterialService;
@@ -22,7 +20,6 @@ import com.fs.sop.mapper.QwSopLogsMapper;
 import com.fs.sop.mapper.QwSopMapper;
 import com.fs.sop.mapper.SopUserLogsMapper;
 import com.fs.sop.service.*;
-import com.fs.sop.vo.ExtCourseSopWatchLogVO;
 import com.fs.sop.vo.QwSopLogsDoSendListTVO;
 import com.fs.store.service.IFsUserCourseCountService;
 import io.swagger.annotations.Api;
@@ -37,7 +34,6 @@ import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.Arrays;
-import java.util.Base64;
 import java.util.List;
 
 @Api("公共接口")
@@ -49,6 +45,8 @@ public class CommonController {
     @Autowired
     private SopLogsTaskService service;
     @Autowired
+    private IFsUserCourseVideoService courseVideoService;
+    @Autowired
     private SopLogsTaskService sopLogsTaskService;
     @Autowired
     private SopWxLogsService sopWxLogsService;
@@ -262,6 +260,10 @@ public class CommonController {
         userCourseCountService.insertFsUserCourseCountTask();
         return "s";
     }
+    @GetMapping("/isAddkf")
+    public ResponseResult<Boolean> isAddkf(FsUserCourseAddCompanyUserParam param) throws Exception {
+        return courseVideoService.isAddCompanyUser(param);
+    }
 
     @PostMapping("/updateUrl")
     public R updateUrl()

+ 1 - 228
fs-service/src/main/java/com/fs/company/domain/Company.java

@@ -115,6 +115,7 @@ public class Company extends BaseEntity
     private Integer repeat;
     private Integer sendIfType;
     private Integer ifNum;
+    private String groupName;
     @TableField(exist = false)
     private List<String> miniAppMaster;
     @TableField(exist = false)
@@ -123,232 +124,4 @@ public class Company extends BaseEntity
     /** 后台制单是否需要付款 默认1 0-否 1-是*/
     private Integer isPay;
 
-//    public String getDoctorIds() {
-//        return doctorIds;
-//    }
-//
-//    public void setDoctorIds(String doctorIds) {
-//        this.doctorIds = doctorIds;
-//    }
-//
-//    public String getFollowDoctorIds() {
-//        return followDoctorIds;
-//    }
-//
-//    public void setFollowDoctorIds(String followDoctorIds) {
-//        this.followDoctorIds = followDoctorIds;
-//    }
-//
-//    public String getManager() {
-//        return manager;
-//    }
-//
-//    public void setManager(String manager) {
-//        this.manager = manager;
-//    }
-//
-//    public String getOmsCode() {
-//        return omsCode;
-//    }
-//
-//    public void setOmsCode(String omsCode) {
-//        this.omsCode = omsCode;
-//    }
-//
-//    public Integer getVoiceCallerNumber() {
-//        return voiceCallerNumber;
-//    }
-//
-//    public void setVoiceCallerNumber(Integer voiceCallerNumber) {
-//        this.voiceCallerNumber = voiceCallerNumber;
-//    }
-//
-//    public Integer getIsDel() {
-//        return isDel;
-//    }
-//
-//    public void setIsDel(Integer isDel) {
-//        this.isDel = isDel;
-//    }
-//
-//    public BigDecimal getTuiMoney() {
-//        return tuiMoney;
-//    }
-//
-//    public void setTuiMoney(BigDecimal tuiMoney) {
-//        this.tuiMoney = tuiMoney;
-//    }
-//
-//    @Override
-//    public String getRemark() {
-//        return remark;
-//    }
-//
-//    @Override
-//    public void setRemark(String remark) {
-//        this.remark = remark;
-//    }
-//
-//    public String getLinkName() {
-//        return linkName;
-//    }
-//
-//    public void setLinkName(String linkName) {
-//        this.linkName = linkName;
-//    }
-//
-//    public Integer getLimitUserCount() {
-//        return limitUserCount;
-//    }
-//
-//    public void setLimitUserCount(Integer limitUserCount) {
-//        this.limitUserCount = limitUserCount;
-//    }
-//
-//    public Date getLimitTime() {
-//        return limitTime;
-//    }
-//
-//    public void setLimitTime(Date limitTime) {
-//        this.limitTime = limitTime;
-//    }
-//
-//    public String getAppId() {
-//        return appId;
-//    }
-//
-//    public void setAppId(String appId) {
-//        this.appId = appId;
-//    }
-//
-//    public String getAppKey() {
-//        return appKey;
-//    }
-//
-//    public void setAppKey(String appKey) {
-//        this.appKey = appKey;
-//    }
-//
-//    public Long getUserId() {
-//        return userId;
-//    }
-//
-//    public void setUserId(Long userId) {
-//        this.userId = userId;
-//    }
-//
-//    public String getAddressId() {
-//        return addressId;
-//    }
-//
-//    public void setAddressId(String addressId) {
-//        this.addressId = addressId;
-//    }
-//
-//    public Integer getCompanyType() {
-//        return companyType;
-//    }
-//
-//    public void setCompanyType(Integer companyType) {
-//        this.companyType = companyType;
-//    }
-//
-//    public static long getSerialVersionUID() {
-//        return serialVersionUID;
-//    }
-//
-//
-//
-//    public String getUserName() {
-//        return userName;
-//    }
-//
-//    public void setUserName(String userName) {
-//        this.userName = userName;
-//    }
-//
-//    public String getPassword() {
-//        return password;
-//    }
-//
-//    public void setPassword(String password) {
-//        this.password = password;
-//    }
-//
-//    public void setCompanyId(Long companyId)
-//    {
-//        this.companyId = companyId;
-//    }
-//
-//    public Long getCompanyId()
-//    {
-//        return companyId;
-//    }
-//    public void setCompanyName(String companyName)
-//    {
-//        this.companyName = companyName;
-//    }
-//
-//    public String getCompanyName()
-//    {
-//        return companyName;
-//    }
-//    public void setCompanyMobile(String companyMobile)
-//    {
-//        this.companyMobile = companyMobile;
-//    }
-//
-//    public String getCompanyMobile()
-//    {
-//        return companyMobile;
-//    }
-//    public void setCompanyAddress(String companyAddress)
-//    {
-//        this.companyAddress = companyAddress;
-//    }
-//
-//    public String getCompanyAddress()
-//    {
-//        return companyAddress;
-//    }
-//    public void setStatus(Integer status)
-//    {
-//        this.status = status;
-//    }
-//
-//    public Integer getStatus()
-//    {
-//        return status;
-//    }
-//    public void setStartTime(Date startTime)
-//    {
-//        this.startTime = startTime;
-//    }
-//
-//    public Date getStartTime()
-//    {
-//        return startTime;
-//    }
-//
-//    public void setMoney(BigDecimal money)
-//    {
-//        this.money = money;
-//    }
-//
-//    public BigDecimal getMoney()
-//    {
-//        return money;
-//    }
-//
-//    public void setVoiceApiId(Long voiceApiId)
-//    {
-//        this.voiceApiId = voiceApiId;
-//    }
-//
-//    public Long getVoiceApiId()
-//    {
-//        return voiceApiId;
-//    }
-
-
 }

+ 3 - 0
fs-service/src/main/java/com/fs/company/service/impl/CompanyMiniappServiceImpl.java

@@ -2,6 +2,8 @@ package com.fs.company.service.impl;
 
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.annotation.Log;
+import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.PubFun;
 import com.fs.company.domain.CompanyMiniapp;
@@ -119,6 +121,7 @@ public class CompanyMiniappServiceImpl extends ServiceImpl<CompanyMiniappMapper,
     }
 
     @Override
+    @Log(title = "删除企业绑定小程序", businessType = BusinessType.DELETE)
     public void removeByCompanyId(Long companyId) {
         remove(new QueryWrapper<CompanyMiniapp>().eq("company_id",companyId));
     }

+ 63 - 9
fs-service/src/main/java/com/fs/ipad/IpadSendUtils.java

@@ -6,10 +6,14 @@ import com.fs.common.core.redis.RedisCache;
 import com.fs.common.core.redis.RedisCacheT;
 import com.fs.common.exception.base.BaseException;
 import com.fs.common.utils.StringUtils;
+import com.fs.ipad.param.WxGetSessionRoomListParam;
+import com.fs.ipad.param.WxRoomUserListParam;
+import com.fs.ipad.param.WxSendAtMsgParam;
 import com.fs.ipad.vo.*;
 import com.fs.wxwork.dto.*;
 import com.fs.wxwork.service.WxWorkServiceNew;
 import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Component;
 
 import java.net.MalformedURLException;
@@ -21,6 +25,7 @@ import java.util.List;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Supplier;
 
+@Slf4j
 @Component
 @AllArgsConstructor
 public class IpadSendUtils {
@@ -141,6 +146,25 @@ public class IpadSendUtils {
         dto.setIsRoom(vo.isRoom());
         return wxWorkService.SendCDNVideoMsg(dto, vo.getServerId());
     }
+    /**
+     * 发送视频号
+     */
+    public WxWorkResponseDTO<WxwSendVideoNumberRespDTO> sendWxVideo(WxVideoVo vo){
+        WxwSendVideoNumberMsgDTO dto = new WxwSendVideoNumberMsgDTO();
+        dto.setUuid(vo.getUuid());
+        dto.setSend_userid(userIds(vo));
+        dto.setIsRoom(vo.isRoom());
+        dto.setCoverUrl(vo.getCoverUrl());
+        dto.setThumbUrl(vo.getThumbUrl());
+        dto.setAvatar(vo.getAvatar());
+        dto.setNickname(vo.getNickname());
+        dto.setDesc(vo.getDesc());
+        dto.setUrl(vo.getUrl());
+        dto.setExtras(vo.getExtras());
+        dto.setObjectId(vo.getObjectId());
+        dto.setObjectNonceId(vo.getObjectNonceId());
+        return wxWorkService.SendVideoNumber(dto, vo.getServerId());
+    }
     /**
      * 发送小程序
      */
@@ -178,17 +202,15 @@ public class IpadSendUtils {
         }
         WxWorkUserId2VidDTO wxWorkUserId2VidDTO = new WxWorkUserId2VidDTO();
         wxWorkUserId2VidDTO.setOpenid(Collections.singletonList(vo.getExId()));
+        wxWorkUserId2VidDTO.setCorpid(vo.getCorpId());
+        wxWorkUserId2VidDTO.setScorpid(vo.getCorpCode());
         wxWorkUserId2VidDTO.setUuid(vo.getUuid());
         WxWorkResponseDTO<List<WxWorkVid2UserIdRespDTO>> WxWorkVid2UserIdRespDTO = wxWorkService.UserId2Vid(wxWorkUserId2VidDTO, vo.getServerId());
-//        if(WxWorkVid2UserIdRespDTO.getErrcode() != 0){
-//            String errmsg = WxWorkVid2UserIdRespDTO.getErrmsg();
-//            if("实例不存在或在,规定时间内未完成扫码登录uuid失效。".equals(WxWorkVid2UserIdRespDTO.getErrmsg())){
-//                errmsg += "serverId:" + vo.getServerId() + "====url:" + redisCache.getCacheObject("serverId:" + vo.getServerId());
-//            }
-//            throw new BaseException(errmsg);
-//        }
         List<WxWorkVid2UserIdRespDTO> data = WxWorkVid2UserIdRespDTO.getData();
-        if(data.isEmpty()) throw new BaseException("未找到用户");
+        if(data.isEmpty()) {
+            log.error("未找到用户数据,基础数据:{},请求数据:{},返回数据:{}", vo, JSON.toJSONString(wxWorkUserId2VidDTO), JSON.toJSONString(WxWorkVid2UserIdRespDTO));
+            throw new BaseException("未找到用户:" + vo.getId());
+        }
         return data.get(0).getUser_id();
     }
 
@@ -200,13 +222,17 @@ public class IpadSendUtils {
     private Long chatIds(BaseVo vo){
         WxWorkChatIdToRoomIdDTO tdo = new WxWorkChatIdToRoomIdDTO();
         tdo.setChatid(vo.getExId());
+        tdo.setCorpid(vo.getCorpId());
         tdo.setUuid(vo.getUuid());
         WxWorkResponseDTO<WxWorkChatIdToRoomIdResp> result = wxWorkService.ChatIdToRoomId(tdo, vo.getServerId());
         if(result.getErrcode() != 0){
             throw new BaseException(result.getErrmsg());
         }
         WxWorkChatIdToRoomIdResp data = result.getData();
-        if(data == null || data.getRoom_id() == null) throw new BaseException("未找到用户");
+        if(data == null || data.getRoom_id() == null) {
+            log.error("未找到群聊数据,请求数据:{},返回数据:{}", JSON.toJSONString(tdo), JSON.toJSONString(result));
+            throw new BaseException("未找到群聊:" + vo.getId());
+        }
         return data.getRoom_id();
     }
 
@@ -325,4 +351,32 @@ public class IpadSendUtils {
         dto.setUuid(uuid);
         wxWorkService.LoginOut(dto, serverId);
     }
+    public List<WxGetSessionRoomListVo.RoomList> getSessionRoomList(String uuid, Long serverId){
+        WxGetSessionRoomListParam param = new WxGetSessionRoomListParam();
+        param.setUuid(uuid);
+        WxWorkResponseDTO<WxGetSessionRoomListVo> result = wxWorkService.getSessionRoomList(param, serverId);
+        if(result.getErrcode() != 0) throw new BaseException("获取会话群聊失败:" + result.getErrmsg());
+        return result.getData().getRoom_list();
+    }
+
+    public List<WxRoomUserListVo.MemberList> getSessionRoomUserList(String uuid, Long roomId, Long serverId){
+        WxRoomUserListParam param = new WxRoomUserListParam();
+        param.setUuid(uuid);
+        param.setRoomid(roomId);
+        WxWorkResponseDTO<WxRoomUserListVo> result = wxWorkService.getRoomUserList(param, serverId);
+        if(result.getErrcode() != 0) throw new BaseException("获取群聊用户列表错误:" + result.getErrmsg());
+        return result.getData().getMember_list();
+    }
+
+    public void sendTextAtMsg(WxSendAtMsgParam param, Long serverId){
+        WxWorkResponseDTO<WxSendAtMsgVo> result = wxWorkService.sendTextAtMsg(param, serverId);
+        log.info("发送返回数据:{}", result);
+        if(result.getErrcode() != 0) throw new BaseException("发送@消息错误:" + result.getErrmsg());
+    }
+
+    public void sendTxtNoVo(WxWorkSendTextMsgDTO dto, Long serverId){
+        WxWorkResponseDTO<WxWorkSendTextMsgRespDTO> result = wxWorkService.SendTextMsg(dto, serverId);
+        log.info("发送返回数据:{}", result);
+        if(result.getErrcode() != 0) throw new BaseException("发送消息错误:" + result.getErrmsg());
+    }
 }

+ 12 - 0
fs-service/src/main/java/com/fs/ipad/param/WxGetSessionRoomListParam.java

@@ -0,0 +1,12 @@
+package com.fs.ipad.param;
+
+import lombok.Data;
+
+@Data
+public class WxGetSessionRoomListParam {
+
+    private String uuid;
+    private int limit = 1000;
+    private int star_index = 0;
+
+}

+ 11 - 0
fs-service/src/main/java/com/fs/ipad/param/WxRoomUserListParam.java

@@ -0,0 +1,11 @@
+package com.fs.ipad.param;
+
+import lombok.Data;
+
+@Data
+public class WxRoomUserListParam {
+
+    private String uuid;
+    private Long roomid;
+
+}

+ 18 - 0
fs-service/src/main/java/com/fs/ipad/param/WxSendAtMsgParam.java

@@ -0,0 +1,18 @@
+package com.fs.ipad.param;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class WxSendAtMsgParam {
+
+    private String uuid;
+    private String content;
+    private Long send_userid;
+    private List<Long> atids;
+    @JSONField(name = "isRoom")
+    private boolean isRoom = true;
+
+}

+ 23 - 0
fs-service/src/main/java/com/fs/ipad/vo/WxGetSessionRoomListVo.java

@@ -0,0 +1,23 @@
+package com.fs.ipad.vo;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class WxGetSessionRoomListVo {
+
+    private Integer seq;
+    private List<RoomList> room_list;
+
+
+    @Data
+    public static class RoomList{
+        private Long room_id;
+        private Long create_user_id;
+        private Integer total;
+        private Integer flag;
+        private Long create_time;
+        private String nickname;
+    }
+}

+ 44 - 0
fs-service/src/main/java/com/fs/ipad/vo/WxRoomUserListVo.java

@@ -0,0 +1,44 @@
+package com.fs.ipad.vo;
+
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class WxRoomUserListVo {
+
+    private Long room_id;
+    private Long create_user_id;
+    private Long notice_sendervid;
+    private Long create_time;
+    private Long notice_time;
+    private Integer total;
+    private List<Integer> anti_spam_rules;
+    private String notice_content;
+    private String nickname;
+    private Integer flag;
+    private Integer new_flag;
+    private List<MemberList> member_list;
+
+
+    @Data
+    public static class MemberList{
+        private String unionid;
+        private Long create_time;
+        private Long jointime;
+        private Integer sex;
+        private String mobile;
+        private String acctid;
+        private Integer join_scene;
+        private String avatar;
+        private String english_name;
+        private String realname;
+        private String room_notes;
+        private String nickname;
+        private String room_nickname;
+        private String position;
+        private Long uin;
+        private Long invite_user_id;
+        private Long corp_id;
+    }
+}

+ 19 - 0
fs-service/src/main/java/com/fs/ipad/vo/WxSendAtMsgVo.java

@@ -0,0 +1,19 @@
+package com.fs.ipad.vo;
+
+import lombok.Data;
+
+@Data
+public class WxSendAtMsgVo {
+
+    private Long receiver;
+    private Long sender;
+    private Long sendtime;
+    private Long msg_id;
+    private Long server_id;
+    private Long msgtype;
+    private String EmotionType;
+    private String sender_name;
+    private String app_info;
+    private String url;
+    private Integer is_room;
+}

+ 22 - 0
fs-service/src/main/java/com/fs/ipad/vo/WxVideoVo.java

@@ -0,0 +1,22 @@
+package com.fs.ipad.vo;
+
+import lombok.Builder;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@Data
+@Builder
+@EqualsAndHashCode(callSuper = true)
+public class WxVideoVo extends BaseVo {
+
+
+    private String coverUrl;
+    private String thumbUrl;
+    private String avatar;
+    private String nickname;
+    private String desc;
+    private String url;
+    private String extras;
+    private String objectId;
+    private String objectNonceId;
+}

+ 1 - 0
fs-service/src/main/java/com/fs/qw/domain/QwUser.java

@@ -108,4 +108,5 @@ public class QwUser extends BaseEntity
      * 是否自动发课 00、禁用,01、启用
      */
     private String isAuto;
+    private Integer videoGetStatus;
 }

+ 7 - 0
fs-service/src/main/java/com/fs/qw/domain/QwUserVideo.java

@@ -3,6 +3,8 @@ package com.fs.qw.domain;
 import com.fs.common.annotation.Excel;
 import com.fs.common.core.domain.BaseEntity;
 import lombok.Data;
+import org.apache.commons.lang3.builder.ToStringBuilder;
+import org.apache.commons.lang3.builder.ToStringStyle;
 
 /**
  * 企业微信的视频号对象 qw_user_video
@@ -58,4 +60,9 @@ public class QwUserVideo extends BaseEntity
     /** 标识 */
     @Excel(name = "标识")
     private String extras;
+
+    private String objectNonceId;
+    private Long qwUserId;
+    private Long companyUserId;
+    private Long companyId;
 }

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

@@ -424,4 +424,6 @@ public interface QwUserMapper extends BaseMapper<QwUser>
     List<ExternalContactQwUserVO> selectQwUserByFsUserId(@Param("fsUserId") Long fsUserId);
 
     void updateSendType(UpdateSendTypeVo vo);
+
+    QwUser selectOfflineUser();
 }

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

@@ -189,4 +189,8 @@ public interface IQwUserService
     R updateSendType(UpdateSendTypeVo vo);
 
     R getLoginQwIpadStatus(QwLoginHookParam loginParam);
+
+    void atMsg(QwUser qwUser, String s);
+
+    void changeVideoStatus(Long id);
 }

+ 71 - 0
fs-service/src/main/java/com/fs/qw/service/impl/QwUserServiceImpl.java

@@ -13,14 +13,20 @@ import com.ecloud.sdk.ecs.v1.model.VmRebootRequest;
 import com.ecloud.sdk.ecs.v1.model.VmRebootResponse;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.redis.RedisCache;
+import com.fs.common.utils.PubFun;
+import com.fs.company.domain.Company;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.service.ICompanyConfigService;
+import com.fs.company.service.ICompanyService;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.config.ai.AiHostProper;
 import com.fs.config.cloud.CloudHostProper;
 import com.fs.his.config.FsSysConfig;
 import com.fs.his.utils.ConfigUtil;
 import com.fs.ipad.IpadSendUtils;
+import com.fs.ipad.param.WxSendAtMsgParam;
+import com.fs.ipad.vo.WxGetSessionRoomListVo;
+import com.fs.ipad.vo.WxRoomUserListVo;
 import com.fs.qw.domain.*;
 import com.fs.qw.dto.QwUserByToolDTO;
 import com.fs.qw.dto.QwUserKeyDTO;
@@ -42,6 +48,7 @@ import com.fs.sop.domain.QwSop;
 import com.fs.voice.utils.StringUtil;
 import com.fs.wxwork.dto.*;
 import com.fs.wxwork.service.WxWorkService;
+import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.Pair;
 import org.slf4j.Logger;
@@ -68,6 +75,7 @@ import java.util.stream.Collectors;
  * @author fs
  * @date 2024-06-20
  */
+@Slf4j
 @Service
 @EnableAsync
 public class QwUserServiceImpl implements IQwUserService
@@ -78,6 +86,8 @@ public class QwUserServiceImpl implements IQwUserService
     @Autowired
     QwWorkTaskMapper qwWorkTaskMapper;
     @Autowired
+    ICompanyService companyService;
+    @Autowired
     private QwUserMapper qwUserMapper;
     @Autowired
     ICompanyConfigService companyConfigService;
@@ -1449,6 +1459,67 @@ public class QwUserServiceImpl implements IQwUserService
         return null;
     }
 
+    @Override
+    public void atMsg(QwUser qwUser, String msg) {
+        new Thread(() -> {
+            try {
+                String corpId = qwUser.getCorpId();
+                log.info("掉线提醒通知:{}, {}, {}", qwUser.getId(), qwUser.getQwUserName(), corpId);
+                // 获取通知账号
+                QwUser user = qwUserMapper.selectOfflineUser();
+                if(user == null){
+                    log.info("qwId:{}=====未找到通知账号", qwUser.getId());
+                    return;
+                }
+                Company company = companyService.selectCompanyById(qwUser.getCompanyId());
+                log.info("查到主体:{}", qwUser);
+                List<WxGetSessionRoomListVo.RoomList> sessionRoomList = ipadSendUtils.getSessionRoomList(user.getUid(), user.getServerId());
+                Optional<WxGetSessionRoomListVo.RoomList> optional = sessionRoomList.stream().filter(e -> e.getNickname().equals(company.getGroupName()) || e.getNickname().equals(company.getCompanyName())).findFirst();
+                if(!optional.isPresent()){
+                    log.warn("qwId:{}=====会话管理未找到群聊,corpId:{},群聊名称:{}, 查到群聊名称:{}", qwUser.getId(), corpId, company.getCompanyName(), PubFun.listToNewList(sessionRoomList, WxGetSessionRoomListVo.RoomList::getNickname));
+                    log.info("qwId:{}=====会话管理未找到群聊,corpId:{},群聊名称:{}, 查到群聊名称:{}", qwUser.getId(), corpId, company.getCompanyName(), PubFun.listToNewList(sessionRoomList, WxGetSessionRoomListVo.RoomList::getNickname));
+                    return;
+                }
+                WxGetSessionRoomListVo.RoomList room = optional.get();
+                log.info("找到会话群聊:{}", room);
+                CompanyUser companyUser = companyUserService.selectCompanyUserById(qwUser.getCompanyUserId());
+                log.info("企微账号:{}", JSON.toJSONString(companyUser));
+                List<WxRoomUserListVo.MemberList> memberLists = ipadSendUtils.getSessionRoomUserList(user.getUid(), room.getRoom_id(), user.getServerId());
+                Function<WxRoomUserListVo.MemberList, String> getName = e -> StringUtils.isEmpty(e.getRoom_nickname()) ? e.getNickname() : e.getRoom_nickname();
+                Optional<WxRoomUserListVo.MemberList> first = memberLists.stream().filter(e -> getName.apply(e).equals(companyUser.getUserName()) || getName.apply(e).equals(companyUser.getNickName())).findFirst();
+                String sendMsg = "企微账号:" + qwUser.getQwUserName() + " - " + msg;
+                if(!first.isPresent()){
+                    WxWorkSendTextMsgDTO dto = new WxWorkSendTextMsgDTO();
+                    dto.setUuid(user.getUid());
+                    dto.setSend_userid(room.getRoom_id());
+                    dto.setIsRoom(true);
+                    dto.setContent(sendMsg);
+                    ipadSendUtils.sendTxtNoVo(dto, user.getServerId());
+                }else{
+                    WxRoomUserListVo.MemberList memberList = first.get();
+                    log.info("找到掉线人:{}", memberList);
+                    WxSendAtMsgParam param = new WxSendAtMsgParam();
+                    param.setUuid(user.getUid());
+                    param.setContent(sendMsg);
+                    param.setSend_userid(room.getRoom_id());
+                    param.setAtids(Collections.singletonList(memberList.getUin()));
+                    param.setRoom(true);
+                    log.info("发送数据组装:{}", param);
+                    ipadSendUtils.sendTextAtMsg(param, user.getServerId());
+                }
+            }catch (Exception e){
+                log.warn("掉线提醒发送失败", e);
+            }
+        }).start();
+    }
+
+    @Override
+    public void changeVideoStatus(Long id) {
+        QwUser qwUser = qwUserMapper.selectQwUserById(id);
+        qwUser.setVideoGetStatus(qwUser.getVideoGetStatus() == 0 ? 1 : 0);
+        qwUserMapper.updateById(qwUser);
+    }
+
 
     /**
      * 构建查询条件

+ 1 - 0
fs-service/src/main/java/com/fs/qw/vo/QwSopCourseFinishTempSetting.java

@@ -49,6 +49,7 @@ public class QwSopCourseFinishTempSetting implements Serializable,Cloneable{
         /**
          * QwSopLog的主键(用于发送)
          */
+        private Long videoId;
         private Long sopLogId;
         /**
          * 员工的id(用于发送)

+ 38 - 0
fs-service/src/main/java/com/fs/wxwork/dto/WxwSendVideoNumberMsgDTO.java

@@ -0,0 +1,38 @@
+package com.fs.wxwork.dto;
+
+import lombok.Data;
+
+/**
+ * 发送CDN视频消息 请求DTO
+ *
+ * @author xdd
+ * @date 2025-02-27
+ */
+@Data
+public class WxwSendVideoNumberMsgDTO {
+
+    /**
+     * 每个实例的唯一标识,根据uuid操作具体企业微信
+     */
+    private String uuid;
+
+    /**
+     * 要发送的人或群id
+     */
+    private Long send_userid;
+
+    /**
+     * 是否是群消息
+     */
+    private Boolean isRoom;
+
+    private String coverUrl;
+    private String thumbUrl;
+    private String avatar;
+    private String nickname;
+    private String desc;
+    private String url;
+    private String extras;
+    private String objectId;
+    private String objectNonceId;
+}

+ 45 - 16
fs-service/src/main/java/com/fs/wxwork/service/WxWorkServiceNew.java

@@ -1,15 +1,24 @@
 package com.fs.wxwork.service;
 
+import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.TypeReference;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.exception.CustomException;
+import com.fs.ipad.param.WxGetSessionRoomListParam;
+import com.fs.ipad.param.WxRoomUserListParam;
+import com.fs.ipad.param.WxSendAtMsgParam;
+import com.fs.ipad.vo.WxGetSessionRoomListVo;
+import com.fs.ipad.vo.WxRoomUserListVo;
+import com.fs.ipad.vo.WxSendAtMsgVo;
 import com.fs.qw.domain.QwIpadServer;
 import com.fs.qw.service.IQwIpadServerService;
 import com.fs.wxwork.dto.*;
 import com.fs.wxwork.utils.WxWorkHttpUtil;
 import com.fs.wxwork.utils.WxWorkHttpUtilNew;
 import lombok.AllArgsConstructor;
+import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import java.util.List;
@@ -30,7 +39,7 @@ public class WxWorkServiceNew {
         if (url != null && !url.isEmpty()) {
             return url;
         }
-        System.out.println("serverId:" + serverId);
+        log.info("serverId:" + serverId);
         QwIpadServer qwIpadServer = qwIpadServerService.selectQwIpadServerById(serverId);
         if (qwIpadServer == null||qwIpadServer.getUrl()==null) {
             throw new CustomException("未获取到服务地址与端口");
@@ -51,20 +60,20 @@ public class WxWorkServiceNew {
         });
     }
 
-    
+
     public WxWorkResponseDTO<WxWorkSendAppMsgRespDTO> SendAppMsg(WxWorkSendAppMsgDTO param,Long serverId) {
         String url = getUrl(serverId) + "/SendAppMsg";
         return WxWorkHttpUtilNew.postWithType(url, param, new TypeReference<WxWorkResponseDTO<WxWorkSendAppMsgRespDTO>>() {
         });
     }
 
-    
+
     public WxWorkResponseDTO<WxwSendLinkMsgRespDTO> SendLinkMsg(WxwSendLinkMsgDTO param,Long serverId) {
         String url = getUrl(serverId) + "/SendLinkMsg";
         return WxWorkHttpUtilNew.postWithType(url, param, new TypeReference<WxWorkResponseDTO<WxwSendLinkMsgRespDTO>>() {
         });
     }
-    
+
     public WxWorkResponseDTO<WxwSendCDNImgMsgRespDTO> SendCDNImgMsg(WxwSendCDNImgMsgDTO param,Long serverId) {
         String url = getUrl(serverId) + "/SendCDNImgMsg";
         return WxWorkHttpUtilNew.postWithType(url, param, new TypeReference<WxWorkResponseDTO<WxwSendCDNImgMsgRespDTO>>() {});
@@ -80,12 +89,17 @@ public class WxWorkServiceNew {
         String url = getUrl(serverId) + "/SendCDNVoiceMsg";
         return WxWorkHttpUtilNew.postWithType(url, param, new TypeReference<WxWorkResponseDTO<WxwSendCDNVoiceMsgRespDTO>>() {});
     }
-    
+
     public WxWorkResponseDTO<WxwSendCDNVideoMsgRespDTO> SendCDNVideoMsg(WxwSendCDNVideoMsgDTO param,Long serverId) {
         String url = getUrl(serverId) + "/SendCDNVideoMsg";
         return WxWorkHttpUtilNew.postWithType(url, param, new TypeReference<WxWorkResponseDTO<WxwSendCDNVideoMsgRespDTO>>() {});
     }
-    
+
+    public WxWorkResponseDTO<WxwSendVideoNumberRespDTO> SendVideoNumber(WxwSendVideoNumberMsgDTO param,Long serverId) {
+        String url = getUrl(serverId) + "/SendVideoNumber";
+        return WxWorkHttpUtilNew.postWithType(url, param, new TypeReference<WxWorkResponseDTO<WxwSendVideoNumberRespDTO>>() {});
+    }
+
     public String getCorpId(String uuid, Long corpId,Long serverId) {
         String sCorpId = redisCache.getCacheObject("ipad:corpId:" + corpId);
         if (sCorpId != null&& !sCorpId.isEmpty()) {
@@ -108,7 +122,7 @@ public class WxWorkServiceNew {
             String scorpId = corpInfo.getScorp_id();
 
             if (scorpId!=null&&!scorpId.isEmpty()) {
-               redisCache.setCacheObject("ipad:corpId:" + corpId,scorpId);
+                redisCache.setCacheObject("ipad:corpId:" + corpId,scorpId);
 
                 return scorpId;
             }
@@ -116,19 +130,19 @@ public class WxWorkServiceNew {
 
         return "";
     }
-    
+
     public WxWorkResponseDTO<WxwUploadCdnLinkFileRespDTO> uploadCdnLinkFile(WxwUploadCdnLinkFileDTO param, Long serverId) {
 
         String url = getUrl(serverId) + "/UploadCdnLinkFile";
         return WxWorkHttpUtilNew.postWithType(url, param, new TypeReference<WxWorkResponseDTO<WxwUploadCdnLinkFileRespDTO>>() {});
     }
-    
+
     public WxWorkResponseDTO<List<WxWorkVid2UserIdRespDTO>> UserId2Vid(WxWorkUserId2VidDTO param,Long serverId) {
         String url = getUrl(serverId) + "/UserId2Vid";
         return WxWorkHttpUtilNew.postWithType(url, param, new TypeReference<WxWorkResponseDTO<List<WxWorkVid2UserIdRespDTO>>>() {});
     }
 
-    
+
     public WxWorkResponseDTO<WxWorkChatIdToRoomIdResp> ChatIdToRoomId(WxWorkChatIdToRoomIdDTO param, Long serverId) {
         String url = getUrl(serverId) + "/ChatIdToRoomId";
         return WxWorkHttpUtilNew.postWithType(url, param, new TypeReference<WxWorkResponseDTO<WxWorkChatIdToRoomIdResp>>() {});
@@ -140,7 +154,7 @@ public class WxWorkServiceNew {
      * @param serverId  服务器ID
      * @return  WxWorkResponseDTO
      */
-    
+
     public WxWorkResponseDTO<WxCdnUploadImgLinkResp> cdnUploadImgLink(WxCdnUploadImgLinkDTO param, Long serverId) {
         String url = getUrl(serverId) + "/CdnUploadImgLink";
         return WxWorkHttpUtilNew.postWithType(url, param, new TypeReference<WxWorkResponseDTO<WxCdnUploadImgLinkResp>>() {});
@@ -151,7 +165,7 @@ public class WxWorkServiceNew {
      * @param serverId  服务器ID
      * @return  WxWorkResponseDTO
      */
-    
+
     public WxWorkResponseDTO<WxCdnUploadVideoResp> uploadCdnVideoLink(WxCdnUploadVideoLinkDTO param, Long serverId) {
         String url = getUrl(serverId) + "/UploadCdnVideoLink";
         return WxWorkHttpUtilNew.postWithType(url, param, new TypeReference<WxWorkResponseDTO<WxCdnUploadVideoResp>>() {});
@@ -163,7 +177,7 @@ public class WxWorkServiceNew {
      * @param serverId  服务器ID
      * @return  WxWorkResponseDTO
      */
-    
+
     public WxWorkResponseDTO<WxBigFileUploadLinkResp> bigFileUploadLink(WxBigFileUploadLinkDTO param, Long serverId) {
         WGetBigAuthkeyDTO dto = new WGetBigAuthkeyDTO();
         dto.setUuid(param.getUuid());
@@ -174,7 +188,7 @@ public class WxWorkServiceNew {
         return WxWorkHttpUtilNew.postWithType(url, param, new TypeReference<WxWorkResponseDTO<WxBigFileUploadLinkResp>>() {});
     }
 
-    
+
     public WxWorkResponseDTO<WxLoginResp> isLogin(WxLoginDTO param, Long serverId) {
         String url = getUrl(serverId) + "/GetRunClientByUuid";
         return WxWorkHttpUtilNew.postWithType(url, param, new TypeReference<WxWorkResponseDTO<WxLoginResp>>() {});
@@ -193,6 +207,21 @@ public class WxWorkServiceNew {
 
     public void LoginOut(WxWorkGetQrCodeDTO dto, Long serverId) {
         String url = getUrl(serverId) + "/LoginOut";
-        log.info("退出登录:{}, serverID:{}", dto, serverId);
         WxWorkHttpUtil.postWithType(url, dto, new TypeReference<WxWorkResponseDTO<WxwLoginOutRespDTO>>() {});
-    }}
+    }
+
+    public WxWorkResponseDTO<WxGetSessionRoomListVo> getSessionRoomList(WxGetSessionRoomListParam param, Long serverId) {
+        String url = getUrl(serverId) + "/GetSessionRoomList";
+        return WxWorkHttpUtil.postWithType(url, param, new TypeReference<WxWorkResponseDTO<WxGetSessionRoomListVo>>() {});
+    }
+
+    public WxWorkResponseDTO<WxRoomUserListVo> getRoomUserList(WxRoomUserListParam param, Long serverId) {
+        String url = getUrl(serverId) + "/GetRoomUserList";
+        return WxWorkHttpUtil.postWithType(url, param, new TypeReference<WxWorkResponseDTO<WxRoomUserListVo>>() {});
+    }
+
+    public WxWorkResponseDTO<WxSendAtMsgVo> sendTextAtMsg(WxSendAtMsgParam param, Long serverId) {
+        String url = getUrl(serverId) + "/SendTextAtMsg";
+        return WxWorkHttpUtil.postWithType(url, param, new TypeReference<WxWorkResponseDTO<WxSendAtMsgVo>>() {});
+    }
+}

+ 90 - 0
fs-service/src/main/resources/application-config-druid-drk-test.yml

@@ -0,0 +1,90 @@
+baidu:
+  token: 12313231232
+  back-domain: https://www.xxxx.com
+#配置
+logging:
+  level:
+    org.springframework.web: INFO
+    com.github.binarywang.demo.wx.cp: DEBUG
+    me.chanjar.weixin: DEBUG
+wx:
+  miniapp:
+    configs:
+      - appid: wx76cb55db092a41ae   #德瑞康
+        secret: c737a27415946994e977d665d87643b3
+        token: Ncbnd7lJvkripVOpyTFAna6NAWCxCrvC
+        aesKey: HlEiBB55eaWUaeBVAQO3cWKWPYv1vOVQSq7nFNICw4E
+        msgDataFormat: JSON
+  cp:
+    corpId: wwde3be9a4a7004380 #德瑞康企业id
+    appConfigs:
+      - agentId: 1000006 #德瑞康应用id
+        secret: prclvyFLiZD9XEns9ltt6vFMszuFc_wgm3MTnfY6ZEk #德瑞康
+        token: PPKOdAlCoMO
+        aesKey: PKvaxtpSv8NGpfTDm7VUHIK8Wok2ESyYX24qpXJAdMP
+  pay:
+    appId:  #微信公众号或者小程序等的appid
+    mchId:  #微信支付商户号
+    mchKey:  #微信支付商户密钥
+    subAppId:  #服务商模式下的子商户公众账号ID
+    subMchId:  #服务商模式下的子商户号
+    keyPath: c:\\cert\\apiclient_cert.p12 # p12证书的位置,可以指定绝对路径,也可以指定类路径(以classpath:开头)
+    notifyUrl: https://userapp.his.runtzh.com/app/wxpay/wxPayNotify
+  mp:
+    useRedis: false
+    redisConfig:
+      host: 127.0.0.1
+      port: 6379
+      timeout: 2000
+    configs:
+      - appId: wx090c5f399d65456e # 第一个公众号的appid  //公众号名称:德瑞康
+        secret: dc70e963077d80cf61280aea6b7c52b7 # 公众号的appsecret--德瑞康
+        token: PPKOdAlCoMO # 接口配置里的Token值
+        aesKey: Eswa6VjwtVMCcw03qZy6fWllgrv5aytIA1SZPEU0kU2 # 接口配置里的EncodingAESKey值
+aifabu:  #爱链接
+  appKey: 7b471be905ab17e00f3b858c6710dd117601d008
+watch:
+  watchUrl: watch.ylrzcloud.com/prod-api
+  #  account: tcloud
+  #  password: mdf-m2h_6yw2$hq
+  account1: ccif #866655060138751
+  password1: cp-t5or_6xw7$mt
+  account2: tcloud #rt500台
+  password2: mdf-m2h_6yw2$hq
+  account3: whr
+  password3: v9xsKuqn_$d2y
+
+fs :
+  commonApi: http://127.0.0.1:7771
+  h5CommonApi: http://127.0.0.1:7771
+nuonuo:
+  key: 10924508
+  secret: A2EB20764D304D16
+
+# 存储捅配置
+tencent_cloud_config:
+  secret_id: AKIDiMq9lDf2EOM9lIfqqfKo7FNgM5meD0sT
+  secret_key: u5SuS80342xzx8FRBukza9lVNHKNMSaB
+  bucket: drk-1323137866
+  app_id: 1323137866
+  region: ap-chongqing
+  proxy: drk
+tmp_secret_config:
+  secret_id: AKIDCj7NSNAovtqeJpBau8GZ4CGB71thXIxX
+  secret_key: lTB5zwqqz7CNhzDOWivFWedgfTBgxgBT
+  bucket: fs-1319721001
+  app_id: 1319721001
+  region: ap-chongqing
+  proxy: fs
+cloud_host:
+  company_name: 德瑞康
+headerImg:
+  imgUrl: https://drk-1363981074.cos.ap-chongqing.myqcloud.com/fs/logo/30d7a0d1ec31e5ac16c6e96d5ca76ad.png
+ipad:
+  ipadUrl: http://ipad.cdwjyyh.com
+  aiApi: 1212121212
+wx_miniapp_temp:
+  pay_order_temp_id:
+  inquiry_temp_id:
+
+

+ 4 - 2
fs-service/src/main/resources/application-config-druid-yjb.yml

@@ -10,7 +10,7 @@ logging:
 wx:
   miniapp:
     configs:
-      - appid: wx76cb55db092a41ae   #德瑞康
+      - appid: wx76cb55db092a41ae   #医健保
         secret: c737a27415946994e977d665d87643b3
         token: Ncbnd7lJvkripVOpyTFAna6NAWCxCrvC
         aesKey: HlEiBB55eaWUaeBVAQO3cWKWPYv1vOVQSq7nFNICw4E
@@ -85,5 +85,7 @@ ipad:
 wx_miniapp_temp:
   pay_order_temp_id:
   inquiry_temp_id:
-
+openIM:
+  secret: openIM123
+  userID: imAdmin
 

+ 143 - 0
fs-service/src/main/resources/application-druid-drk-test.yml

@@ -0,0 +1,143 @@
+# 数据源配置
+spring:
+    profiles:
+        include: config-druid-drk,common
+    # redis 配置
+    redis:
+        # 地址
+        host: localhost
+        # 端口,默认为6379
+        port: 6379
+        # 数据库索引
+        database: 0
+        # 密码
+        password:
+        # 连接超时时间
+        timeout: 20s
+        lettuce:
+            pool:
+                # 连接池中的最小空闲连接
+                min-idle: 0
+                # 连接池中的最大空闲连接
+                max-idle: 8
+                # 连接池的最大数据库连接数
+                max-active: 8
+                # #连接池最大阻塞等待时间(使用负值表示没有限制)
+                max-wait: -1ms
+    datasource:
+        mysql:
+            type: com.alibaba.druid.pool.DruidDataSource
+            driverClassName: com.mysql.cj.jdbc.Driver
+            druid:
+                # 主库数据源
+                master:
+                    url: jdbc:mysql://nj-cdb-r9csy22x.sql.tencentcdb.com:28653/fs_his?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: Ylrz_1q2w3e4r5t6y
+                # 从库数据源
+                slave:
+                    # 从数据源开关/默认关闭
+                    enabled: false
+                    url:
+                    username:
+                    password:
+                # 初始连接数
+                initialSize: 5
+                # 最小连接池数量
+                minIdle: 10
+                # 最大连接池数量
+                maxActive: 20
+                # 配置获取连接等待超时的时间
+                maxWait: 60000
+                # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+                timeBetweenEvictionRunsMillis: 60000
+                # 配置一个连接在池中最小生存的时间,单位是毫秒
+                minEvictableIdleTimeMillis: 300000
+                # 配置一个连接在池中最大生存的时间,单位是毫秒
+                maxEvictableIdleTimeMillis: 900000
+                # 配置检测连接是否有效
+                validationQuery: SELECT 1 FROM DUAL
+                testWhileIdle: true
+                testOnBorrow: false
+                testOnReturn: false
+                webStatFilter:
+                    enabled: true
+                statViewServlet:
+                    enabled: true
+                    # 设置白名单,不填则允许所有访问
+                    allow:
+                    url-pattern: /druid/*
+                    # 控制台管理用户名和密码
+                    login-username: fs
+                    login-password: 123456
+                filter:
+                    stat:
+                        enabled: true
+                        # 慢SQL记录
+                        log-slow-sql: true
+                        slow-sql-millis: 1000
+                        merge-sql: true
+                    wall:
+                        config:
+                            multi-statement-allow: true
+        sop:
+            type: com.alibaba.druid.pool.DruidDataSource
+            driverClassName: com.mysql.cj.jdbc.Driver
+            druid:
+                # 主库数据源
+                master:
+                    url: jdbc:mysql://nj-cdb-r9csy22x.sql.tencentcdb.com:28653/fs_his_sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: Ylrz_1q2w3e4r5t6y
+                # 初始连接数
+                initialSize: 5
+                # 最小连接池数量
+                minIdle: 10
+                # 最大连接池数量
+                maxActive: 20
+                # 配置获取连接等待超时的时间
+                maxWait: 60000
+                # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+                timeBetweenEvictionRunsMillis: 60000
+                # 配置一个连接在池中最小生存的时间,单位是毫秒
+                minEvictableIdleTimeMillis: 300000
+                # 配置一个连接在池中最大生存的时间,单位是毫秒
+                maxEvictableIdleTimeMillis: 900000
+                # 配置检测连接是否有效
+                validationQuery: SELECT 1 FROM DUAL
+                testWhileIdle: true
+                testOnBorrow: false
+                testOnReturn: false
+                webStatFilter:
+                    enabled: true
+                statViewServlet:
+                    enabled: true
+                    # 设置白名单,不填则允许所有访问
+                    allow:
+                    url-pattern: /druid/*
+                    # 控制台管理用户名和密码
+                    login-username: fs
+                    login-password: 123456
+                filter:
+                    stat:
+                        enabled: true
+                        # 慢SQL记录
+                        log-slow-sql: true
+                        slow-sql-millis: 1000
+                        merge-sql: true
+                    wall:
+                        config:
+                            multi-statement-allow: true
+rocketmq:
+    name-server: rmq-1243b25nj.rocketmq.gz.public.tencenttdmq.com:8080 # RocketMQ NameServer 地址
+    producer:
+        group: my-producer-group
+        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
+        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey
+    consumer:
+        group: test-group
+        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
+        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey
+openIM:
+    secret: openIM123
+    userID: imAdmin

+ 1 - 1
fs-service/src/main/resources/application-druid-yjb.yml

@@ -1,7 +1,7 @@
 # 数据源配置
 spring:
     profiles:
-        include: config-druid-xzt,common
+        include: config-druid-yjb,common
     # redis 配置
     redis:
         # 地址

+ 4 - 0
fs-service/src/main/resources/mapper/company/CompanyMapper.xml

@@ -38,6 +38,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="repeat"    column="repeat"    />
         <result property="sendIfType"    column="send_if_type"    />
         <result property="ifNum"    column="if_num"    />
+        <result property="groupName"    column="group_name"    />
     </resultMap>
 
     <sql id="selectCompanyVo">
@@ -111,6 +112,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="repeat != null">`repeat`,</if>
             <if test="sendIfType != null">send_if_type,</if>
             <if test="ifNum != null">if_num,</if>
+            <if test="groupName != null">group_name,</if>
         </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="companyName != null">#{companyName},</if>
@@ -143,6 +145,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="repeat != null">#{repeat},</if>
             <if test="sendIfType != null">#{sendIfType},</if>
             <if test="ifNum != null">#{ifNum},</if>
+            <if test="groupName != null">#{groupName},</if>
          </trim>
     </insert>
 
@@ -181,6 +184,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="repeat != null">`repeat` = #{repeat},</if>
             <if test="sendIfType != null">send_if_type = #{sendIfType},</if>
             <if test="ifNum != null">if_num = #{ifNum},</if>
+            <if test="groupName != null">group_name = #{groupName},</if>
         </trim>
         where company_id = #{companyId}
     </update>

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

@@ -33,10 +33,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="serverId"    column="server_id"    />
         <result property="serverStatus"    column="server_status"    />
         <result property="isAuto"    column="is_auto"    />
+        <result property="videoGetStatus"    column="video_get_status"    />
     </resultMap>
 
     <sql id="selectQwUserVo">
-        select id,is_auto, qw_user_id,server_id,server_status,ipad_status,config_id,vid,uid,contact_way,app_key, qw_user_name, department, openid, company_id, company_user_id, corp_id, status, is_del, welcome_text, welcome_image, is_send_msg,app_key,qw_hook_id,fastGpt_role_id,login_status,tool_status,login_code_url,version from qw_user
+        select id,is_auto, video_get_status, qw_user_id,server_id,server_status,ipad_status,config_id,vid,uid,contact_way,app_key, qw_user_name, department, openid, company_id, company_user_id, corp_id, status, is_del, welcome_text, welcome_image, is_send_msg,app_key,qw_hook_id,fastGpt_role_id,login_status,tool_status,login_code_url,version from qw_user
         </sql>
 
     <select id="selectQwUserList" parameterType="QwUser" resultMap="QwUserResult">
@@ -112,6 +113,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="serverId != null">server_id,</if>
             <if test="serverStatus != null">server_status,</if>
             <if test="isAuto != null">is_auto,</if>
+            <if test="videoGetStatus != null">video_get_status,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="qwUserId != null">#{qwUserId},</if>
@@ -139,6 +141,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="ipadStatus != null">#{ipadStatus},</if>
             <if test="serverId != null">#{serverId},</if>
             <if test="isAuto != null">#{isAuto},</if>
+            <if test="videoGetStatus != null">#{videoGetStatus},</if>
          </trim>
     </insert>
 
@@ -172,6 +175,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="serverId != null">server_id = #{serverId},</if>
             <if test="serverStatus != null">server_status = #{serverStatus},</if>
             <if test="isAuto != null">is_auto = #{isAuto},</if>
+            <if test="videoGetStatus != null">video_get_status = #{videoGetStatus},</if>
         </trim>
         where id = #{id}
     </update>
@@ -251,4 +255,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <update id="updateSendType">
         update qw_user set send_msg_type = #{type} where id in <foreach collection="ids" open="(" close=")" separator="," item="item">#{item}</foreach>
     </update>
+
+    <select id="selectOfflineUser" resultType="com.fs.qw.domain.QwUser">
+        select * from qw_user where send_msg_type = 2 and server_id is not null and server_status = 1 and ipad_status = 1 limit 1
+    </select>
 </mapper>

+ 32 - 5
fs-service/src/main/resources/mapper/qw/QwUserVideoMapper.xml

@@ -1,7 +1,7 @@
 <?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">
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.fs.qw.mapper.QwUserVideoMapper">
 
     <resultMap type="QwUserVideo" id="QwUserVideoResult">
@@ -18,10 +18,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="extras"    column="extras"    />
         <result property="createTime"    column="create_time"    />
         <result property="updateTime"    column="update_time"    />
+        <result property="objectNonceId"    column="object_nonce_id"    />
+        <result property="qwUserId"    column="qw_user_id"    />
+        <result property="companyUserId"    column="company_user_id"    />
+        <result property="companyId"    column="company_id"    />
     </resultMap>
 
     <sql id="selectQwUserVideoVo">
-        select id, app_key, sender_name, object_id, cover_url, thumb_url, avatar, nick_name, `desc`, url, extras,update_time,create_time from qw_user_video
+        select id, app_key,qw_user_id, company_user_id, company_id, object_nonce_id, sender_name, object_id, cover_url, thumb_url, avatar, nick_name, `desc`, url, extras,update_time,create_time from qw_user_video
     </sql>
 
     <select id="selectQwUserVideoList" parameterType="QwUserVideo" resultMap="QwUserVideoResult">
@@ -62,7 +66,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="extras != null">extras,</if>
             <if test="updateTime != null">update_time,</if>
             <if test="createTime != null">create_time,</if>
-         </trim>
+            <if test="objectNonceId != null">object_nonce_id,</if>
+            <if test="qwUserId != null">qw_user_id,</if>
+            <if test="companyUserId != null">company_user_id,</if>
+            <if test="companyId != null">company_id,</if>
+        </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="appKey != null">#{appKey},</if>
             <if test="senderName != null">#{senderName},</if>
@@ -76,7 +84,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="extras != null">#{extras},</if>
             <if test="updateTime != null">#{updateTime},</if>
             <if test="createTime != null">#{createTime},</if>
-         </trim>
+            <if test="objectNonceId != null">#{objectNonceId},</if>
+            <if test="qwUserId != null">#{qwUserId},</if>
+            <if test="companyUserId != null">#{companyUserId},</if>
+            <if test="companyId != null">#{companyId},</if>
+        </trim>
     </insert>
 
     <update id="updateQwUserVideo" parameterType="QwUserVideo">
@@ -94,6 +106,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="extras != null">extras = #{extras},</if>
             <if test="updateTime != null">update_time = #{updateTime},</if>
             <if test="createTime != null">create_time = #{createTime},</if>
+            <if test="objectNonceId != null">object_nonce_id = #{objectNonceId},</if>
+            <if test="qwUserId != null">qw_user_id = #{qwUserId},</if>
+            <if test="companyUserId != null">company_user_id = #{companyUserId},</if>
+            <if test="companyId != null">company_id = #{companyId},</if>
         </trim>
         where id = #{id}
     </update>
@@ -108,4 +124,15 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             #{id}
         </foreach>
     </delete>
+
+    <select id="selectQwUserVideoByIds" resultType="com.fs.qw.domain.QwUserVideo">
+        select * from qw_user_video where id in
+        <foreach item="id" collection="ids" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </select>
+
+    <select id="selectByObjectId" resultType="com.fs.qw.domain.QwUserVideo">
+        select * from qw_user_video where object_id = #{objectId} and qw_user_id = #{id}
+    </select>
 </mapper>