liujc 2 năm trước cách đây
mục cha
commit
a00d4ab771
51 tập tin đã thay đổi với 2901 bổ sung1301 xóa
  1. 76 0
      cooleshow-common/src/main/java/com/yonge/cooleshow/common/enums/EGroupDefinedDataType.java
  2. 88 0
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/ImGroupController.java
  3. 245 0
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/open/ImController.java
  4. 0 95
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/open/ImUserFriendController.java
  5. 16 0
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/task/TaskController.java
  6. 11 0
      cooleshow-user/user-biz/pom.xml
  7. 24 24
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/config/RongCloudConfig.java
  8. 9 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dao/ImGroupDao.java
  9. 1 1
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dao/ImGroupMemberDao.java
  10. 41 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/LiveRoomStatus.java
  11. 521 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/TencentData.java
  12. 40 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/TencentImCallbackResult.java
  13. 3 191
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/CourseGroup.java
  14. 9 220
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/CourseSchedule.java
  15. 91 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/ImHistoryMessage.java
  16. 10 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/ImRoomMessage.java
  17. 3 39
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/ImUserStateSync.java
  18. 17 142
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/LiveRoom.java
  19. 7 23
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/LiveRoomVideo.java
  20. 7 164
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/RoomInfoCache.java
  21. 1 1
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/RoomTypeEnum.java
  22. 30 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/im/ETencentGroupType.java
  23. 31 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/im/ETencentImCallbackCommand.java
  24. 43 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/live/EAnchorStatus.java
  25. 40 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/live/EOnOffStatus.java
  26. 8 8
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/ImGroupMemberService.java
  27. 61 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/ImGroupService.java
  28. 41 4
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/LiveRoomService.java
  29. 2 2
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/CourseGroupServiceImpl.java
  30. 5 5
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/CourseHomeworkServiceImpl.java
  31. 8 12
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/CourseScheduleServiceImpl.java
  32. 109 48
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/CustomerServiceBatchSendingServiceImpl.java
  33. 3 2
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImGroupMemberAuditServiceImpl.java
  34. 39 35
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImGroupMemberServiceImpl.java
  35. 422 92
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImGroupServiceImpl.java
  36. 9 6
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImNetworkRoomMemberServiceImpl.java
  37. 28 13
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImNetworkRoomServiceImpl.java
  38. 70 38
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImUserFriendServiceImpl.java
  39. 598 90
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/LiveRoomServiceImpl.java
  40. 0 2
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/LiveRoomVideoServiceImpl.java
  41. 3 2
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/StudentServiceImpl.java
  42. 2 6
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/StudentStarServiceImpl.java
  43. 37 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/im/ImGroupWrapper.java
  44. 38 0
      cooleshow-user/user-biz/src/main/resources/config/mybatis/ImGroupMapper.xml
  45. 1 5
      cooleshow-user/user-biz/src/main/resources/config/mybatis/ImGroupMemberMapper.xml
  46. 7 1
      cooleshow-user/user-classroom/src/main/java/com/yonge/cooleshow/classroom/controller/ImNetworkRoomController.java
  47. 6 2
      cooleshow-user/user-student/src/main/java/com/yonge/cooleshow/student/controller/CourseHomeworkController.java
  48. 15 21
      cooleshow-user/user-student/src/main/java/com/yonge/cooleshow/student/controller/TeacherController.java
  49. 13 6
      cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/controller/CourseHomeworkController.java
  50. 11 0
      cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/controller/TeacherLiveRoomController.java
  51. 1 1
      cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/task/TaskController.java

+ 76 - 0
cooleshow-common/src/main/java/com/yonge/cooleshow/common/enums/EGroupDefinedDataType.java

@@ -0,0 +1,76 @@
+package com.yonge.cooleshow.common.enums;
+
+/**
+ * Description
+ *
+ * @author liujunchi
+ * @date 2023-03-20
+ */
+public enum EGroupDefinedDataType {
+
+    /**
+     * 主讲人状态
+     */
+    ANCHOR_STATUS("主讲人状态"), // "ONLINE":"OFFLINE"
+
+    /**
+     * 全局禁言
+     */
+    GLOBAL_BAN("全局禁言"), // 开启禁言"ON" 关闭禁言 "OFF"
+
+    /**
+     * 点赞数
+     */
+    LIKES("点赞数"), // 点赞数
+
+    /**
+     * 在线人数
+     */
+    MEMBER_ONLINE("在线人数"), // 在线人数
+
+
+    /**
+     * 累计观看人数
+     */
+    MEMBER_TOTAL("累计观看人数"), // 累计观看人数
+
+    /**
+     * 当前房间推流状态
+     */
+    LIVE_STATUS("当前房间推流状态"), //开播 "ON" 暂停直播 "OFF"
+
+    /**
+     * 主播摄像头状态
+     */
+    ANCHOR_CAMERA("主播摄像头状态"), // 开启摄像头 "ON" 关闭摄像头 "OFF"
+
+    /**
+     * 主播全员闭麦状态
+     */
+    ANCHOR_MIC("主播全员闭麦状态"), // 开启 "ON" 关闭 "OFF"
+    ;
+    private String code;
+
+    private String msg;
+
+    EGroupDefinedDataType(String msg) {
+        this.msg = msg;
+        this.code = this.name();
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+
+    public void setMsg(String msg) {
+        this.msg = msg;
+    }
+}

+ 88 - 0
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/ImGroupController.java

@@ -1,17 +1,26 @@
 package com.yonge.cooleshow.admin.controller;
 
 
+import com.alibaba.fastjson.JSONObject;
+import com.tencentyun.TLSSigAPIv2;
 import com.yonge.cooleshow.biz.dal.dto.ImGroupSearchDto;
 import com.yonge.cooleshow.biz.dal.entity.ImGroup;
+import com.yonge.cooleshow.biz.dal.entity.ImHistoryMessage;
 import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
 import com.yonge.cooleshow.biz.dal.service.ImGroupService;
 import com.yonge.cooleshow.biz.dal.service.SysUserService;
 import com.yonge.cooleshow.common.controller.BaseController;
 import com.yonge.cooleshow.common.entity.HttpResponseResult;
+import com.yonge.cooleshow.common.redis.service.RedisCache;
+import com.yonge.toolset.utils.date.DateUtil;
+import com.yonge.toolset.utils.http.HttpUtil;
 import com.yonge.toolset.utils.validator.ValidationKit;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.validation.BindingResult;
@@ -19,6 +28,13 @@ import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
 import javax.validation.Valid;
+import java.io.File;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
 import java.util.List;
 
 /**
@@ -39,6 +55,11 @@ public class ImGroupController extends BaseController {
     @Resource
     private SysUserService sysUserService;
 
+
+
+    @Autowired
+    private RedisCache redisCache;
+
     @ApiOperation("获取群详情")
     @PostMapping(value = "/getDetail/{groupId}")
     @PreAuthorize("@pcs.hasPermissions('imGroup/detail')")
@@ -65,5 +86,72 @@ public class ImGroupController extends BaseController {
         imGroupService.quit(groupId,sysUserService.getUserId(), ClientEnum.STUDENT);
         return succeed();
     }
+
+
+
+    @GetMapping(value = "/syncImHistoryMessageTask")
+    // 融云同步即时通讯聊天记录
+    public void syncImHistoryMessageTask(String date) throws Exception {
+        if(date == null){
+            date = DateUtil.format(DateUtil.addHours(new Date(),-2), DateUtil.YEAR_MONTH_DAY_HOUR);
+        }
+        // 获取输入日期
+        SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHH");
+        Date currentDate = format.parse(date);
+
+        // 创建Calendar对象 设置为输入时间
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(currentDate);
+
+        // 将日期减少一个月
+        calendar.add(Calendar.MONTH, -1);
+
+        // 获取减少一个月后的日期
+        Date lastMonthDate = calendar.getTime();
+
+        calendar.setTime(currentDate);
+
+        //按照小时递减
+        while (currentDate.after(lastMonthDate))
+        {
+            String d = DateUtil.format(DateUtil.addHours(currentDate,0), DateUtil.YEAR_MONTH_DAY_HOUR);
+            Object o = imGroupService.historyGet(d);
+            JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(o));
+            if(jsonObject.get("code").equals(200)){
+                Boolean success = redisCache.getRedisTemplate().opsForValue().setIfAbsent("syncImHistoryMessage" + d,d);
+                if(!success){
+                    return;
+                }
+                String url = jsonObject.getString("url");
+                if(StringUtils.isEmpty(url)){
+                    return;
+                }
+                File file = new File(FileUtils.getTempDirectoryPath() + url.substring(url.lastIndexOf("/")));
+                URL url1 = new URL(url);
+                FileUtils.copyURLToFile(url1,file);
+                imGroupService.saveImHistoryMessage(new File(file.getAbsolutePath()));
+            }
+            calendar.add(Calendar.HOUR,-1);
+            currentDate = calendar.getTime();
+        }
+
+    }
+
+
+    @ApiOperation("IM导入消息")
+    @PostMapping(value = "/ImportIM")
+    public void ImportIM() throws Exception {
+
+        //获取融云消息
+        List<ImHistoryMessage> info = imGroupService.getRongYunInfo();
+
+        imGroupService.importInfo(info);
+
+
+    }
+
+
+
+
 }
 

+ 245 - 0
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/open/ImController.java

@@ -0,0 +1,245 @@
+package com.yonge.cooleshow.admin.controller.open;
+
+import com.alibaba.fastjson.JSON;
+import com.google.common.collect.Lists;
+import com.microsvc.toolkit.middleware.live.LivePluginContext;
+import com.microsvc.toolkit.middleware.live.LivePluginService;
+import com.microsvc.toolkit.middleware.live.impl.TencentCloudLivePlugin;
+import com.yonge.cooleshow.admin.io.request.course.CourseRelationVo;
+import com.yonge.cooleshow.admin.io.request.im.IMNotifyMessageVO;
+import com.yonge.cooleshow.admin.io.request.im.UserFriendInfoVO;
+import com.yonge.cooleshow.biz.dal.dto.TencentData;
+import com.yonge.cooleshow.biz.dal.dto.TencentImCallbackResult;
+import com.yonge.cooleshow.biz.dal.entity.ImUserStateSync;
+import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
+import com.yonge.cooleshow.biz.dal.enums.im.ETencentImCallbackCommand;
+import com.yonge.cooleshow.biz.dal.service.CourseRelationMusicAlbumService;
+import com.yonge.cooleshow.biz.dal.service.ImUserFriendService;
+import com.yonge.cooleshow.biz.dal.service.LiveRoomService;
+import com.yonge.cooleshow.biz.dal.wrapper.im.CustomerService;
+import com.yonge.cooleshow.common.controller.BaseController;
+import com.yonge.cooleshow.common.entity.HttpResponseResult;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * 用户通讯录表(ImUserFriend)表控制层
+ *
+ * @author zx
+ * @since 2022-03-22 10:45:59
+ */
+@Api(tags = "用户通讯录表")
+@RestController
+@RequestMapping("/open")
+@Slf4j
+public class ImController extends BaseController {
+
+    /**
+     * 服务对象
+     */
+    @Resource
+    private ImUserFriendService imUserFriendService;
+    @Autowired
+    private CourseRelationMusicAlbumService courseRelationMusicAlbumService;
+
+    @Autowired
+    private LivePluginContext livePluginContext;
+
+    @Autowired
+    private LiveRoomService liveRoomService;
+
+    @ApiOperation("新用户添加客服")
+    @PostMapping(value = "/im/customerService")
+    public HttpResponseResult<Boolean> customerService(@RequestBody UserFriendInfoVO info) {
+
+        if (info.invalidRequestParam()) {
+            return failed("无效的请求参数");
+        }
+
+        // 新用户自动绑定系统客服
+        int ret = imUserFriendService.registerUserBindCustomerService(info.getUserId(),
+                info.getFriendIds(), ClientEnum.valueOf(info.getClientType()));
+
+        return succeed(ret > 0);
+    }
+
+    /**
+     * 发送系统客服消息
+     * @param info IMNotifyMessageVO
+     * @return HttpResponseResult<Boolean>
+     */
+    @PostMapping(value = "/im/message")
+    public HttpResponseResult<Boolean> sendSysCustomerServiceMessage(@RequestBody IMNotifyMessageVO info) {
+
+        if (info.invalidRequestParam()) {
+            return failed("无效的请求参数");
+        }
+
+        // 发送客服通知消息
+        imUserFriendService.sendCustomerServiceNotifyMessage(info.getSender(),
+                CustomerService.NotifyMessage.from(info.jsonString()));
+
+        return succeed();
+    }
+
+    /**
+     * 同步用户购买课程赠送数据
+     * @param query CourseRelationVo.CourseRelationQuery
+     * @return HttpResponseResult<Boolean>
+     */
+    @ApiOperation("已购买课程赠送同步")
+    @PostMapping(value = "/course/sync")
+    public HttpResponseResult<Boolean> syncUserPurchaseRelationMusicAlbum(@RequestBody CourseRelationVo.CourseRelationQuery query) {
+
+        if (Objects.isNull(query.getCourseGroupId()) || Objects.isNull(query.getCourseType())) {
+            return failed("无效的请求参数");
+        }
+
+        // 同步数据
+        courseRelationMusicAlbumService.asyncUpdateCourseRelationMusicAlbumInfo(query.getCourseGroupId(), query.getCourseType());
+
+        return succeed();
+    }
+
+
+    @ApiOperation("腾讯im 回调接口")
+    @PostMapping(value = "/tencentImCallback")
+    public TencentImCallbackResult tencentImCallback(@RequestBody String body, HttpServletRequest request) {
+        log.info("tencentImCallback body:{}", body);
+
+        LivePluginService pluginService = livePluginContext.getPluginService(TencentCloudLivePlugin.PLUGIN_NAME);
+        String appKey = pluginService.getLiveRoomConfig().getAppKey();
+
+        log.info("tencentImCallback request param:{}", JSON.toJSONString(request.getParameterMap()));
+
+        List<String> sdkList = Arrays.asList(request.getParameterValues("SdkAppid"));
+        if (sdkList == null || sdkList.size() == 0) {
+            log.error("tencentImCallback sdkAppid is null");
+            return new TencentImCallbackResult();
+        }
+        if (!sdkList.contains(appKey)) {
+            log.error("tencentImCallback sdkAppid is not match");
+            return new TencentImCallbackResult();
+        }
+
+        String clientIP = request.getParameter("ClientIP");
+        String optPlatform = request.getParameter("OptPlatform");
+
+
+        if(request.getParameter("CallbackCommand").equals(ETencentImCallbackCommand.GROUP_CALLBACKONMEMBERSTATECHANGE.getCommand())) {
+            // 直播群成员在线状态回调
+            TencentData.CallbackOnMemberStateChange callbackOnMemberStateChange = TencentData.CallbackOnMemberStateChange.toObject(
+                    body);
+
+            log.debug("callbackOnMemberStateChange: {}", callbackOnMemberStateChange);
+            callbackOnMemberStateChange.setClientIP(clientIP);
+            callbackOnMemberStateChange.setOptPlatform(optPlatform);
+            // 直播间成员状态变更
+            liveRoomService.callbackOnMemberStateChange(callbackOnMemberStateChange);
+        } else if(request.getParameter("CallbackCommand").equals(ETencentImCallbackCommand.GROUP_CALLBACKAFTERMEMBEREXIT.getCommand())) {
+            // 群成员离开之后回调
+            TencentData.CallbackAfterMemberExit callbackAfterMemberExit = TencentData.CallbackAfterMemberExit.toObject(
+                    body);
+
+            log.debug("callbackAfterMemberExit: {}", callbackAfterMemberExit);
+            callbackAfterMemberExit.setClientIP(clientIP);
+            callbackAfterMemberExit.setOptPlatform(optPlatform);
+
+            // 直播间成员状态变更
+            liveRoomService.callbackAfterMemberExit(callbackAfterMemberExit);
+        } else if(request.getParameter("CallbackCommand").equals(ETencentImCallbackCommand.GROUP_CALLBACKAFTERNEWMEMBERJOIN.getCommand())) {
+            // 新成员入群之后回调
+            TencentData.CallbackAfterNewMemberJoin callbackAfterNewMemberJoin = TencentData.CallbackAfterNewMemberJoin.toObject(
+                    body);
+
+            log.debug("CallbackAfterNewMemberJoin: {}", callbackAfterNewMemberJoin);
+            callbackAfterNewMemberJoin.setClientIP(clientIP);
+            callbackAfterNewMemberJoin.setOptPlatform(optPlatform);
+
+            // 直播间成员状态变更
+            liveRoomService.callbackAfterNewMemberJoin(callbackAfterNewMemberJoin);
+        }
+
+
+        return new TencentImCallbackResult();
+    }
+
+
+    @ApiOperation("腾讯云直播-推流 回调接口")
+    @PostMapping(value = "/tencentStreamEventCallback")
+    public TencentData.StreamEventCallbackResult tencentStreamEventCallback(@RequestBody String body) {
+
+        log.info("tencentStreamEventCallback body:{}", body);
+
+        TencentData.CallbackStreamStateEvent event = TencentData.CallbackStreamStateEvent.from(body);
+
+
+        ImUserStateSync imUserState = new ImUserStateSync();
+        imUserState.setUserid(getSpeakerId(event.getStreamId()).toString());
+        // 断流事件通知
+        if (event.getEventType() == 0) {
+            imUserState.setStatus("3");
+        }
+
+        // 推流事件通知
+        if (event.getEventType() == 1) {
+
+            imUserState.setStatus("0");
+        }
+        liveRoomService.opsRoom(Lists.newArrayList(imUserState));
+
+        return TencentData.StreamEventCallbackResult.builder().code(0).build();
+    }
+
+    private Integer getSpeakerId(String streamId) {
+        return Integer.parseInt(streamId.split("_")[1]);
+    }
+
+    private String getRoomUid(String streamId) {
+        return streamId.split("_")[0];
+    }
+
+    @ApiOperation("腾讯云直播-录制 回调接口")
+    @PostMapping(value = "/tencentStreamRecordCallback")
+    public TencentData.StreamEventCallbackResult tencentStreamRecordCallback(@RequestBody String body) {
+
+        log.info("tencentStreamRecordCallback body:{}", body);
+
+        TencentData.CallbackSteamRecordEvent event = TencentData.CallbackSteamRecordEvent.from(body);
+
+        // 直播录制事件通知
+        if (event.getStreamId().startsWith("LIVE")) {
+            log.info("taskId={}, url={}", event.getTaskId(), event.getVideoUrl());
+
+            // 生成直播录制信息
+            liveRoomService.createLiveRoomVideoRecord(event);
+        }
+
+        return TencentData.StreamEventCallbackResult.builder().code(0).build();
+    }
+
+    @ApiOperation("腾讯云直播-推流异常 回调接口")
+    @PostMapping(value = "/tencentStreamExceptionCallback")
+    public TencentData.StreamEventCallbackResult tencentStreamExceptionCallback(@RequestBody String body) {
+
+        log.info("tencentStreamExceptionCallback body:{}", body);
+
+        return TencentData.StreamEventCallbackResult.builder().code(0).build();
+    }
+
+}
+

+ 0 - 95
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/open/ImUserFriendController.java

@@ -1,95 +0,0 @@
-package com.yonge.cooleshow.admin.controller.open;
-
-import com.yonge.cooleshow.admin.io.request.course.CourseRelationVo;
-import com.yonge.cooleshow.admin.io.request.im.IMNotifyMessageVO;
-import com.yonge.cooleshow.admin.io.request.im.UserFriendInfoVO;
-import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
-import com.yonge.cooleshow.biz.dal.service.CourseRelationMusicAlbumService;
-import com.yonge.cooleshow.biz.dal.service.ImUserFriendService;
-import com.yonge.cooleshow.biz.dal.wrapper.im.CustomerService;
-import com.yonge.cooleshow.common.controller.BaseController;
-import com.yonge.cooleshow.common.entity.HttpResponseResult;
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-import javax.annotation.Resource;
-import java.util.Objects;
-
-/**
- * 用户通讯录表(ImUserFriend)表控制层
- *
- * @author zx
- * @since 2022-03-22 10:45:59
- */
-@Api(tags = "用户通讯录表")
-@RestController
-@RequestMapping("/open")
-public class ImUserFriendController extends BaseController {
-
-    /**
-     * 服务对象
-     */
-    @Resource
-    private ImUserFriendService imUserFriendService;
-    @Autowired
-    private CourseRelationMusicAlbumService courseRelationMusicAlbumService;
-
-    @ApiOperation("新用户添加客服")
-    @PostMapping(value = "/im/customerService")
-    public HttpResponseResult<Boolean> customerService(@RequestBody UserFriendInfoVO info) {
-
-        if (info.invalidRequestParam()) {
-            return failed("无效的请求参数");
-        }
-
-        // 新用户自动绑定系统客服
-        int ret = imUserFriendService.registerUserBindCustomerService(info.getUserId(),
-                info.getFriendIds(), ClientEnum.valueOf(info.getClientType()));
-
-        return succeed(ret > 0);
-    }
-
-    /**
-     * 发送系统客服消息
-     * @param info IMNotifyMessageVO
-     * @return HttpResponseResult<Boolean>
-     */
-    @PostMapping(value = "/im/message")
-    public HttpResponseResult<Boolean> sendSysCustomerServiceMessage(@RequestBody IMNotifyMessageVO info) {
-
-        if (info.invalidRequestParam()) {
-            return failed("无效的请求参数");
-        }
-
-        // 发送客服通知消息
-        imUserFriendService.sendCustomerServiceNotifyMessage(info.getSender(),
-                CustomerService.NotifyMessage.from(info.jsonString()));
-
-        return succeed();
-    }
-
-    /**
-     * 同步用户购买课程赠送数据
-     * @param query CourseRelationVo.CourseRelationQuery
-     * @return HttpResponseResult<Boolean>
-     */
-    @ApiOperation("已购买课程赠送同步")
-    @PostMapping(value = "/course/sync")
-    public HttpResponseResult<Boolean> syncUserPurchaseRelationMusicAlbum(@RequestBody CourseRelationVo.CourseRelationQuery query) {
-
-        if (Objects.isNull(query.getCourseGroupId()) || Objects.isNull(query.getCourseType())) {
-            return failed("无效的请求参数");
-        }
-
-        // 同步数据
-        courseRelationMusicAlbumService.asyncUpdateCourseRelationMusicAlbumInfo(query.getCourseGroupId(), query.getCourseType());
-
-        return succeed();
-    }
-}
-

+ 16 - 0
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/task/TaskController.java

@@ -57,6 +57,9 @@ public class TaskController extends BaseController {
     @Autowired
     private UserPaymentCoreService userPaymentCoreService;
 
+    @Autowired
+    private LiveRoomService liveRoomService;
+
     /***
      * 轮询用户订单
      * @author liweifan
@@ -170,4 +173,17 @@ public class TaskController extends BaseController {
         return HttpResponseResult.succeed();
     }
 
+
+    /**
+     * 直播间销毁
+     */
+    @GetMapping("/destroyLiveRoom")
+    public HttpResponseResult<Object> destroyLiveRoom() {
+
+        // 群发消息定时
+        liveRoomService.destroyLiveRoom();
+
+        return HttpResponseResult.succeed();
+    }
+
 }

+ 11 - 0
cooleshow-user/user-biz/pom.xml

@@ -108,6 +108,17 @@
             <groupId>com.microsvc.toolkit.middleware</groupId>
             <artifactId>microsvc-middleware-payment</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.microsvc.toolkit.middleware</groupId>
+            <artifactId>microsvc-middleware-live</artifactId>
+        </dependency>
+
+
+        <dependency>
+            <groupId>com.microsvc.toolkit.middleware</groupId>
+            <artifactId>microsvc-middleware-im</artifactId>
+        </dependency>
+
     </dependencies>
 
     <build>

+ 24 - 24
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/config/RongCloudConfig.java

@@ -1,24 +1,24 @@
-package com.yonge.cooleshow.biz.dal.config;
-
-import io.rong.RongCloud;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Primary;
-
-@Configuration
-public class RongCloudConfig {
-
-    @Value("${cn.rongcloud.im.appkey}")
-    private String appKey;
-    @Value("${cn.rongcloud.im.secret}")
-    private String appSecret;
-
-    public static RongCloud rongCloud;
-
-    @Bean
-    @Primary
-    public void getRongCloud() {
-        rongCloud = RongCloud.getInstance(appKey,appSecret);
-    }
-}
+//package com.yonge.cooleshow.biz.dal.config;
+//
+//import io.rong.RongCloud;
+//import org.springframework.beans.factory.annotation.Value;
+//import org.springframework.context.annotation.Bean;
+//import org.springframework.context.annotation.Configuration;
+//import org.springframework.context.annotation.Primary;
+//
+//@Configuration
+//public class RongCloudConfig {
+//
+//    @Value("${cn.rongcloud.im.appkey}")
+//    private String appKey;
+//    @Value("${cn.rongcloud.im.secret}")
+//    private String appSecret;
+//
+//    public static RongCloud rongCloud;
+//
+//    @Bean
+//    @Primary
+//    public void getRongCloud() {
+//        rongCloud = RongCloud.getInstance(appKey,appSecret);
+//    }
+//}

+ 9 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dao/ImGroupDao.java

@@ -4,9 +4,11 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.yonge.cooleshow.biz.dal.dto.ImGroupResultDto;
 import com.yonge.cooleshow.biz.dal.dto.ImGroupSearchDto;
 import com.yonge.cooleshow.biz.dal.entity.ImGroup;
+import com.yonge.cooleshow.biz.dal.entity.ImHistoryMessage;
 import org.apache.ibatis.annotations.Param;
 
 import java.util.List;
+import java.util.Set;
 
 /**
  * 即时通讯群组(ImGroup)表数据库访问层
@@ -23,5 +25,12 @@ public interface ImGroupDao extends BaseMapper<ImGroup> {
 
     //更新群成员数量
     void updateMemberNum(@Param("groupId") String groupId);
+
+    //批量插入历史数据
+    void batchInsert(@Param("list") List<ImHistoryMessage> historyMessages);
+
+    //获取融云数据
+    List<ImHistoryMessage> selectAll();
+
 }
 

+ 1 - 1
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dao/ImGroupMemberDao.java

@@ -32,7 +32,7 @@ public interface ImGroupMemberDao extends BaseMapper<ImGroupMember> {
     * @author zx
     * @date 2022/3/23 15:17
     */
-    List<GroupMember> queryGroupMember(@Param("groupId") String groupId);
+    List<ImGroupMember> queryGroupMember(@Param("groupId") String groupId);
 
     /**
      * @description: 获取群成员列表

+ 41 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/LiveRoomStatus.java

@@ -0,0 +1,41 @@
+package com.yonge.cooleshow.biz.dal.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+
+/**
+ * Description
+ *
+ * @author liujunchi
+ * @date 2023-03-09
+ */
+@Data
+@ApiModel(value = "更新直播间状态")
+public class LiveRoomStatus {
+
+    @ApiModelProperty(value = "房间uid",required = true)
+    @NotBlank(message = "房间号不能为空")
+    private String roomUid;
+
+    @ApiModelProperty(value = "主播状态: 0离开;1在播")
+    private Integer speakerStatus;
+
+    @ApiModelProperty(value = "推流状态: 0 暂停; 1在播")
+    private Integer pushStatus;
+
+    @ApiModelProperty(value = "禁言状态: 0 取消;1禁言")
+    private Integer banStatus;
+
+    @ApiModelProperty(value = "主播摄像头状态 1:开启 0:关闭")
+    private Integer cameraStatus;
+
+    @ApiModelProperty(value = "直播时长")
+    private Integer liveTotalTime;
+
+    @ApiModelProperty(value = "全员闭麦状态 1:开启 0:关闭")
+    private Integer micStatus;
+
+}

+ 521 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/TencentData.java

@@ -0,0 +1,521 @@
+package com.yonge.cooleshow.biz.dal.dto;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONArray;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.annotation.JSONField;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.yonge.cooleshow.biz.dal.enums.im.ETencentGroupType;
+import com.yonge.cooleshow.biz.dal.enums.im.ETencentImCallbackCommand;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+import java.sql.Timestamp;
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+/**
+ * Description
+ *
+ * @author liujunchi
+ * @date 2023-03-06
+ */
+@NoArgsConstructor
+@Data
+public class TencentData {
+
+    // 群成员离开之后回调对象
+    @NoArgsConstructor
+    @Data
+    public static class CallbackAfterMemberExit {
+
+
+        private String clientIP;
+
+        private String optPlatform;
+
+        // 回调命令
+        private ETencentImCallbackCommand callbackCommand;
+        // 群组 ID
+        private String groupId;
+        // 群组类型
+        private ETencentGroupType type;
+        // 成员离开方式:Kicked-被踢;Quit-主动退群
+        private String exitType;
+        // 操作者
+        private String operatorAccount;
+        // 离开群的成员列表
+        private List<MemberListDTO> exitMemberList;
+        //毫秒级别,事件触发时间戳
+        private LocalDateTime eventTime;
+
+        public static CallbackAfterMemberExit toObject(String jsonString) {
+
+            CallbackAfterMemberExit res = new CallbackAfterMemberExit();
+
+            JSONObject jsonObject = JSON.parseObject(jsonString);
+            res.setCallbackCommand(ETencentImCallbackCommand.valueOf(jsonObject.getString("CallbackCommand").replace(".","_").toUpperCase(
+                Locale.ROOT)));
+            res.setGroupId(jsonObject.getString("GroupId"));
+            res.setType(jsonObject.getObject("Type",ETencentGroupType.class));
+            res.setExitType(jsonObject.getString("ExitType"));
+            res.setOperatorAccount(jsonObject.getString("Operator_Account"));
+
+            JSONArray exitMemberList = jsonObject.getJSONArray("ExitMemberList");
+            if (!exitMemberList.isEmpty()) {
+                List<MemberListDTO> memberListDTOS = new ArrayList<>();
+                for (int i = 0; i < exitMemberList.size(); i++) {
+                    MemberListDTO memberListDTO = new MemberListDTO();
+                    JSONObject exitMemberListJSONObject = exitMemberList.getJSONObject(i);
+                    String member_account = exitMemberListJSONObject.getString("Member_Account");
+                    memberListDTO.setMemberAccount(member_account);
+                    memberListDTOS.add(memberListDTO);
+                }
+                res.setExitMemberList(memberListDTOS);
+            }
+
+
+
+            res.setEventTime(((Timestamp)jsonObject.getTimestamp("EventTime")).toLocalDateTime());
+
+            return res;
+        }
+    }
+
+    @NoArgsConstructor
+    @Data
+    public static class MemberListDTO {
+        private String memberAccount;
+    }
+
+    // 直播群成员在线状态回调
+    @NoArgsConstructor
+    @Data
+    public static class CallbackOnMemberStateChange {
+
+        // 客户端平台,对应不同的平台类型,可能的取值有:
+        // RESTAPI(使用 REST API 发送请求)、Web(使用 Web SDK 发送请求)、
+        // Android、iOS、Windows、Mac、iPad、Unknown(使用未知类型的设备发送请求)
+        private String clientIP;
+
+        private String optPlatform;
+        // 回调命令
+        private ETencentImCallbackCommand callbackCommand;
+        // 群组 ID
+        private String groupId;
+        // 事件类型:Offline - 掉线、Online - 重新上线
+        private String eventType;
+        // 离开群的成员列表
+        private List<MemberListDTO> memberList;
+
+        public static CallbackOnMemberStateChange toObject(String jsonString) {
+
+            CallbackOnMemberStateChange res = new CallbackOnMemberStateChange();
+
+            JSONObject jsonObject = JSON.parseObject(jsonString);
+            res.setCallbackCommand(ETencentImCallbackCommand.valueOf(jsonObject.getString("CallbackCommand").replace(".","_").toUpperCase(
+                Locale.ROOT)));
+            res.setGroupId(jsonObject.getString("GroupId"));
+
+            JSONArray exitMemberList = jsonObject.getJSONArray("MemberList");
+            if (!exitMemberList.isEmpty()) {
+                List<MemberListDTO> memberListDTOS = new ArrayList<>();
+                for (int i = 0; i < exitMemberList.size(); i++) {
+                    MemberListDTO memberListDTO = new MemberListDTO();
+                    JSONObject exitMemberListJSONObject = exitMemberList.getJSONObject(i);
+                    String member_account = exitMemberListJSONObject.getString("Member_Account");
+                    memberListDTO.setMemberAccount(member_account);
+                    memberListDTOS.add(memberListDTO);
+                }
+                res.setMemberList(memberListDTOS);
+            }
+
+            res.setEventType(jsonObject.getString("EventType"));
+
+            return res;
+        }
+    }
+
+    @NoArgsConstructor
+    @Data
+    public static class CallbackAfterNewMemberJoin {
+
+        private String clientIP;
+
+        private String optPlatform;
+        // 回调命令
+        private ETencentImCallbackCommand callbackCommand;
+        // 群组 ID
+        private String groupId;
+        // 群组类型
+        private ETencentGroupType type;
+        // 入群方式:Apply(申请入群);Invited(邀请入群)
+        private String joinType;
+        // 操作者成员
+        private String operatorAccount;
+        // 新入群成员列表
+        private List<MemberListDTO> newMemberList;
+        //毫秒级别,事件触发时间戳
+        private LocalDateTime eventTime;
+
+        public static CallbackAfterNewMemberJoin toObject(String jsonString) {
+
+            CallbackAfterNewMemberJoin res = new CallbackAfterNewMemberJoin();
+
+            JSONObject jsonObject = JSON.parseObject(jsonString);
+            res.setCallbackCommand(ETencentImCallbackCommand.valueOf(jsonObject.getString("CallbackCommand").replace(".","_").toUpperCase(
+                Locale.ROOT)));
+            res.setGroupId(jsonObject.getString("GroupId"));
+            res.setType(jsonObject.getObject("Type",ETencentGroupType.class));
+            res.setJoinType(jsonObject.getString("JoinType"));
+            res.setOperatorAccount(jsonObject.getString("Operator_Account"));
+
+            JSONArray exitMemberList = jsonObject.getJSONArray("NewMemberList");
+            if (!exitMemberList.isEmpty()) {
+                List<MemberListDTO> memberListDTOS = new ArrayList<>();
+                for (int i = 0; i < exitMemberList.size(); i++) {
+                    MemberListDTO memberListDTO = new MemberListDTO();
+                    JSONObject exitMemberListJSONObject = exitMemberList.getJSONObject(i);
+                    String member_account = exitMemberListJSONObject.getString("Member_Account");
+                    memberListDTO.setMemberAccount(member_account);
+                    memberListDTOS.add(memberListDTO);
+                }
+                res.setNewMemberList(memberListDTOS);
+            }
+
+
+            res.setEventTime(((Timestamp)jsonObject.getTimestamp("EventTime")).toLocalDateTime());
+
+            return res;
+        }
+    }
+
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel("推断流事件回调通知")
+    public static class CallbackStreamStateEvent implements Serializable {
+
+
+        @ApiModelProperty("推流域名")
+        @JSONField(name = "app")
+        private String app;
+
+        @ApiModelProperty("用户 APPID")
+        @JSONField(name = "appid")
+        private Integer appid;
+
+        @ApiModelProperty("推流路径")
+        @JSONField(name = "appname")
+        private String appname;
+
+        @ApiModelProperty("同直播流名称")
+        @JSONField(name = "channel_id")
+        private String channelId;
+
+        @ApiModelProperty("断流事件通知推流时长,单位毫秒")
+        @JSONField(name = "push_duration")
+        private String pushDuration;
+
+        @ApiModelProperty("推断流错误码")
+        @JSONField(name = "errcode")
+        private Integer errcode;
+
+        @ApiModelProperty("推断流错误描述")
+        @JSONField(name = "errmsg")
+        private String errmsg;
+
+        @ApiModelProperty("事件消息产生的 UNIX 时间戳")
+        @JSONField(name = "event_time")
+        private Integer eventTime;
+
+        @ApiModelProperty("事件类型: 推流:1;断流:0")
+        @JSONField(name = "event_type")
+        private Integer eventType;
+
+        @ApiModelProperty("判断是否为国内外推流。1-6为国内,7-200为国外")
+        @JSONField(name = "set_id")
+        private Integer setId;
+
+        @ApiModelProperty("直播接入点的 IP")
+        @JSONField(name = "node")
+        private String node;
+
+        @ApiModelProperty("消息序列号,标识一次推流活动,一次推流活动会产生相同序列号的推流和断流消息")
+        @JSONField(name = "sequence")
+        private String sequence;
+
+        @ApiModelProperty("直播流名称")
+        @JSONField(name = "stream_id")
+        private String streamId;
+
+        @ApiModelProperty("用户推流 URL 所带参数")
+        @JSONField(name = "stream_param")
+        private String streamParam;
+
+        @ApiModelProperty("用户推流 IP")
+        @JSONField(name = "user_ip")
+        private String userIp;
+
+        @ApiModelProperty("视频宽度,最开始推流回调的时候若视频头部信息缺失,可能为0")
+        @JSONField(name = "width")
+        private Integer width;
+
+        @ApiModelProperty("视频高度,最开始推流回调的时候若视频头部信息缺失,可能为0")
+        @JSONField(name = "height")
+        private Integer height;
+
+        @ApiModelProperty("事件通知安全签名 sign = MD5(key + t)")
+        @JSONField(name = "sign")
+        private String sign;
+
+        @ApiModelProperty("过期时间,事件通知签名过期 UNIX 时间戳")
+        @JSONField(name = "t")
+        private Integer t;
+
+        public static CallbackStreamStateEvent from(String jsonString) {
+            return JSON.parseObject(jsonString, CallbackStreamStateEvent.class);
+        }
+    }
+
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel("流录制事件回调通知")
+    public static class CallbackSteamRecordEvent implements Serializable {
+
+        @ApiModelProperty("事件类型:直播录制: 100")
+        @JSONField(name = "event_type")
+        private Integer eventType;
+
+        @ApiModelProperty("用户 APPID")
+        @JSONField(name = "appid")
+        private Integer appid;
+
+        @ApiModelProperty("推流域名")
+        @JSONField(name = "app")
+        private String app;
+
+        @ApiModelProperty("json对象字符串,用户自定义数据")
+        @JSONField(name = "callback_ext")
+        private String callbackExt;
+
+        @ApiModelProperty("推流路径")
+        @JSONField(name = "appname")
+        private String appname;
+
+        @ApiModelProperty("直播流名称")
+        @JSONField(name = "stream_id")
+        private String streamId;
+
+        @ApiModelProperty("同直播流名称")
+        @JSONField(name = "channel_id")
+        private String channelId;
+
+        @ApiModelProperty("点播 file ID,在 云点播平台 可以唯一定位一个点播视频文件")
+        @JSONField(name = "file_id")
+        private String fileId;
+
+        @ApiModelProperty("点播文件ID")
+        @JSONField(name = "record_file_id")
+        private String recordFileId;
+
+        @ApiModelProperty("文件格式: FLV,HLS,MP4,AAC")
+        @JSONField(name = "file_format")
+        private String fileFormat;
+
+        @ApiModelProperty("录制任务 ID")
+        @JSONField(name = "task_id")
+        private String taskId;
+
+        @ApiModelProperty("录制任务开始写文件的时间;不能以该值作为录制内容的开始时间,录制内容的开始时间 = end_time – duration")
+        @JSONField(name = "start_time")
+        private Integer startTime;
+
+        @ApiModelProperty("录制任务结束写文件的时间")
+        @JSONField(name = "end_time")
+        private Integer endTime;
+
+        @ApiModelProperty("录制任务开始写文件的时间,微秒部分")
+        @JSONField(name = "start_time_usec")
+        private Integer startTimeUsec;
+
+        @ApiModelProperty("录制任务结束写文件的时间,微秒部分")
+        @JSONField(name = "end_time_usec")
+        private Integer endTimeUsec;
+
+        @ApiModelProperty("录制文件时长,单位秒")
+        @JSONField(name = "duration")
+        private Integer duration;
+
+        @ApiModelProperty("录制文件大小,单位字节")
+        @JSONField(name = "file_size")
+        private Integer fileSize;
+
+        @ApiModelProperty("用户推流 URL 所带参数(自定义)")
+        @JSONField(name = "stream_param")
+        private String streamParam;
+
+        @ApiModelProperty("录制文件下载 URL")
+        @JSONField(name = "video_url")
+        private String videoUrl;
+
+        @ApiModelProperty("录制开始拉流收到的首帧 pts ")
+        @JSONField(name = "media_start_time")
+        private Integer mediaStartTime;
+
+        @ApiModelProperty("录制从转码拉流录制对应的码率(单位 kbps)")
+        @JSONField(name = "record_bps")
+        private Integer recordBps;
+
+        @ApiModelProperty("事件通知安全签名 sign = MD5(key + t)")
+        @JSONField(name = "sign")
+        private String sign;
+
+        @ApiModelProperty("过期时间,事件通知签名过期 UNIX 时间戳")
+        @JSONField(name = "t")
+        private Integer t;
+
+
+        public static CallbackSteamRecordEvent from(String json) {
+            return JSON.parseObject(json, CallbackSteamRecordEvent.class);
+        }
+    }
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel("流异常事件回调通知")
+    public static class CallbackStreamExceptionEvent implements Serializable {
+
+        @ApiModelProperty("用户 APPID")
+        @JSONField(name = "appid")
+        private Integer appid;
+
+        @ApiModelProperty("推流事件回调时间(单位ms)")
+        @JSONField(name = "data_time")
+        private Long dataTime;
+
+        @ApiModelProperty("推流域名")
+        @JSONField(name = "domain")
+        private String domain;
+
+        @ApiModelProperty("事件类型:推流异常:321")
+        @JSONField(name = "event_type")
+        private Integer eventType;
+
+        @JSONField(name = "interface")
+        private String interfaceX;
+        @JSONField(name = "path")
+        private String path;
+
+        @ApiModelProperty("有异常事件时,上报间隔(单位ms)")
+        @JSONField(name = "report_interval")
+        private Integer reportInterval;
+
+        @JSONField(name = "sequence")
+        private String sequence;
+
+        @ApiModelProperty("直播流名称")
+        @JSONField(name = "stream_id")
+        private String streamId;
+
+        @ApiModelProperty("用户推流 URL 所带参数")
+        @JSONField(name = "stream_param")
+        private String streamParam;
+
+        @JSONField(name = "timeout")
+        private Integer timeout;
+
+        @ApiModelProperty("详细异常事件事件组")
+        @JSONField(name = "abnormal_event")
+        private String abnormalEvent;
+
+        public static CallbackStreamExceptionEvent from(String json) {
+            return JSON.parseObject(json, CallbackStreamExceptionEvent.class);
+        }
+    }
+
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel("流事件回调结果")
+    public static class StreamEventCallbackResult implements Serializable {
+
+        @ApiModelProperty("事件响应状态")
+        private Integer code;
+    }
+
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel("腾讯云回调事件")
+    public static class TRTCEventInfo implements Serializable {
+
+        @JsonProperty("EventGroupId")
+        @ApiModelProperty("事件组")
+        private Integer eventGroupId;
+
+        @JsonProperty("EventType")
+        @ApiModelProperty("事件类型")
+        private Integer eventType;
+
+        @JsonProperty("CallbackTs")
+        @ApiModelProperty("事件时间")
+        private Long callbackTs;
+
+        @JsonProperty("EventInfo")
+        @ApiModelProperty("事件消息")
+        private EventInfo eventInfo;
+
+        public String jsonString() {
+            return JSON.toJSONString(this);
+        }
+    }
+
+    @Data
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel("事件消息")
+    public static class EventInfo implements Serializable {
+
+        @JsonProperty("RoomId")
+        @ApiModelProperty("房间号")
+        private String roomId;
+
+        @JsonProperty("EventTs")
+        @ApiModelProperty("事件发生时间")
+        private Long eventTs;
+
+        @JsonProperty("UserId")
+        @ApiModelProperty("事件发生用户")
+        private String userId;
+
+        @JsonProperty("Role")
+        @ApiModelProperty("用户角色")
+        private String role;
+
+        @JsonProperty("TerminalType")
+        @ApiModelProperty("终端类型")
+        private String terminalType;
+
+        @JsonProperty("UserType")
+        @ApiModelProperty("用户类型")
+        private String userType;
+
+        @JsonProperty("Reason")
+        @ApiModelProperty("原因")
+        private String reason;
+    }
+}

+ 40 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/TencentImCallbackResult.java

@@ -0,0 +1,40 @@
+package com.yonge.cooleshow.biz.dal.dto;
+
+/**
+ * Description
+ *
+ * @author liujunchi
+ * @date 2023-03-02
+ */
+public class TencentImCallbackResult {
+
+    private String ActionStatus = "OK";
+
+    private String ErrorInfo;
+
+    private int ErrorCode = 0;
+
+    public String getActionStatus() {
+        return ActionStatus;
+    }
+
+    public void setActionStatus(String actionStatus) {
+        ActionStatus = actionStatus;
+    }
+
+    public String getErrorInfo() {
+        return ErrorInfo;
+    }
+
+    public void setErrorInfo(String errorInfo) {
+        ErrorInfo = errorInfo;
+    }
+
+    public int getErrorCode() {
+        return ErrorCode;
+    }
+
+    public void setErrorCode(int errorCode) {
+        ErrorCode = errorCode;
+    }
+}

+ 3 - 191
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/CourseGroup.java

@@ -7,6 +7,7 @@ import com.baomidou.mybatisplus.annotation.TableId;
 import com.yonge.cooleshow.common.enums.YesOrNoEnum;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
 
 import java.io.Serializable;
 import java.math.BigDecimal;
@@ -18,6 +19,8 @@ import java.util.Date;
  * @author hgw
  * @since 2022-03-23 14:35:19
  */
+
+@Data
 @ApiModel(value = "course_group-课程组表")
 public class CourseGroup implements Serializable {
     @TableId(value = "id_", type = IdType.AUTO)
@@ -116,196 +119,5 @@ public class CourseGroup implements Serializable {
     @ApiModelProperty(value = "更新时间")
     private Date updatedTime;
 
-    public String getReason() {
-        return reason;
-    }
-
-    public void setReason(String reason) {
-        this.reason = reason;
-    }
-
-    public Long getId() {
-        return id;
-    }
-
-    public void setId(Long id) {
-        this.id = id;
-    }
-
-    public String getType() {
-        return type;
-    }
-
-    public void setType(String type) {
-        this.type = type;
-    }
-
-    public Long getTeacherId() {
-        return teacherId;
-    }
-
-    public void setTeacherId(Long teacherId) {
-        this.teacherId = teacherId;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public void setName(String name) {
-        this.name = name;
-    }
-
-    public Long getSubjectId() {
-        return subjectId;
-    }
-
-    public void setSubjectId(Long subjectId) {
-        this.subjectId = subjectId;
-    }
-
-    public Integer getSingleCourseMinutes() {
-        return singleCourseMinutes;
-    }
-
-    public void setSingleCourseMinutes(Integer singleCourseMinutes) {
-        this.singleCourseMinutes = singleCourseMinutes;
-    }
-
-    public Integer getCourseNum() {
-        return courseNum;
-    }
-
-    public void setCourseNum(Integer courseNum) {
-        this.courseNum = courseNum;
-    }
-
-    public String getCourseIntroduce() {
-        return courseIntroduce;
-    }
-
-    public void setCourseIntroduce(String courseIntroduce) {
-        this.courseIntroduce = courseIntroduce;
-    }
-
-    public BigDecimal getCoursePrice() {
-        return coursePrice;
-    }
-
-    public void setCoursePrice(BigDecimal coursePrice) {
-        this.coursePrice = coursePrice;
-    }
-
-    public String getStatus() {
-        return status;
-    }
-
-    public void setStatus(String status) {
-        this.status = status;
-    }
-
-    public Date getSalesStartDate() {
-        return salesStartDate;
-    }
-
-    public void setSalesStartDate(Date salesStartDate) {
-        this.salesStartDate = salesStartDate;
-    }
-
-    public Date getSalesEndDate() {
-        return salesEndDate;
-    }
-
-    public void setSalesEndDate(Date salesEndDate) {
-        this.salesEndDate = salesEndDate;
-    }
-
-    public String getBackgroundPic() {
-        return backgroundPic;
-    }
-
-    public void setBackgroundPic(String backgroundPic) {
-        this.backgroundPic = backgroundPic;
-    }
-
-    public Integer getMixStudentNum() {
-        return mixStudentNum;
-    }
-
-    public void setMixStudentNum(Integer mixStudentNum) {
-        this.mixStudentNum = mixStudentNum;
-    }
-
-    public Integer getPreStudentNum() {
-        return preStudentNum;
-    }
-
-    public void setPreStudentNum(Integer preStudentNum) {
-        this.preStudentNum = preStudentNum;
-    }
-
-    public String getImGroupId() {
-        return imGroupId;
-    }
-
-    public void setImGroupId(String imGroupId) {
-        this.imGroupId = imGroupId;
-    }
-
-    public Date getCourseStartTime() {
-        return courseStartTime;
-    }
-
-    public void setCourseStartTime(Date courseStartTime) {
-        this.courseStartTime = courseStartTime;
-    }
-
-    public Long getCreatedBy() {
-        return createdBy;
-    }
-
-    public void setCreatedBy(Long createdBy) {
-        this.createdBy = createdBy;
-    }
-
-    public Date getCreatedTime() {
-        return createdTime;
-    }
-
-    public void setCreatedTime(Date createdTime) {
-        this.createdTime = createdTime;
-    }
-
-    public Long getUpdatedBy() {
-        return updatedBy;
-    }
-
-    public void setUpdatedBy(Long updatedBy) {
-        this.updatedBy = updatedBy;
-    }
-
-    public Date getUpdatedTime() {
-        return updatedTime;
-    }
-
-    public void setUpdatedTime(Date updatedTime) {
-        this.updatedTime = updatedTime;
-    }
-
-    public Integer getCompleteCourseNum() {
-        return completeCourseNum;
-    }
-
-    public void setCompleteCourseNum(Integer completeCourseNum) {
-        this.completeCourseNum = completeCourseNum;
-    }
-
-    public YesOrNoEnum getAuditVersion() {
-        return auditVersion;
-    }
-
-    public void setAuditVersion(YesOrNoEnum auditVersion) {
-        this.auditVersion = auditVersion;
-    }
 }
 

+ 9 - 220
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/CourseSchedule.java

@@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
 
 import java.io.Serializable;
 import java.util.Date;
@@ -15,6 +16,7 @@ import java.util.Date;
  * @author hgw
  * @since 2022-03-23 14:35:20
  */
+@Data
 @ApiModel(value = "course_schedule-老师课程表")
 public class CourseSchedule implements Serializable {
     @TableId(value = "id_", type = IdType.AUTO)
@@ -25,6 +27,13 @@ public class CourseSchedule implements Serializable {
     @ApiModelProperty(value = "课程组id_")
     private Long courseGroupId;
 
+
+
+    @TableField("room_uid_")
+    @ApiModelProperty(value = "房间编号 直播课直播房间号")
+    private String roomUid;
+
+
     @TableField("type_")
     @ApiModelProperty(value = "类型 practice陪练课 live直播课  CourseScheduleEnum")
     private String type;
@@ -85,225 +94,5 @@ public class CourseSchedule implements Serializable {
     @ApiModelProperty(value = "更新时间")
     private Date updatedTime;
 
-    public Integer getSingleCourseTime() {
-        return singleCourseTime;
-    }
-
-    public void setSingleCourseTime(Integer singleCourseTime) {
-        this.singleCourseTime = singleCourseTime;
-    }
-
-    public static CourseSchedule build() {
-        return new CourseSchedule();
-    }
-
-    public Long getId() {
-        return id;
-    }
-
-    public void setId(Long id) {
-        this.id = id;
-    }
-
-    public CourseSchedule id(Long id) {
-        this.id = id;
-        return this;
-    }
-
-    public String getType() {
-        return type;
-    }
-
-    public void setType(String type) {
-        this.type = type;
-    }
-
-    public CourseSchedule type(String type) {
-        this.type = type;
-        return this;
-    }
-
-    public Long getTeacherId() {
-        return teacherId;
-    }
-
-    public void setTeacherId(Long teacherId) {
-        this.teacherId = teacherId;
-    }
-
-    public CourseSchedule teacherId(Long teacherId) {
-        this.teacherId = teacherId;
-        return this;
-    }
-
-    public Date getClassDate() {
-        return classDate;
-    }
-
-    public void setClassDate(Date classDate) {
-        this.classDate = classDate;
-    }
-
-    public CourseSchedule classDate(Date classDate) {
-        this.classDate = classDate;
-        return this;
-    }
-
-    public String getStatus() {
-        return status;
-    }
-
-    public void setStatus(String status) {
-        this.status = status;
-    }
-
-    public CourseSchedule status(String status) {
-        this.status = status;
-        return this;
-    }
-
-    public Long getCourseGroupId() {
-        return courseGroupId;
-    }
-
-    public void setCourseGroupId(Long courseGroupId) {
-        this.courseGroupId = courseGroupId;
-    }
-
-    public CourseSchedule courseGroupId(Long courseGroupId) {
-        this.courseGroupId = courseGroupId;
-        return this;
-    }
-
-    public Integer getClassNum() {
-        return classNum;
-    }
-
-    public void setClassNum(Integer classNum) {
-        this.classNum = classNum;
-    }
-
-    public CourseSchedule classNum(Integer classNum) {
-        this.classNum = classNum;
-        return this;
-    }
-
-    public Date getStartTime() {
-        return startTime;
-    }
-
-    public void setStartTime(Date startTime) {
-        this.startTime = startTime;
-    }
-
-    public CourseSchedule startTime(Date startTime) {
-        this.startTime = startTime;
-        return this;
-    }
-
-    public Date getEndTime() {
-        return endTime;
-    }
-
-    public void setEndTime(Date endTime) {
-        this.endTime = endTime;
-    }
-
-    public CourseSchedule endTime(Date endTime) {
-        this.endTime = endTime;
-        return this;
-    }
-
-    public Integer getLock() {
-        return lock;
-    }
-
-    public void setLock(Integer lock) {
-        this.lock = lock;
-    }
-
-    public CourseSchedule lock(Integer lock) {
-        this.lock = lock;
-        return this;
-    }
-
-    public Date getLockTime() {
-        return lockTime;
-    }
-
-    public void setLockTime(Date lockTime) {
-        this.lockTime = lockTime;
-    }
-
-    public CourseSchedule lockTime(Date lockTime) {
-        this.lockTime = lockTime;
-        return this;
-    }
-
-    public Integer getExStudentNum() {
-        return exStudentNum;
-    }
-
-    public void setExStudentNum(Integer exStudentNum) {
-        this.exStudentNum = exStudentNum;
-    }
-
-    public CourseSchedule exStudentNum(Integer exStudentNum) {
-        this.exStudentNum = exStudentNum;
-        return this;
-    }
-
-    public Long getCreatedBy() {
-        return createdBy;
-    }
-
-    public void setCreatedBy(Long createdBy) {
-        this.createdBy = createdBy;
-    }
-
-    public CourseSchedule createdBy(Long createdBy) {
-        this.createdBy = createdBy;
-        return this;
-    }
-
-    public Date getCreatedTime() {
-        return createdTime;
-    }
-
-    public void setCreatedTime(Date createdTime) {
-        this.createdTime = createdTime;
-    }
-
-    public CourseSchedule createdTime(Date createdTime) {
-        this.createdTime = createdTime;
-        return this;
-    }
-
-    public Long getUpdatedBy() {
-        return updatedBy;
-    }
-
-    public void setUpdatedBy(Long updatedBy) {
-        this.updatedBy = updatedBy;
-    }
-
-    public CourseSchedule updatedBy(Long updatedBy) {
-        this.updatedBy = updatedBy;
-        return this;
-    }
-
-    public Date getUpdatedTime() {
-        return updatedTime;
-    }
-
-    public void setUpdatedTime(Date updatedTime) {
-        this.updatedTime = updatedTime;
-    }
-
-    public CourseSchedule updatedTime(Date updatedTime) {
-        this.updatedTime = updatedTime;
-        return this;
-    }
-
 }
 

+ 91 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/ImHistoryMessage.java

@@ -0,0 +1,91 @@
+package com.yonge.cooleshow.biz.dal.entity;
+
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+
+/**
+ * @Author:haonan
+ * @Date:2023/8/7 15:17
+ * @Filename:ImHistoryMessage
+ */
+@Data
+@ApiModel(" ImHistoryMessage-历史消息记录")
+@TableName("im_history_message")
+public class ImHistoryMessage implements Serializable {
+
+    @ApiModelProperty("可通过 msgUID 确定消息唯一")
+    @TableId(value = "msgUID_")
+    private String msgUID;
+
+    @ApiModelProperty("发送者 ID")
+    @TableField(value = "fromUserId_")
+    private String fromUserId;
+
+    @ApiModelProperty("接收者 ID,在消息路由中为 toUserId,当发送聊天室广播消息、全量用户落地通知时此字段为空")
+    @TableField(value = "targetId_")
+    private String targetId;
+
+    @ApiModelProperty("会话类型。1(单聊会话)、2(讨论组会话)、3(群组会话)、4(聊天室会话)、5(客服会话)、6(系统通知)、7(应用公众服务)、8(公众服务)、10(超级群会话)。targetType 在 SDK 中为 ConversationType。")
+    @TableField(value = "targetType_")
+    private Integer targetType;
+
+    @ApiModelProperty("根据不同的 targetType,可能是讨论组 Id、群组 ID、超级群 ID 或聊天室 ID ,如 targetType 为 1 时可忽略 GroupId")
+    @TableField(value = "GroupId_")
+    private String groupId;
+
+    @ApiModelProperty("	超级群频道 ID。")
+    @TableField(value = "busChannel_")
+    private String busChannel;
+
+    @ApiModelProperty("消息类型,例如文本消息 RC:TxtMsg、图片消息 RC:ImgMsg")
+    @TableField(value = "classname_")
+    private String classname;
+
+    @ApiModelProperty("消息内容")
+    @TableField(value = "content_")
+    private String content;
+
+    @ApiModelProperty("消息扩展")
+    @TableField(value = "extraContent_")
+    private String extraContent;
+
+    @ApiModelProperty("消息时间")
+    @TableField(value = "dateTime_")
+    private String dateTime;
+
+    @ApiModelProperty("消息来源,包括:iOS、Android、Websocket、MiniProgram(小程序)、PC、Server。")
+    @TableField(value = "source_")
+    private String source;
+
+    @ApiModelProperty("是否被丢弃,true 为是,false 为否,只针对聊天室会话类型存在。")
+    @TableField(value = "isDiscard_")
+    private String isDiscard;
+
+    @ApiModelProperty("是否含有屏蔽敏感词,true 为含有、false 为不含有。只针对聊天室会话类型存在。")
+    @TableField(value = "isSensitiveWord_")
+    private String isSensitiveWord;
+
+    @ApiModelProperty("是否为被禁言后发送的消息,只针对聊天室会话类型存在。")
+    @TableField(value = "isForbidden_")
+    private String isForbidden;
+
+    @ApiModelProperty("	消息是否不分发,true 为不分发、false 为分发。只针对聊天室会话类型存在。")
+    @TableField(value = "isNotForward_")
+    private String isNotForward;
+
+    @ApiModelProperty("targetType 为 3 时此参数有效,显示为群组中指定接收消息的用户 ID 数组,该条消息为群组定向消息。非定向消息时内容为空,如指定的用户不在群组中内容也为空。")
+    @TableField(value = "groupUserIds_")
+    private String groupUserIds;
+
+    @ApiModelProperty("App Key")
+    @TableField(value = "appId_")
+    private String appId;
+
+}

+ 10 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/ImRoomMessage.java

@@ -13,6 +13,7 @@ public class ImRoomMessage extends BaseMessage {
     //objectName 类型-将所有人强制踢出房间
     public static final String FORCED_OFFLINE = "RC:ForcedOffline";
 
+    private String serviceProvider;
     /**
      * 消息类型
      */
@@ -33,6 +34,15 @@ public class ImRoomMessage extends BaseMessage {
      */
     private String toChatroomId;
 
+
+    public String getServiceProvider() {
+        return serviceProvider;
+    }
+
+    public void setServiceProvider(String serviceProvider) {
+        this.serviceProvider = serviceProvider;
+    }
+
     @Override
     public String getObjectName() {
         return objectName;

+ 3 - 39
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/ImUserStateSync.java

@@ -1,6 +1,7 @@
 package com.yonge.cooleshow.biz.dal.entity;
 
 import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
 
 import java.io.Serializable;
 
@@ -10,6 +11,7 @@ import java.io.Serializable;
  * @author hgw
  * Created by 2022-02-18
  */
+@Data
 public class ImUserStateSync implements Serializable {
 
     @ApiModelProperty(value = "用户 Id")
@@ -27,43 +29,5 @@ public class ImUserStateSync implements Serializable {
     @ApiModelProperty(value = "用户当前的 IP 地址及端口")
     private String clientIp;
 
-    public String getUserid() {
-        return userid;
-    }
-
-    public void setUserid(String userid) {
-        this.userid = userid;
-    }
-
-    public String getStatus() {
-        return status;
-    }
-
-    public void setStatus(String status) {
-        this.status = status;
-    }
-
-    public String getOs() {
-        return os;
-    }
-
-    public void setOs(String os) {
-        this.os = os;
-    }
-
-    public Long getTime() {
-        return time;
-    }
-
-    public void setTime(Long time) {
-        this.time = time;
-    }
-
-    public String getClientIp() {
-        return clientIp;
-    }
-
-    public void setClientIp(String clientIp) {
-        this.clientIp = clientIp;
-    }
+    private String roomUid;
 }

+ 17 - 142
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/LiveRoom.java

@@ -6,6 +6,7 @@ import com.baomidou.mybatisplus.annotation.TableField;
 import com.baomidou.mybatisplus.annotation.TableId;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
 
 import java.io.Serializable;
 import java.util.Date;
@@ -16,19 +17,20 @@ import java.util.Date;
  * @author hgw
  * @since 2022-03-23 14:35:19
  */
+@Data
 @ApiModel(value = "live_room-直播房间与课程的关系表表")
 public class LiveRoom implements Serializable {
     @TableId(value = "id_", type = IdType.AUTO)
     @ApiModelProperty(value = "主键")
     private Long id;
 
-    @TableField("course_group_id_")
-    @ApiModelProperty(value = "课程组id")
-    private Long courseGroupId;
-
-    @TableField("course_id_")
-    @ApiModelProperty(value = "课程id")
-    private Long courseId;
+//    @TableField("course_group_id_")
+//    @ApiModelProperty(value = "课程组id")
+//    private Long courseGroupId;
+//
+//    @TableField("course_id_")
+//    @ApiModelProperty(value = "课程id")
+//    private Long courseId;
 
     @TableField("speaker_id_")
     @ApiModelProperty(value = "主讲人id/老师id")
@@ -42,6 +44,14 @@ public class LiveRoom implements Serializable {
     @ApiModelProperty(value = "房间标题/最多12个字")
     private String roomTitle;
 
+    @TableField("service_provider_")
+    @ApiModelProperty(value = "服务提供方")
+    private String serviceProvider;
+
+    @TableField("video_record_")
+    @ApiModelProperty(value = "录制记录")
+    private String videoRecord;
+
     @TableField("live_start_time_")
     @ApiModelProperty(value = "直播开始时间")
     private Date liveStartTime;
@@ -86,140 +96,5 @@ public class LiveRoom implements Serializable {
     @ApiModelProperty(value = "更新时间")
     private Date updatedTime;
 
-    public Long getId() {
-        return id;
-    }
-
-    public void setId(Long id) {
-        this.id = id;
-    }
-
-    public Long getCourseGroupId() {
-        return courseGroupId;
-    }
-
-    public void setCourseGroupId(Long courseGroupId) {
-        this.courseGroupId = courseGroupId;
-    }
-
-    public Long getCourseId() {
-        return courseId;
-    }
-
-    public void setCourseId(Long courseId) {
-        this.courseId = courseId;
-    }
-
-    public Long getSpeakerId() {
-        return speakerId;
-    }
-
-    public void setSpeakerId(Long speakerId) {
-        this.speakerId = speakerId;
-    }
-
-    public String getRoomUid() {
-        return roomUid;
-    }
-
-    public void setRoomUid(String roomUid) {
-        this.roomUid = roomUid;
-    }
-
-    public String getRoomTitle() {
-        return roomTitle;
-    }
-
-    public void setRoomTitle(String roomTitle) {
-        this.roomTitle = roomTitle;
-    }
-
-    public Date getLiveStartTime() {
-        return liveStartTime;
-    }
-
-    public void setLiveStartTime(Date liveStartTime) {
-        this.liveStartTime = liveStartTime;
-    }
-
-    public Date getLiveEndTime() {
-        return liveEndTime;
-    }
-
-    public void setLiveEndTime(Date liveEndTime) {
-        this.liveEndTime = liveEndTime;
-    }
-
-    public String getLiveRemark() {
-        return liveRemark;
-    }
-
-    public void setLiveRemark(String liveRemark) {
-        this.liveRemark = liveRemark;
-    }
-
-    public Integer getLiveState() {
-        return liveState;
-    }
-
-    public void setLiveState(Integer liveState) {
-        this.liveState = liveState;
-    }
-
-    public Integer getRoomState() {
-        return roomState;
-    }
-
-    public void setRoomState(Integer roomState) {
-        this.roomState = roomState;
-    }
-
-    public String getType() {
-        return type;
-    }
-
-    public void setType(String type) {
-        this.type = type;
-    }
-
-    public Long getCreatedBy() {
-        return createdBy;
-    }
-
-    public void setCreatedBy(Long createdBy) {
-        this.createdBy = createdBy;
-    }
-
-    public Date getCreatedTime() {
-        return createdTime;
-    }
-
-    public void setCreatedTime(Date createdTime) {
-        this.createdTime = createdTime;
-    }
-
-    public Long getUpdatedBy() {
-        return updatedBy;
-    }
-
-    public void setUpdatedBy(Long updatedBy) {
-        this.updatedBy = updatedBy;
-    }
-
-    public Date getUpdatedTime() {
-        return updatedTime;
-    }
-
-    public void setUpdatedTime(Date updatedTime) {
-        this.updatedTime = updatedTime;
-    }
-
-    public String getCoverPic() {
-        return coverPic;
-    }
-
-    public void setCoverPic(String coverPic) {
-        this.coverPic = coverPic;
-    }
 }
 

+ 7 - 23
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/LiveRoomVideo.java

@@ -22,13 +22,13 @@ public class LiveRoomVideo implements Serializable {
     @ApiModelProperty(value = "主键")
     private Long id;
 
-    @TableField("course_group_id_")
-    @ApiModelProperty(value = "课程组id")
-    private Long courseGroupId;
-
-    @TableField("course_id_")
-    @ApiModelProperty(value = "课程id")
-    private Long courseId;
+//    @TableField("course_group_id_")
+//    @ApiModelProperty(value = "课程组id")
+//    private Long courseGroupId;
+//
+//    @TableField("course_id_")
+//    @ApiModelProperty(value = "课程id")
+//    private Long courseId;
 
     @TableField("room_uid_")
     @ApiModelProperty(value = "房间编号")
@@ -67,22 +67,6 @@ public class LiveRoomVideo implements Serializable {
         this.id = id;
     }
 
-    public Long getCourseGroupId() {
-        return courseGroupId;
-    }
-
-    public void setCourseGroupId(Long courseGroupId) {
-        this.courseGroupId = courseGroupId;
-    }
-
-    public Long getCourseId() {
-        return courseId;
-    }
-
-    public void setCourseId(Long courseId) {
-        this.courseId = courseId;
-    }
-
     public String getRoomUid() {
         return roomUid;
     }

+ 7 - 164
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/RoomInfoCache.java

@@ -2,6 +2,7 @@ package com.yonge.cooleshow.biz.dal.entity;
 
 import com.fasterxml.jackson.annotation.JsonFormat;
 import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
 
 import java.io.Serializable;
 import java.util.Date;
@@ -10,13 +11,14 @@ import java.util.Date;
  * @author hgw
  * Created by 2022年3月24日
  */
+@Data
 public class RoomInfoCache implements Serializable {
 
-    @ApiModelProperty(value = "课程id")
-    private Long courseId;
-
-    @ApiModelProperty(value = "课程组id")
-    private Long courseGroupId;
+//    @ApiModelProperty(value = "课程id")
+//    private Long courseId;
+//
+//    @ApiModelProperty(value = "课程组id")
+//    private Long courseGroupId;
 
     @ApiModelProperty(value = "主讲人id")
     private Long speakerId;
@@ -75,163 +77,4 @@ public class RoomInfoCache implements Serializable {
     @ApiModelProperty(value = "是否允许连麦 0允许 1不允许")
     private Integer whether_mic = 0;
 
-    public Integer getWhether_mic() {
-        return whether_mic;
-    }
-
-    public void setWhether_mic(Integer whether_mic) {
-        this.whether_mic = whether_mic;
-    }
-
-    public Integer getExpiredCloseMinute() {
-        return expiredCloseMinute;
-    }
-
-    public void setExpiredCloseMinute(Integer expiredCloseMinute) {
-        this.expiredCloseMinute = expiredCloseMinute;
-    }
-
-    public Long getCourseId() {
-        return courseId;
-    }
-
-    public void setCourseId(Long courseId) {
-        this.courseId = courseId;
-    }
-
-    public Long getCourseGroupId() {
-        return courseGroupId;
-    }
-
-    public void setCourseGroupId(Long courseGroupId) {
-        this.courseGroupId = courseGroupId;
-    }
-
-    public Long getSpeakerId() {
-        return speakerId;
-    }
-
-    public void setSpeakerId(Long speakerId) {
-        this.speakerId = speakerId;
-    }
-
-    public String getSpeakerName() {
-        return speakerName;
-    }
-
-    public void setSpeakerName(String speakerName) {
-        this.speakerName = speakerName;
-    }
-
-    public Integer getSpeakerState() {
-        return speakerState;
-    }
-
-    public void setSpeakerState(Integer speakerState) {
-        this.speakerState = speakerState;
-    }
-
-    public String getRoomUid() {
-        return roomUid;
-    }
-
-    public void setRoomUid(String roomUid) {
-        this.roomUid = roomUid;
-    }
-
-    public Date getLiveStartTime() {
-        return liveStartTime;
-    }
-
-    public void setLiveStartTime(Date liveStartTime) {
-        this.liveStartTime = liveStartTime;
-    }
-
-    public Date getLiveEndTime() {
-        return liveEndTime;
-    }
-
-    public void setLiveEndTime(Date liveEndTime) {
-        this.liveEndTime = liveEndTime;
-    }
-
-    public Date getCreateRoomTime() {
-        return createRoomTime;
-    }
-
-    public void setCreateRoomTime(Date createRoomTime) {
-        this.createRoomTime = createRoomTime;
-    }
-
-    public Integer getExpiredMinute() {
-        return expiredMinute;
-    }
-
-    public void setExpiredMinute(Integer expiredMinute) {
-        this.expiredMinute = expiredMinute;
-    }
-
-    public Date getJoinRoomTime() {
-        return joinRoomTime;
-    }
-
-    public void setJoinRoomTime(Date joinRoomTime) {
-        this.joinRoomTime = joinRoomTime;
-    }
-
-    public Date getExitRoomTime() {
-        return exitRoomTime;
-    }
-
-    public void setExitRoomTime(Date exitRoomTime) {
-        this.exitRoomTime = exitRoomTime;
-    }
-
-    public String getRoomType() {
-        return roomType;
-    }
-
-    public void setRoomType(String roomType) {
-        this.roomType = roomType;
-    }
-
-    public Integer getLikeNum() {
-        return likeNum;
-    }
-
-    public void setLikeNum(Integer likeNum) {
-        this.likeNum = likeNum;
-    }
-
-    public Integer getLookNum() {
-        return lookNum;
-    }
-
-    public void setLookNum(Integer lookNum) {
-        this.lookNum = lookNum;
-    }
-
-    public String getSpeakerPic() {
-        return speakerPic;
-    }
-
-    public void setSpeakerPic(String speakerPic) {
-        this.speakerPic = speakerPic;
-    }
-
-    public String getRoomTitle() {
-        return roomTitle;
-    }
-
-    public void setRoomTitle(String roomTitle) {
-        this.roomTitle = roomTitle;
-    }
-
-    public String getLiveRemark() {
-        return liveRemark;
-    }
-
-    public void setLiveRemark(String liveRemark) {
-        this.liveRemark = liveRemark;
-    }
 }

+ 1 - 1
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/RoomTypeEnum.java

@@ -7,7 +7,7 @@ import java.util.stream.Collectors;
 public enum RoomTypeEnum {
 
     LIVE("直播课"),
-    PRACTICE("陪练课"),
+//    PRACTICE("陪练课"),
     TEMP("临时直播间");
 
     private final String code;

+ 30 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/im/ETencentGroupType.java

@@ -0,0 +1,30 @@
+package com.yonge.cooleshow.biz.dal.enums.im;
+
+/**
+ * Description
+ *
+ * @author liujunchi
+ * @date 2023-03-06
+ */
+public enum ETencentGroupType {
+
+    Public("陌生人社交群"),
+    Work("好友工作群"),
+    AVChatRoom("直播群"),
+    Meeting("临时会议群"),
+    Community("社群"),
+
+    ;
+
+    private String code;
+
+    private String msg;
+
+
+    ETencentGroupType(String msg) {
+        this.code = this.name();
+        this.msg = msg;
+    }
+
+
+}

+ 31 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/im/ETencentImCallbackCommand.java

@@ -0,0 +1,31 @@
+package com.yonge.cooleshow.biz.dal.enums.im;
+
+import lombok.Getter;
+
+/**
+ * Description
+ *
+ * @author liujunchi
+ * @date 2023-03-02
+ */
+@Getter
+public enum ETencentImCallbackCommand {
+
+    // STATE_STATECHANGE("State.StateChange", "用户状态变更"),
+    GROUP_CALLBACKAFTERMEMBEREXIT("Group.CallbackAfterMemberExit", "群成员离开之后回调"),
+    GROUP_CALLBACKAFTERNEWMEMBERJOIN("Group.CallbackAfterNewMemberJoin", "新成员入群之后回调"),
+    GROUP_CALLBACKONMEMBERSTATECHANGE("Group.CallbackOnMemberStateChange", "直播群成员在线状态回调"),
+    ;
+
+    private final String command;
+    private final String desc;
+
+    private final String code;
+
+    ETencentImCallbackCommand(String command, String desc) {
+        this.command = command;
+        this.desc = desc;
+
+        this.code = this.name();
+    }
+}

+ 43 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/live/EAnchorStatus.java

@@ -0,0 +1,43 @@
+package com.yonge.cooleshow.biz.dal.enums.live;
+
+/**
+ * Description
+ *
+ * @author liujunchi
+ * @date 2023-03-20
+ */
+public enum EAnchorStatus {
+
+    /**
+     * 主讲人状态
+     */
+    ONLINE("在线"),
+    OFFLINE("离线"),
+
+
+    ;
+    private String code;
+
+    private String msg;
+
+    EAnchorStatus(String msg) {
+        this.msg = msg;
+        this.code = this.name();
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+
+    public void setMsg(String msg) {
+        this.msg = msg;
+    }
+}

+ 40 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/live/EOnOffStatus.java

@@ -0,0 +1,40 @@
+package com.yonge.cooleshow.biz.dal.enums.live;
+
+/**
+ * Description
+ *
+ * @author liujunchi
+ * @date 2023-03-20
+ */
+public enum EOnOffStatus {
+
+
+    ON("是"),
+    OFF("否"),
+
+    ;
+    private String code;
+
+    private String msg;
+
+    EOnOffStatus(String msg) {
+        this.msg = msg;
+        this.code = this.name();
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+
+    public void setMsg(String msg) {
+        this.msg = msg;
+    }
+}

+ 8 - 8
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/ImGroupMemberService.java

@@ -30,16 +30,16 @@ public interface ImGroupMemberService extends IService<ImGroupMember> {
     void insertBatch(List<ImGroupMember> imGroupMembers);
 
     /**
-    * @description: 初始化单个群成员信息,并返回
      * @param imGroupId
      * @param userId
      * @param isAdmin
      * @param roleType
-    * @return List<GroupMember>
-    * @author zx
-    * @date 2022/3/22 15:59
-    */
-    List<GroupMember> initGroupMember(String imGroupId, Long userId, Boolean isAdmin, ImGroupMemberRoleType roleType) throws Exception;
+     * @return List<GroupMember>
+     * @description: 初始化单个群成员信息,并返回
+     * @author zx
+     * @date 2022/3/22 15:59
+     */
+    List<ImGroupMember> initGroupMember(String imGroupId, Long userId, Boolean isAdmin, ImGroupMemberRoleType roleType) throws Exception;
 
     /**
     * @description: 添加群成员
@@ -50,7 +50,7 @@ public interface ImGroupMemberService extends IService<ImGroupMember> {
     * @author zx
     * @date 2022/3/22 15:59
     */
-    List<GroupMember> initGroupMembers(String imGroupId, Set<Long> userIds, ImGroupMemberRoleType roleType);
+    List<ImGroupMember> initGroupMembers(String imGroupId, Set<Long> userIds, ImGroupMemberRoleType roleType);
 
     /**
     * @description: 加入融云群
@@ -60,7 +60,7 @@ public interface ImGroupMemberService extends IService<ImGroupMember> {
     * @author zx
     * @date 2022/3/23 17:50
     */
-    void join(List<GroupMember> groupMemberList,String imGroupId) throws Exception;
+    void join(List<ImGroupMember> groupMemberList,String imGroupId) throws Exception;
 
     /**
     * @description: 删除群成员

+ 61 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/ImGroupService.java

@@ -1,12 +1,19 @@
 package com.yonge.cooleshow.biz.dal.service;
 
 import com.baomidou.mybatisplus.extension.service.IService;
+import com.microsvc.toolkit.middleware.im.message.GroupMemberWrapper;
 import com.yonge.cooleshow.biz.dal.dao.ImGroupDao;
 import com.yonge.cooleshow.biz.dal.dto.ImGroupResultDto;
 import com.yonge.cooleshow.biz.dal.dto.ImGroupSearchDto;
 import com.yonge.cooleshow.biz.dal.entity.ImGroup;
+import com.yonge.cooleshow.biz.dal.entity.ImGroupMember;
+import com.yonge.cooleshow.biz.dal.entity.ImHistoryMessage;
 import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
+import com.yonge.cooleshow.biz.dal.wrapper.im.ImGroupWrapper;
+import io.rong.models.Result;
 
+import java.io.File;
+import java.net.MalformedURLException;
 import java.util.List;
 import java.util.Set;
 
@@ -20,6 +27,31 @@ public interface ImGroupService extends IService<ImGroup> {
 
     ImGroupDao getDao();
 
+
+
+    /**
+     * IM 用户注册
+     * @param userId 用户ID
+     * @param username 用户名
+     * @param avatar 用户头象
+     * @return IM聊天Token
+     */
+    ImGroupWrapper.ImUserInfo register(String userId,ClientEnum clientType, String username, String avatar) throws Exception;
+
+    /**
+     * IM 用户注册
+     * @param userId 用户Id
+     * @return String
+     */
+    String getImUserId(String userId,String clientType);
+
+    /**
+     * 解析IM用户规则
+     * @param imUserId IM用户Id
+     * @return String
+     */
+    String analysisImUserId(String imUserId);
+
     /**
     * @description: 用户主动创建群聊
      * @param imGroup
@@ -37,6 +69,10 @@ public interface ImGroupService extends IService<ImGroup> {
     */
     String autoCreate(Long courseGroupId,String courseGroupType) throws Exception;
 
+    List<GroupMemberWrapper.ImGroupMember> getImGroupMembers(List<ImGroupMember> groupMemberList);
+
+    void asyncRegisterUser(List<GroupMemberWrapper.ImGroupMember> groupMembers);
+
     /**
     * @description: 关闭群聊、解散
      * @param groupId
@@ -93,5 +129,30 @@ public interface ImGroupService extends IService<ImGroup> {
      * @param studentIdList 学生成员列表
      */
     void addGroupMember(String groupId, Set<Long> studentIdList) throws Exception;
+
+    /**
+     * 同步即时通讯聊天记录
+     * @param date
+     * @return
+     */
+    Result historyGet (String date)throws Exception;
+
+    /**
+     * 保存历史消息
+     * @param file
+     */
+    void saveImHistoryMessage(File file);
+
+    /**
+     * 读取融云数据库信息
+     */
+    List<ImHistoryMessage> getRongYunInfo();
+
+    /**
+     * 导入IM腾讯云
+     * @param info
+     * @throws Exception
+     */
+    void importInfo(List<ImHistoryMessage> info) throws Exception;
 }
 

+ 41 - 4
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/LiveRoomService.java

@@ -1,17 +1,19 @@
 package com.yonge.cooleshow.biz.dal.service;
 
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.yonge.cooleshow.biz.dal.dao.LiveRoomDao;
-import com.yonge.cooleshow.biz.dal.entity.ImRoomMessage;
-import com.yonge.cooleshow.biz.dal.entity.ImUserStateSync;
-import com.yonge.cooleshow.biz.dal.entity.LiveRoom;
-import com.yonge.cooleshow.biz.dal.entity.RoomInfoCache;
+import com.yonge.cooleshow.biz.dal.dto.LiveRoomStatus;
+import com.yonge.cooleshow.biz.dal.dto.TencentData;
+import com.yonge.cooleshow.biz.dal.entity.*;
+import com.yonge.cooleshow.biz.dal.enums.RoomTypeEnum;
 import com.yonge.cooleshow.biz.dal.vo.TeacherLivingInfoVo;
 import com.yonge.cooleshow.biz.dal.wrapper.liveroom.LiveRoomWrapper;
 import com.yonge.toolset.base.page.PageInfo;
+import org.springframework.transaction.annotation.Transactional;
 
 /**
  * 直播房间与课程的关系表表(LiveRoom)表服务接口
@@ -67,6 +69,8 @@ public interface LiveRoomService extends IService<LiveRoom> {
      */
     void destroyLiveRoom(String roomUId);
 
+    void updateLiveCourseSchedule(String preCreateRoomMinute, Map<Long, String> titleMap, Map<Long, String> remarkMap, RoomTypeEnum en, Date now, CourseSchedule c);
+
     /**
      * 创建临时房间-直播间
      */
@@ -145,5 +149,38 @@ public interface LiveRoomService extends IService<LiveRoom> {
      * @return LiveRoomWrapper
      */
     LiveRoomWrapper findLiveRoomDetailInfoByRoomId(String liveRoomId, Long userId);
+
+    void createLiveRoomVideoRecord(TencentData.CallbackSteamRecordEvent event);
+
+
+    /**
+     * 直播群成员在线状态回调处理
+     *
+     */
+    void callbackOnMemberStateChange(TencentData.CallbackOnMemberStateChange callbackOnMemberStateChange);
+    /**
+     * 群成员离开之后回调
+     */
+    void callbackAfterMemberExit(TencentData.CallbackAfterMemberExit callbackAfterMemberExit);
+
+    /**
+     * 新成员入群之后回调
+     */
+    void callbackAfterNewMemberJoin(TencentData.CallbackAfterNewMemberJoin callbackAfterNewMemberJoin);
+
+    // 定时任务凌晨2点,关闭腾讯直播间, 融云直播间自动关闭,不做处理
+    void destroyLiveRoom();
+
+    boolean tryDestroyLiveRoom(LiveRoom liveRoom);
+
+
+    /**
+     * 更新主播直播间状态
+     *
+     * @param status 直播间状态
+     * @return
+     */
+    Boolean updateRoomStatus(LiveRoomStatus status);
+
 }
 

+ 2 - 2
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/CourseGroupServiceImpl.java

@@ -184,8 +184,8 @@ public class CourseGroupServiceImpl extends ServiceImpl<CourseGroupDao, CourseGr
         if (CollectionUtils.isNotEmpty(result.getStudentList())) {
 
             for (LiveCourseInfoVo.CourseBuyStudentVo item : result.getStudentList()) {
-
-                item.setImUserId(MessageFormat.format("{0}:{1}", String.valueOf(item.getStudentId()), ClientEnum.STUDENT.name()));
+                item.setImUserId(imGroupService.getImUserId(String.valueOf(item.getStudentId()),
+                        ClientEnum.STUDENT.name()));
             }
         }
         //查询是否购买过该课程组

+ 5 - 5
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/CourseHomeworkServiceImpl.java

@@ -106,8 +106,8 @@ public class CourseHomeworkServiceImpl extends ServiceImpl<CourseHomeworkDao, Co
         }
 
         for (CourseHomeworkVo item : studentInfoList) {
-
-            item.setImUserId(MessageFormat.format("{0}:{1}", String.valueOf(item.getStudentId()), ClientEnum.STUDENT.name()));
+            item.setImUserId(imGroupService.getImUserId(String.valueOf(item.getStudentId()),
+                    ClientEnum.STUDENT.name()));
         }
 
         Map<Long, List<CourseHomeworkVo>> studentCollect = studentInfoList.stream()
@@ -120,8 +120,7 @@ public class CourseHomeworkServiceImpl extends ServiceImpl<CourseHomeworkDao, Co
         }
 
         for (CourseHomeworkVo item : teacherInfoList) {
-
-            item.setImUserId(String.valueOf(item.getTeacherId()));
+            item.setImUserId(imGroupService.getImUserId(String.valueOf(item.getTeacherId()),ClientEnum.TEACHER.name()));
         }
 
         Map<Long, List<CourseHomeworkVo>> teacherCollect = teacherInfoList.stream()
@@ -194,7 +193,8 @@ public class CourseHomeworkServiceImpl extends ServiceImpl<CourseHomeworkDao, Co
         }
 
         // 设置用户IM聊天ID
-        courseHomeworkDetailVo.setImUserId(MessageFormat.format("{0}:{1}", String.valueOf(studentId), ClientEnum.STUDENT.name()));
+        courseHomeworkDetailVo.setImUserId(imGroupService.getImUserId(String.valueOf(studentId),
+                ClientEnum.STUDENT.name()));
 
         // 课程组的群聊
         ImGroup imGroup = imGroupService.getByCourseGroupId(courseHomeworkDetailVo.getCourseGroupId());

+ 8 - 12
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/CourseScheduleServiceImpl.java

@@ -713,8 +713,7 @@ public class CourseScheduleServiceImpl extends ServiceImpl<CourseScheduleDao, Co
             List<MyCourseVo> list = baseMapper.queryTeacherPracticeCourse(page, monthToDate(search));
 
             for (MyCourseVo item : list) {
-
-                item.setImUserId(MessageFormat.format("{0}:{1}", String.valueOf(item.getUserId()), ClientEnum.STUDENT.name()));
+                item.setImUserId(imGroupService.getImUserId(String.valueOf(item.getUserId()),ClientEnum.STUDENT.name()));
             }
 
             if (replied == 0) {//未评价
@@ -732,8 +731,7 @@ public class CourseScheduleServiceImpl extends ServiceImpl<CourseScheduleDao, Co
 
         List<MyCourseVo> records = baseMapper.queryTeacherPracticeCourse(page, monthToDate(search));
         for (MyCourseVo item : records) {
-
-            item.setImUserId(MessageFormat.format("{0}:{1}", String.valueOf(item.getUserId()), ClientEnum.STUDENT.name()));
+            item.setImUserId(imGroupService.getImUserId(String.valueOf(item.getUserId()),ClientEnum.STUDENT.name()));
         }
 
         return page.setRecords(records);
@@ -794,7 +792,7 @@ public class CourseScheduleServiceImpl extends ServiceImpl<CourseScheduleDao, Co
 
             for (MyCourseVo item : records) {
 
-                item.setImUserId(String.valueOf(item.getUserId()));
+                item.setImUserId(imGroupService.getImUserId(String.valueOf(item.getUserId()),ClientEnum.TEACHER.name()));
             }
         }
 
@@ -845,8 +843,7 @@ public class CourseScheduleServiceImpl extends ServiceImpl<CourseScheduleDao, Co
         if (CollectionUtils.isNotEmpty(studentList)) {
 
             for (CourseStudent item : studentList) {
-
-                item.setImUserId(MessageFormat.format("{0}:{1}", item.getUserId(), ClientEnum.STUDENT.name()));
+                item.setImUserId(imGroupService.getImUserId(String.valueOf(item.getUserId()),ClientEnum.STUDENT.name()));
             }
         }
 
@@ -902,8 +899,7 @@ public class CourseScheduleServiceImpl extends ServiceImpl<CourseScheduleDao, Co
         if (CollectionUtils.isNotEmpty(teacherList)) {
 
             for (CourseStudent item : teacherList) {
-
-                item.setImUserId(item.getUserId());
+                item.setImUserId(imGroupService.getImUserId(String.valueOf(item.getUserId()),ClientEnum.STUDENT.name()));
             }
         }
 
@@ -1063,7 +1059,7 @@ public class CourseScheduleServiceImpl extends ServiceImpl<CourseScheduleDao, Co
             schedule.setEndTime(date.getEndTime());
             schedule.setCourseGroupId(groupId);
             schedule.setType(CourseScheduleEnum.PRACTICE.getCode());
-            schedule.classNum(i + 1);
+            schedule.setClassNum(i + 1);
             schedule.setTeacherId(scheduleDto.getTeacherId());
             schedule.setLock(1);
             schedule.setStatus(CourseScheduleEnum.NOT_START.getCode());
@@ -2058,7 +2054,7 @@ public class CourseScheduleServiceImpl extends ServiceImpl<CourseScheduleDao, Co
             CourseSchedule schedule = new CourseSchedule();
             schedule.setCourseGroupId(courseGroup.getId());
             schedule.setType(CourseScheduleEnum.PIANO_ROOM_CLASS.getCode());
-            schedule.classNum(i + 1);
+            schedule.setClassNum(i + 1);
             schedule.setTeacherId(teacherId);
             schedule.setClassDate(DateUtil.trunc(courseTimeEntity.getStartTime()));
             schedule.setStartTime(courseTimeEntity.getStartTime());
@@ -2558,7 +2554,7 @@ public class CourseScheduleServiceImpl extends ServiceImpl<CourseScheduleDao, Co
             schedule.setEndTime(date.getEndTime());
             schedule.setCourseGroupId(groupId);
             schedule.setType(CourseScheduleEnum.PRACTICE.getCode());
-            schedule.classNum(i + 1);
+            schedule.setClassNum(i + 1);
             schedule.setTeacherId(scheduleDto.getTeacherId());
             schedule.setLock(1);
             schedule.setStatus(CourseScheduleEnum.NOT_START.getCode());

+ 109 - 48
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/CustomerServiceBatchSendingServiceImpl.java

@@ -6,8 +6,12 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.google.common.collect.Lists;
+import com.microsvc.toolkit.middleware.common.http.ImageUtil;
+import com.microsvc.toolkit.middleware.im.ImPluginContext;
+import com.microsvc.toolkit.middleware.im.impl.TencentCloudImPlugin;
+import com.microsvc.toolkit.middleware.im.message.MessageWrapper;
+import com.microsvc.toolkit.middleware.im.message.TencentRequest;
 import com.yonge.cooleshow.auth.config.CustomerServiceConfig;
-import com.yonge.cooleshow.biz.dal.config.RongCloudConfig;
 import com.yonge.cooleshow.biz.dal.entity.CustomerServiceBatchSending;
 import com.yonge.cooleshow.biz.dal.entity.CustomerServiceReceive;
 import com.yonge.cooleshow.biz.dal.entity.Subject;
@@ -20,10 +24,7 @@ import com.yonge.cooleshow.biz.dal.enums.im.EImSendStatus;
 import com.yonge.cooleshow.biz.dal.enums.im.EImSendType;
 import com.yonge.cooleshow.biz.dal.mapper.CustomerServiceBatchSendingMapper;
 import com.yonge.cooleshow.biz.dal.mapper.SysUserMapper;
-import com.yonge.cooleshow.biz.dal.service.CustomerServiceBatchSendingService;
-import com.yonge.cooleshow.biz.dal.service.CustomerServiceReceiveService;
-import com.yonge.cooleshow.biz.dal.service.SubjectService;
-import com.yonge.cooleshow.biz.dal.service.TeacherService;
+import com.yonge.cooleshow.biz.dal.service.*;
 import com.yonge.cooleshow.biz.dal.wrapper.im.CustomerService;
 import com.yonge.cooleshow.biz.dal.wrapper.im.CustomerServiceBatchSendingWrapper;
 import com.yonge.cooleshow.biz.dal.wrapper.im.CustomerServiceReceiveWrapper;
@@ -48,13 +49,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.text.MessageFormat;
-import java.util.Arrays;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Random;
+import java.util.*;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
@@ -80,6 +75,12 @@ public class CustomerServiceBatchSendingServiceImpl extends ServiceImpl<Customer
     @Autowired
     private TeacherService teacherService;
 
+    @Autowired
+    private ImPluginContext imPluginContext;
+
+    @Autowired
+    private ImGroupService imGroupService;
+
 	/**
      * 查询详情
      * @param id 详情ID
@@ -406,8 +407,6 @@ public class CustomerServiceBatchSendingServiceImpl extends ServiceImpl<Customer
         // 消息状态判定,且不能重复发送
         DistributedLock.of(redissonClient).runIfLockCanGet(lockName, () -> {
 
-            List<BaseMessage> messages = getReceiveMessage(info);
-
             // 异步发送消息且同步更新已发送人数,稍后可在页面刷新查看已发送用户数
             ThreadPool.getExecutor().submit(() -> {
                 // 接收消息用户数
@@ -444,13 +443,11 @@ public class CustomerServiceBatchSendingServiceImpl extends ServiceImpl<Customer
                                         .map(x -> String.valueOf(x.getUserId()))
                                         .collect(Collectors.toList());
 
-                                if (ClientEnum.STUDENT.match(clientType)) {
-                                    receiveUserIds = receiveUserIds.stream()
-                                            .map(x -> MessageFormat.format("{0}:STUDENT", x))
-                                            .collect(Collectors.toList());
-                                }
+                                receiveUserIds = receiveUserIds.stream()
+                                        .map(x -> imGroupService.getImUserId(x, clientType))
+                                        .collect(Collectors.toList());
 
-                                batchSendCustomerServiceMessage(info, messages, receiveNums, receiveUserIds);
+                                batchSendCustomerServiceMessage(info, receiveNums, receiveUserIds);
                             }
                         });
 
@@ -472,15 +469,10 @@ public class CustomerServiceBatchSendingServiceImpl extends ServiceImpl<Customer
                         for (Integer pageNum : collect) {
 
                             List<String> receiveUserIds = customerServiceReceiveService.selectPage(PageUtil.getPage(pageNum, limit), receiveQuery).getRecords().stream()
-                                    .map(x -> {
-                                        if (ClientEnum.STUDENT.match(x.getClientType())) {
-                                            return MessageFormat.format("{0}:STUDENT", String.valueOf(x.getUserId()));
-                                        }
-                                        return String.valueOf(x.getUserId());
-                                    })
+                                    .map(x -> imGroupService.getImUserId(String.valueOf(x.getUserId()), x.getClientType()))
                                     .collect(Collectors.toList());
 
-                            batchSendCustomerServiceMessage(info, messages, receiveNums, receiveUserIds);
+                            batchSendCustomerServiceMessage(info, receiveNums, receiveUserIds);
                         }
                     }
 
@@ -504,38 +496,52 @@ public class CustomerServiceBatchSendingServiceImpl extends ServiceImpl<Customer
     /**
      * 批量发送客服消息
      * @param info CustomerServiceBatchSending
-     * @param messages 推送消息类型
      * @param receiveNums 接收消息用户数
      * @param receiveUserIds 接收消息用户Id
      */
-    private static void batchSendCustomerServiceMessage(CustomerServiceBatchSending info, List<BaseMessage> messages,
+    private void batchSendCustomerServiceMessage(CustomerServiceBatchSending info,
                                                         List<Integer> receiveNums, List<String> receiveUserIds) {
+        List<BaseMessage> baseMessages = new ArrayList<>();
+        List<TencentRequest.MessageBody> tencentMessages = new ArrayList<>();
+        getReceiveMessage(info,baseMessages,tencentMessages);
         // 拓展消息
         PushExt pushExt = PushExt.build(info.getTitle(), 1,
                 new PushExt.HW("channelId", "NORMAL"), new PushExt.VIVO("1"),
                 new PushExt.APNs("", ""),
                 new PushExt.OPPO(""));
 
-        String senderId = String.valueOf(info.getSenderId());
-        PrivateMessage privateMessage;
-        ResponseResult privateResult;
+        String senderId = imGroupService.getImUserId(String.valueOf(info.getSenderId()), ClientEnum.TEACHER.getCode());
         for (List<String> item : Lists.partition(receiveUserIds, 100)) {
 
             try {
+                MessageWrapper.PrivateMessage build = MessageWrapper.PrivateMessage.builder()
+                        .senderId(senderId)
+                        .targetIds(item)
+//                        .objectName(message.getType())
+//                        .rongCloueMessage(message)
+                        .pushExt(JSON.toJSONString(pushExt))
+                        .includeSender(0)
+//                        .tencentMessage()
+                        .build();
 
-                for (BaseMessage message : messages) {
-
-                    // 发送用户IM通知消息
-                    privateMessage = new PrivateMessage()
-                            .setSenderId(senderId)
-                            .setTargetId(item.toArray(new String[0]))
-                            .setObjectName(message.getType())
-                            .setContent(message)
-                            .setPushExt(pushExt)
-                            .setIsIncludeSender(0);
-
-                    privateResult = RongCloudConfig.rongCloud.message.msgPrivate.send(privateMessage);
-                    log.info("batchSendCustomerServiceMessage senderId={}, ret={}", senderId, privateResult.getCode());
+                if (TencentCloudImPlugin.PLUGIN_NAME.equals(imPluginContext.defaultService())) {
+                    // 腾讯云消息
+                    for (TencentRequest.MessageBody message : tencentMessages) {
+                        if (message == null) {
+                            continue;
+                        }
+                        Boolean ret = imPluginContext.getPluginService().sendPrivateMessage(build.objectName(message.getMsgType()).tencentMessage(message));
+                        log.info("batchSendPrivateMessage GROUP tencentCloud senderId={}, ret={}", senderId, ret);
+                    }
+                } else {
+                    // 融云消息
+                    for (BaseMessage message : baseMessages) {
+                        if (message == null) {
+                            continue;
+                        }
+                        Boolean ret = imPluginContext.getPluginService().sendPrivateMessage(build.objectName(message.getType()).rongCloueMessage(message));
+                        log.info("batchSendPrivateMessage GROUP rongCloud senderId={}, ret={}", senderId, ret);
+                    }
                 }
 
                 receiveNums.add(item.size());
@@ -551,8 +557,7 @@ public class CustomerServiceBatchSendingServiceImpl extends ServiceImpl<Customer
      * @param info CustomerServiceBatchSending
      * @return List<BaseMessage>
      */
-    private static List<BaseMessage> getReceiveMessage(CustomerServiceBatchSending info) {
-        List<BaseMessage> messages = Lists.newArrayList();
+    private static void getReceiveMessage(CustomerServiceBatchSending info, List<BaseMessage> messages, List<TencentRequest.MessageBody> tencentMessages) {
 
         if (StringUtils.isNotEmpty(info.getImgMessage())) {
 
@@ -562,6 +567,9 @@ public class CustomerServiceBatchSendingServiceImpl extends ServiceImpl<Customer
             ImgMessage imgMessage = new ImgMessage(ImUtil.imageToBase64(info.getImgMessage(), "png"), "", info.getImgMessage());
 
             messages.add(imgMessage);
+
+            TencentRequest.MessageBody timImageElem = getTimImageElem(info.getImgMessage());
+            tencentMessages.add(timImageElem);
         }
 
         if (StringUtils.isNotEmpty(info.getTextMessage())) {
@@ -570,8 +578,61 @@ public class CustomerServiceBatchSendingServiceImpl extends ServiceImpl<Customer
             TxtMessage txtMessage = new TxtMessage(info.getTextMessage(), "");
 
             messages.add(txtMessage);
+
+
+            // 腾讯云消息
+            tencentMessages.add(getTimTextElem(info.getTextMessage()));
         }
-        return messages;
+    }
+
+    public static TencentRequest.MessageBody getTimImageElem(String imgMessage) {
+
+        if (StringUtils.isEmpty(imgMessage)) {
+            return null;
+        }
+
+        // 腾讯云消息
+        // 腾讯云消息
+        ImageUtil.ImageInfo imageReq = ImageUtil.ImageInfo.builder()
+                .imgUrl(imgMessage)
+                .suffix(imgMessage.substring(imgMessage.lastIndexOf(".") + 1))
+                .build();
+
+        // 获取图片基本信息
+        ImageUtil.imageToBase64(imageReq);
+
+        // 图片格式。 JPG = 1 GIF = 2 PNG = 3 BMP = 4 其他 = 255
+        // 图片类型。原图 = 1,大图 = 2,缩略图 = 3
+        int imageFormat = 1;
+        if ("PNG".equalsIgnoreCase(imageReq.getSuffix())) {
+            imageFormat = 3;
+        }
+        if ("GIF".equalsIgnoreCase(imageReq.getSuffix())) {
+            imageFormat = 2;
+        }
+        return TencentRequest.MessageBody.builder()
+                .msgType("TIMImageElem")
+                .msgContent(TencentRequest.ImageMessageBody.builder()
+                .uuid(imageReq.getMd5())
+                .imageFormat(imageFormat)
+                .type(1)
+                .height(imageReq.getHeight())
+                .width(imageReq.getWidth())
+                .url(imgMessage)
+                .size(imageReq.getSize()))
+                .build();
+    }
+
+    public static TencentRequest.MessageBody getTimTextElem(String textMessage) {
+
+        if (StringUtils.isEmpty(textMessage)) {
+            return null;
+        }
+
+        return TencentRequest.MessageBody.builder()
+                .msgType("TIMTextElem")
+                .msgContent(TencentRequest.TextMessageBody.builder().text(textMessage))
+                .build();
     }
 
 

+ 3 - 2
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImGroupMemberAuditServiceImpl.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.yonge.cooleshow.auth.api.entity.SysUser;
 import com.yonge.cooleshow.biz.dal.dao.ImGroupMemberAuditDao;
 import com.yonge.cooleshow.biz.dal.entity.ImGroup;
+import com.yonge.cooleshow.biz.dal.entity.ImGroupMember;
 import com.yonge.cooleshow.biz.dal.entity.ImGroupMemberAudit;
 import com.yonge.cooleshow.biz.dal.enums.AuditStatusEnum;
 import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
@@ -86,7 +87,7 @@ public class ImGroupMemberAuditServiceImpl extends ServiceImpl<ImGroupMemberAudi
         baseMapper.insert(imGroupMemberAudit);
         if(imGroup.getAutoPassFlag() && Optional.ofNullable(autoJoin).orElse(false)){
             //处理本地群成员
-            List<GroupMember> groupMembers = imGroupMemberService.initGroupMember(imGroup.getId(),
+            List<ImGroupMember> groupMembers = imGroupMemberService.initGroupMember(imGroup.getId(),
                     imGroupMemberAudit.getUserId(), false,
                     imGroupMemberAudit.getRoleType());
             //同步群成员数量
@@ -121,7 +122,7 @@ public class ImGroupMemberAuditServiceImpl extends ServiceImpl<ImGroupMemberAudi
         if(auditStatus == AuditStatusEnum.OPEN){
             List<ImGroupMemberAudit> imGroupMemberAudit = baseMapper.findByIds(auditIds);
             Set<Long> userIds = imGroupMemberAudit.stream().map(ImGroupMemberAudit::getUserId).collect(Collectors.toSet());
-            List<GroupMember> groupMembers = imGroupMemberService.initGroupMembers(groupId,userIds, ImGroupMemberRoleType.STUDENT);
+            List<ImGroupMember> groupMembers = imGroupMemberService.initGroupMembers(groupId,userIds, ImGroupMemberRoleType.STUDENT);
             //同步群成员数量
             imGroupService.getDao().updateMemberNum(imGroup.getId());
             //加入融云群

+ 39 - 35
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImGroupMemberServiceImpl.java

@@ -1,25 +1,28 @@
 package com.yonge.cooleshow.biz.dal.service.impl;
 
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
 import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.google.common.collect.Lists;
-import com.yonge.cooleshow.biz.dal.config.RongCloudConfig;
+import com.microsvc.toolkit.middleware.im.ImPluginContext;
+import com.microsvc.toolkit.middleware.im.message.GroupMemberWrapper;
 import com.yonge.cooleshow.biz.dal.dao.ImGroupDao;
 import com.yonge.cooleshow.biz.dal.dao.ImGroupMemberDao;
 import com.yonge.cooleshow.biz.dal.dao.TeacherDao;
 import com.yonge.cooleshow.biz.dal.dto.BasicUserInfo;
+import com.yonge.cooleshow.biz.dal.entity.ImGroup;
 import com.yonge.cooleshow.biz.dal.entity.ImGroupMember;
 import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
 import com.yonge.cooleshow.biz.dal.enums.ImGroupMemberRoleType;
 import com.yonge.cooleshow.biz.dal.service.ImGroupMemberService;
+import com.yonge.cooleshow.biz.dal.service.ImGroupService;
 import com.yonge.toolset.base.exception.BizException;
-import io.rong.models.Result;
 import io.rong.models.group.GroupMember;
-import io.rong.models.group.GroupModel;
 import org.apache.commons.collections.CollectionUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -45,6 +48,12 @@ public class ImGroupMemberServiceImpl extends ServiceImpl<ImGroupMemberDao, ImGr
     @Resource
     private ImGroupDao imGroupDao;
 
+    @Autowired
+    private ImPluginContext imPluginContext;
+
+    @Autowired
+    private ImGroupService imGroupService;
+
     @Override
     public ImGroupMemberDao getDao() {
         return this.baseMapper;
@@ -61,7 +70,7 @@ public class ImGroupMemberServiceImpl extends ServiceImpl<ImGroupMemberDao, ImGr
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public List<GroupMember> initGroupMember(String imGroupId, Long userId, Boolean isAdmin, ImGroupMemberRoleType roleType){
+    public List<ImGroupMember> initGroupMember(String imGroupId, Long userId, Boolean isAdmin, ImGroupMemberRoleType roleType){
         //记录群成员
         BasicUserInfo basicUserInfo = teacherDao.getBasicUserInfo(userId);
         ImGroupMember imGroupMember = new ImGroupMember(imGroupId,userId,basicUserInfo.getUsername(),basicUserInfo.getAvatar(), isAdmin, roleType);
@@ -70,20 +79,15 @@ public class ImGroupMemberServiceImpl extends ServiceImpl<ImGroupMemberDao, ImGr
         imGroupMember.setUpdateTime(date);
         this.baseMapper.insert(imGroupMember);
         //加入融云群
-        List<GroupMember> groupMemberList = new ArrayList<>();
-
-        String imUserId = String.valueOf(userId);
-        if (ImGroupMemberRoleType.STUDENT == roleType) {
-            imUserId = MessageFormat.format("{0}:{1}", imUserId, ClientEnum.STUDENT.name());
-        }
+        List<ImGroupMember> groupMemberList = new ArrayList<>();
 
-        groupMemberList.add(new GroupMember(imUserId,imGroupId,null));
+        groupMemberList.add(imGroupMember);
         return groupMemberList;
     }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public List<GroupMember> initGroupMembers(String imGroupId, Set<Long> userIds, ImGroupMemberRoleType roleType){
+    public List<ImGroupMember> initGroupMembers(String imGroupId, Set<Long> userIds, ImGroupMemberRoleType roleType){
         //记录群成员
         List<ImGroupMember> imGroupMembers = baseMapper.initImGroupMember(imGroupId,userIds,roleType.getCode());
         if(CollectionUtils.isNotEmpty(imGroupMembers)){
@@ -97,35 +101,35 @@ public class ImGroupMemberServiceImpl extends ServiceImpl<ImGroupMemberDao, ImGr
             }
             baseMapper.insertBatch(imGroupMembers);
 
-            //加入融云群
-            Function<Long,GroupMember> func = (userId) -> {
-
-                String imUserId = String.valueOf(userId);
-                if (ImGroupMemberRoleType.STUDENT == roleType) {
-                    imUserId = MessageFormat.format("{0}:{1}", imUserId, ClientEnum.STUDENT.name());
-                }
-
-                return  new GroupMember(imUserId,imGroupId,null);
-            };
-            return userIds.stream().map(func).collect(Collectors.toList());
+            return imGroupMembers;
         }
         return new ArrayList<>();
     }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public void join(List<GroupMember> groupMemberList,String imGroupId) throws Exception {
-        GroupMember[] groupMembers = groupMemberList.toArray(new GroupMember[groupMemberList.size()]);
-        GroupModel groupModel = new GroupModel(imGroupId, 0);
-        groupModel.setMembers(groupMembers);
-        groupModel.setName(imGroupDao.selectById(imGroupId).getName());
-        Result result = RongCloudConfig.rongCloud.group.join(groupModel);
-        if(!result.code.equals(200)){
-            log.error("加入群组失败:{}",result.errorMessage);
-            throw new BizException("加入群组失败:请联系管理员");
+    public void join(List<ImGroupMember> groupMemberList,String imGroupId) throws Exception {
+
+        if (CollectionUtils.isEmpty(groupMemberList)) {
+            // 群成员为空,直接忽略
+            return;
+        }
+
+        // 判断群ID是否有效
+        ImGroup imGroup = imGroupDao.selectById(imGroupId);
+        if (Objects.isNull(imGroup)) {
+            throw new BizException("群组不存在");
         }
+        List<GroupMemberWrapper.ImGroupMember> imGroupMembers = imGroupService.getImGroupMembers(groupMemberList);
+        // 添加群成员到当前群组
+        imPluginContext.getPluginService().groupJoin(imGroup.getId(), imGroup.getName(), imGroupMembers);
+
+        // 自动激活用户
+        imGroupService.asyncRegisterUser(imGroupMembers);
     }
 
+
+
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void delByGroupId(String groupId) {
@@ -198,9 +202,9 @@ public class ImGroupMemberServiceImpl extends ServiceImpl<ImGroupMemberDao, ImGr
                 item.setImUserId(String.valueOf(item.getUserId()));
 
                 if (ImGroupMemberRoleType.STUDENT == item.getRoleType()) {
-
-                    item.setImUserId(MessageFormat.format("{0}:{1}", String.valueOf(item.getUserId()),
-                            ClientEnum.STUDENT.getCode()));
+                    item.setImUserId(imGroupService.getImUserId(String.valueOf(item.getUserId()),ClientEnum.STUDENT.name()));
+                }else if(ImGroupMemberRoleType.TEACHER == item.getRoleType()){
+                    item.setImUserId(imGroupService.getImUserId(String.valueOf(item.getUserId()),ClientEnum.TEACHER.name()));
                 }
             }
         }

+ 422 - 92
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImGroupServiceImpl.java

@@ -1,20 +1,24 @@
 package com.yonge.cooleshow.biz.dal.service.impl;
 
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.google.common.collect.Lists;
 import com.yonge.cooleshow.auth.config.AppGlobalServiceConfig;
+import com.microsvc.toolkit.middleware.im.ImPluginContext;
+import com.microsvc.toolkit.middleware.im.message.GroupMemberWrapper;
+import com.microsvc.toolkit.middleware.im.message.MessageWrapper;
+import com.microsvc.toolkit.middleware.im.message.TencentRequest;
+import com.microsvc.toolkit.middleware.im.properties.ImConfigProperties;
 import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
 import com.yonge.cooleshow.auth.api.entity.SysUser;
-import com.yonge.cooleshow.biz.dal.config.RongCloudConfig;
+import com.yonge.cooleshow.auth.config.AppGlobalServiceConfig;
 import com.yonge.cooleshow.biz.dal.dao.CourseScheduleStudentPaymentDao;
 import com.yonge.cooleshow.biz.dal.dao.ImGroupDao;
 import com.yonge.cooleshow.biz.dal.dao.ImGroupMemberAuditDao;
 import com.yonge.cooleshow.biz.dal.dto.ImGroupResultDto;
 import com.yonge.cooleshow.biz.dal.dto.ImGroupSearchDto;
-import com.yonge.cooleshow.biz.dal.entity.CourseGroup;
-import com.yonge.cooleshow.biz.dal.entity.ImGroup;
-import com.yonge.cooleshow.biz.dal.entity.ImGroupMember;
-import com.yonge.cooleshow.biz.dal.entity.Teacher;
+import com.yonge.cooleshow.biz.dal.entity.*;
 import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
 import com.yonge.cooleshow.biz.dal.enums.ImGroupMemberRoleType;
 import com.yonge.cooleshow.biz.dal.enums.ImGroupType;
@@ -26,31 +30,50 @@ import com.yonge.cooleshow.biz.dal.service.ImUserFriendService;
 import com.yonge.cooleshow.biz.dal.service.StudentStarService;
 import com.yonge.cooleshow.biz.dal.service.SysUserService;
 import com.yonge.cooleshow.biz.dal.service.TeacherService;
+import com.yonge.cooleshow.biz.dal.entity.Teacher;
+import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
+import com.yonge.cooleshow.biz.dal.enums.ImGroupMemberRoleType;
+import com.yonge.cooleshow.biz.dal.enums.ImGroupType;
+import com.yonge.cooleshow.biz.dal.entity.StudentStar;
+import com.yonge.cooleshow.biz.dal.service.*;
+import com.yonge.cooleshow.biz.dal.wrapper.im.ImGroupWrapper;
 import com.yonge.toolset.base.exception.BizException;
 import com.yonge.toolset.base.util.ThreadPool;
+import com.yonge.toolset.utils.http.HttpUtil;
+import io.rong.RongCloud;
+import io.rong.methods.message.history.History;
 import io.rong.models.Result;
 import io.rong.models.group.GroupMember;
 import io.rong.models.group.GroupModel;
 import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang.math.RandomUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.redisson.api.RBucket;
 import org.redisson.api.RedissonClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Resource;
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
 import java.text.MessageFormat;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.Set;
-import java.util.UUID;
+import java.util.*;
 import java.util.stream.Collectors;
 import java.util.concurrent.TimeUnit;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipInputStream;
+
+import static com.microsvc.toolkit.middleware.im.message.ETencentMessage.TIMImageElem;
+import static com.microsvc.toolkit.middleware.im.message.ETencentMessage.TIMLocationElem;
+import static com.microsvc.toolkit.middleware.im.message.ETencentMessage.TIMTextElem;
 
 /**
  * 即时通讯群组(ImGroup)表服务实现类
@@ -90,11 +113,89 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
     @Autowired
     private StudentStarService studentStarService;
 
+
+    @Autowired
+    private ImPluginContext imPluginContext;
+
+    @Autowired
+    private ImConfigProperties imConfig;
+
+
+    @Value("${cn.rongcloud.im.appkey}")
+    private String appKey;
+    @Value("${cn.rongcloud.im.secret}")
+    private String appSecret;
+
+
+    @Autowired
+    private SysConfigService sysConfigService;
+
     @Override
     public ImGroupDao getDao() {
         return this.baseMapper;
     }
 
+
+    /**
+     * IM 用户注册
+     *
+     * @param userId   用户ID
+     * @param username 用户名
+     * @param avatar   用户头象
+     * @return IM聊天Token
+     */
+    @Override
+    public ImGroupWrapper.ImUserInfo register(String userId,ClientEnum clientType, String username, String avatar) throws Exception {
+
+        ImGroupWrapper.ImUserInfo userInfo = ImGroupWrapper.ImUserInfo.builder()
+                .imUserId("").imToken("").build();
+        // 生成IM聊天Token
+        try {
+
+            // 聊天Token
+            String imUserId = getImUserId(userId,clientType.getCode());
+
+            // 生成签名
+            userInfo.imUserId(imUserId)
+                    .setImToken(imPluginContext.getPluginService().register(imUserId, username, avatar));
+
+        } catch (Exception e) {
+            log.error("register ImToken EX, userId={}, username={}", userId, username, e);
+        }
+        return userInfo;
+    }
+
+
+    /**
+     * IM 用户注册
+     *
+     * @param userId 用户Id
+     * @return String
+     */
+    @Override
+    public String getImUserId(String userId,String clientType) {
+        String imUserId = userId;
+        if (StringUtils.isNotBlank(imConfig.getAppPrefix()) && !userId.startsWith(imConfig.getAppPrefix())) {
+            imUserId = MessageFormat.format("{0}:{1}:{2}", imConfig.getAppPrefix(), userId,clientType);
+        }
+        return imUserId;
+    }
+
+
+    /**
+     * 解析IM用户规则
+     *
+     * @param imUserId IM用户Id
+     * @return String
+     */
+    @Override
+    public String analysisImUserId(String imUserId) {
+        if (StringUtils.isNotBlank(imConfig.getAppPrefix())) {
+            return imUserId.replace(imConfig.getAppPrefix() + ":", "");
+        }
+        return imUserId;
+    }
+
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void create(ImGroup imGroup) throws Exception {
@@ -108,10 +209,10 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
         imGroup.setId(imGroupId);
         this.baseMapper.insert(imGroup);
         //处理本地群成员列表
-        List<GroupMember> groupMembers = imGroupMemberService.initGroupMember(imGroupId, imGroup.getCreateBy(), true, ImGroupMemberRoleType.TEACHER);
+        List<ImGroupMember> groupMembers = imGroupMemberService.initGroupMember(imGroupId, imGroup.getCreateBy(), true, ImGroupMemberRoleType.TEACHER);
 
         if (imGroup.getStudentIdList().isEmpty()) {
-            List<GroupMember> groupMemberList = imGroupMemberService.initGroupMembers(imGroupId,
+            List<ImGroupMember> groupMemberList = imGroupMemberService.initGroupMembers(imGroupId,
                     imGroup.getStudentIdList(), ImGroupMemberRoleType.STUDENT);
             groupMembers.addAll(groupMemberList);
         }
@@ -134,7 +235,7 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
             throw new BizException("添加的群成员不能为空");
 
         }
-        List<GroupMember> groupMemberList = imGroupMemberService.initGroupMembers(groupId,
+        List<ImGroupMember> groupMemberList = imGroupMemberService.initGroupMembers(groupId,
                 imGroup.getStudentIdList(), ImGroupMemberRoleType.STUDENT);
         imGroupMemberService.join(groupMemberList, groupId);
     }
@@ -172,7 +273,7 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
         imUserFriendService.saveUserFriend(teacherId, studentIds);
         //处理本地群成员列表
         // 添加老师
-        List<GroupMember> groupMembers = imGroupMemberService.initGroupMember(imGroupId, imGroup.getCreateBy(), true, ImGroupMemberRoleType.TEACHER);
+        List<ImGroupMember> groupMembers = imGroupMemberService.initGroupMember(imGroupId, imGroup.getCreateBy(), true, ImGroupMemberRoleType.TEACHER);
         // 添加学生
         groupMembers.addAll(imGroupMemberService.initGroupMembers(imGroupId, studentIds, ImGroupMemberRoleType.STUDENT));
         //创建融云群
@@ -182,21 +283,101 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
         return imGroupId;
     }
 
+
     //创建融云群
     private void rtcCreate(Long userId,String imGroupId,String imGroupName) throws Exception {
-        //创建融云群
-        GroupMember[] groupMembers = new GroupMember[]{new GroupMember(userId.toString(),imGroupId,null)};
-        GroupModel groupModel = new GroupModel(imGroupId,0);
-        groupModel.setMembers(groupMembers);
-        groupModel.setName(imGroupName);
-        Result result = RongCloudConfig.rongCloud.group.create(groupModel);
-        if(!result.code.equals(200)){
-            log.error("创建群聊失败:{}",result.errorMessage);
-            throw new BizException("创建群聊失败,请联系管理员");
+        rtcCreate(userId,imGroupId,imGroupName,ClientEnum.TEACHER);
+    }
+
+    //创建融云群
+    private void rtcCreate(Long userId,String imGroupId,String imGroupName,ClientEnum clientType) throws Exception {
+        //创建群
+        SysUser user = sysUserService.findUserById(userId);
+
+        // 群组默认头象
+//        String groupDefaultAvatar = sysConfigService.findConfigValue(SysConfigConstant.GROUP_DEFAULT_AVATAR);
+        List<GroupMemberWrapper.ImGroupMember> groupMembers = Lists.newArrayList();
+        //记录群成员
+        GroupMemberWrapper.ImGroupMember groupMember = GroupMemberWrapper.ImGroupMember
+                .builder()
+                .id(IdWorker.getId())
+                .groupId(imGroupId)
+                .userId(userId)
+                .clientType(clientType.getCode())
+                .avatar(null)
+                .nickname(user.getUsername())
+                .isAdmin(true)
+                .imUserId(getImUserId(userId.toString(),clientType.getCode()))
+                .roleType(clientType.getCode())
+                .build();
+        groupMembers.add(groupMember);
+
+        // 自动激活学生IM帐号
+        registerUser(groupMembers);
+
+        // 创建IM群组
+        imPluginContext.getPluginService().groupCreate(imGroupId, imGroupName,
+                getImUserId(userId.toString(),clientType.getCode()));
+
+        // 添加群成员到当前群组
+        imPluginContext.getPluginService().groupJoin(imGroupId, imGroupName,
+                groupMembers);
+    }
+
+
+    /**
+     * 批量导入用户
+     * @param groupMembers List<ImGroupMemberWrapper.ImGroupMember>
+     */
+    private void registerUser(List<GroupMemberWrapper.ImGroupMember> groupMembers) {
+        for (GroupMemberWrapper.ImGroupMember member : groupMembers) {
+            // 激活用户帐号为空
+            if (StringUtils.isBlank(member.getNickname())) {
+                continue;
+            }
+
+            try {
+                String imUserId = member.getImUserId();
+                if (StringUtils.isBlank(imUserId)) {
+                    imUserId = getImUserId(String.valueOf(member.getUserId()),
+                            member.getClientType());
+                }
+
+                imPluginContext.getPluginService().register(getImUserId(imUserId,member.getClientType()), member.getNickname(), member.getAvatar());
+            } catch (Exception e) {
+                log.error("registerUser member imToken");
+            }
         }
     }
 
     @Override
+    public List<GroupMemberWrapper.ImGroupMember> getImGroupMembers(List<ImGroupMember> groupMemberList) {
+        return groupMemberList.stream().map(item -> {
+            return GroupMemberWrapper.ImGroupMember.builder()
+                    .id(item.getId())
+                    .groupId(item.getGroupId())
+                    .userId(item.getUserId())
+                    .clientType(item.getRoleType().getCode())
+                    .avatar(item.getAvatar())
+                    .nickname(item.getNickname())
+                    .isAdmin(item.getIsAdmin())
+                    .imUserId(item.getImUserId())
+                    .roleType(item.getRoleType().getCode())
+                    .build();
+        }).collect(Collectors.toList());
+    }
+
+
+    /**
+     * 自动注册用户
+     * @param groupMembers List<ImGroupMemberWrapper.ImGroupMember>
+     */
+    @Override
+    public void asyncRegisterUser(List<GroupMemberWrapper.ImGroupMember> groupMembers) {
+        com.microsvc.toolkit.common.tools.ThreadPool.getExecutor().submit(() -> registerUser(groupMembers));
+    }
+
+    @Override
     @Transactional(rollbackFor = Exception.class)
     public void dismiss(String groupId) throws Exception {
         Long createBy = Optional.ofNullable(this.baseMapper.selectById(groupId)).
@@ -208,20 +389,21 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
         //删除入群申请
         imGroupMemberAuditDao.delByGroupId(groupId);
         //获取所有群成员
-        List<GroupMember> groupMembers = imGroupMemberService.getDao().queryGroupMember(groupId);
+        List<ImGroupMember> groupMembers = imGroupMemberService.getDao().queryGroupMember(groupId);
         //销毁成功,删除群
         this.baseMapper.deleteById(groupId);
         //删除群成员
         imGroupMemberService.delByGroupId(groupId);
         //解散融云群
-        GroupModel groupModel = new GroupModel(groupId,0);
-        GroupMember[] groupMembersArr = groupMembers.toArray(new GroupMember[groupMembers.size()]);
-        groupModel.setMembers(groupMembersArr);
-        Result result = RongCloudConfig.rongCloud.group.dismiss(groupModel);
-        if(!result.code.equals(200)){
-            log.error("解散群聊失败:{}",result.errorMessage);
-            throw new BizException("解散群聊失败,请联系管理员");
-        }
+
+        // 获取群成员
+        List<GroupMemberWrapper.ImGroupMember> imGroupMembers = groupMembers.stream()
+                .map(x -> GroupMemberWrapper.ImGroupMember.builder().userId(x.getUserId())
+                        .groupId(x.getGroupId()).clientType(x.getRoleType().getCode()).build())
+                .collect(Collectors.toList());
+
+        // 解散群
+        imPluginContext.getPluginService().groupDismiss(groupId, imGroupMembers);
     }
 
     @Override
@@ -268,14 +450,72 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
         //修改群成员数
         baseMapper.updateMemberNum(groupId);
 
-        GroupModel groupModel = new GroupModel(groupId,0);
-        GroupMember[] groupMembers = new GroupMember[]{new GroupMember(imUserId,groupId,null)};
-        groupModel.setMembers(groupMembers);
-        Result result = RongCloudConfig.rongCloud.group.quit(groupModel);
-        if(!result.code.equals(200)){
-            log.error("退出群聊失败:{}",result.errorMessage);
-            throw new BizException("退出群聊失败,请联系管理员");
+
+        // 群主退出
+        List<ImGroupMember> groupMembers = Lists.newArrayList();
+        groupMembers.add(groupMember);
+
+
+        imPluginContext.getPluginService().groupQuit(groupId, getImGroupMembers(groupMembers));
+    }
+
+    @Override
+    public void setTeacherFansGroup() throws Exception {
+        // 查询所有认证老师
+        List<Teacher> teacherList = teacherService.lambdaQuery().eq(Teacher::getEntryFlag, 1).list();
+
+        // 遍历给老师创建粉丝群
+
+        for (Teacher teacher : teacherList) {
+
+            // 已经有群的不操作
+            Integer count = this.lambdaQuery()
+                                .eq(ImGroup::getCreateBy, teacher.getUserId())
+                                .eq(ImGroup::getAutoPassFlag, true)
+                                .eq(ImGroup::getType, ImGroupType.FAN)
+                                .count();
+            if (count > 0) {
+                continue;
+            }
+
+            // 老师用户信息
+            SysUser sysUser = sysUserFeignService.queryUserById(teacher.getUserId());
+            if (sysUser == null) {
+                continue;
+            }
+
+
+            // 查询老师的粉丝,添加到群
+            List<StudentStar> list = studentStarService.lambdaQuery()
+                                                       .eq(StudentStar::getTeacherId, teacher.getUserId())
+                                                       .list();
+            Set<Long> studentIdList = list.stream().map(StudentStar::getStudentId).collect(Collectors.toSet());
+
+
+            //创建群聊
+            ImGroup imGroup = new ImGroup();
+            imGroup.setCreateBy(teacher.getUserId());
+            imGroup.setAutoPassFlag(true);
+            imGroup.setMemberNum(studentIdList.size() + 1);
+            imGroup.setName(sysUser.getUsername() + "的粉丝群");
+            imGroup.setType(ImGroupType.FAN);
+            imGroup.setCreateTime(new Date());
+            imGroup.setUpdateTime(new Date());
+            imGroup.setCreateBy(teacher.getUserId());
+            String imGroupId = UUID.randomUUID() + imGroup.getType().getCode();
+            imGroup.setId(imGroupId);
+            this.baseMapper.insert(imGroup);
+            List<ImGroupMember> groupMembers = imGroupMemberService.initGroupMember(imGroupId, imGroup.getCreateBy(), true, ImGroupMemberRoleType.TEACHER);
+            if (!CollectionUtils.isEmpty(studentIdList)) {
+                groupMembers.addAll(imGroupMemberService.initGroupMembers(imGroupId, studentIdList, ImGroupMemberRoleType.STUDENT));
+            }
+            //创建融云群
+            this.rtcCreate(sysUser.getId(), imGroupId, imGroup.getName());
+            //加入融云群
+            imGroupMemberService.join(groupMembers, imGroupId);
+
         }
+
     }
 
     /**
@@ -317,7 +557,7 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
                         }
 
                         // 自动加入该群组
-                        imGroupMemberService.join(Lists.newArrayList(new GroupMember(imIdentity, groupId, null)), groupId);
+                        imGroupMemberService.join(Lists.newArrayList(groupMember), groupId);
 
                         Integer numbers = imGroupMemberService.lambdaQuery()
                                 .eq(ImGroupMember::getGroupId, groupId)
@@ -326,11 +566,7 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
                                 .count();
                         // 拥有老师身份,且非群主时,需要主动退出群聊
                         if (count > 0 && numbers <= 0) {
-
-                            GroupModel groupModel = new GroupModel(groupId,0);
-                            GroupMember[] groupMembers = new GroupMember[]{new GroupMember(String.valueOf(userId), groupId,null)};
-                            groupModel.setMembers(groupMembers);
-                            RongCloudConfig.rongCloud.group.quit(groupModel);
+                            quit(groupId, userId, ClientEnum.TEACHER);
                         }
 
                         // 缓存重新入群标识, 默认有效期为120天
@@ -345,64 +581,158 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
 
         return group;
     }
-    @Override
-    public void setTeacherFansGroup() throws Exception {
-        // 查询所有认证老师
-        List<Teacher> teacherList = teacherService.lambdaQuery().eq(Teacher::getEntryFlag, 1).list();
 
-        // 遍历给老师创建粉丝群
+    /**
+     * 同步即时通讯聊天记录
+     * @param date
+     * @return
+     */
+    @Override
+    public Result historyGet(String date) throws Exception {
+        return getHistory().get(date);
 
-        for (Teacher teacher : teacherList) {
+    }
 
-            // 已经有群的不操作
-            Integer count = this.lambdaQuery()
-                                .eq(ImGroup::getCreateBy, teacher.getUserId())
-                                .eq(ImGroup::getAutoPassFlag, true)
-                                .eq(ImGroup::getType, ImGroupType.FAN)
-                                .count();
-            if (count > 0) {
-                continue;
+    @Override
+    public void saveImHistoryMessage(File file)  {
+        ZipInputStream zin = null;
+        try {
+            zin = new ZipInputStream(new FileInputStream(file), StandardCharsets.UTF_8);
+            ZipFile zf = new ZipFile(file);
+            ZipEntry ze;
+            Set<ImHistoryMessage> historyMessages = new HashSet<>();
+            while ((ze = zin.getNextEntry()) != null) {
+                BufferedReader br = new BufferedReader(new InputStreamReader(zf.getInputStream(ze)));
+                String line;
+                while ((line = br.readLine()) != null) {
+                    try {
+                        historyMessages.add(JSONObject.parseObject(line.substring(line.indexOf("{")), ImHistoryMessage.class));
+//						if(historyMessages.size() >= 2000){
+//							historyMessageDao.batchInsert(new ArrayList<>(historyMessages),HistoryMessage.class);
+//							historyMessages.clear();
+//						}
+                    }catch (Exception e){
+                        e.printStackTrace();
+                    }
+                }
+                br.close();
+                break;
             }
-
-            // 老师用户信息
-            SysUser sysUser = sysUserFeignService.queryUserById(teacher.getUserId());
-            if (sysUser == null) {
-                continue;
+            if(historyMessages.size() > 0){
+                ImGroupDao dao = getDao();
+                dao.batchInsert(historyMessages.stream().sorted(Comparator.comparing(ImHistoryMessage::getDateTime)).collect(Collectors.toList()));
             }
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            if (zin != null) {
+                try {
+                    zin.closeEntry();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
 
+    @Override
+    public List<ImHistoryMessage> getRongYunInfo() {
+        List<ImHistoryMessage> list = getDao().selectAll();
+        return list;
+    }
 
-            // 查询老师的粉丝,添加到群
-            List<StudentStar> list = studentStarService.lambdaQuery()
-                                                       .eq(StudentStar::getTeacherId, teacher.getUserId())
-                                                       .list();
-            Set<Long> studentIdList = list.stream().map(StudentStar::getStudentId).collect(Collectors.toSet());
+    /**
+     * IM导入消息
+     */
+    @Override
+    public void importInfo(List<ImHistoryMessage> info) throws Exception {
+        URL url = new URL("https://console.tim.qq.com/v4/im_open_login_svc/account_import");
+        info.stream().forEach(i -> {
+            //判断消息类型
+            Integer type = i.getTargetType();
+            if (type == 1) {
+                //单聊会话
+
+                MessageWrapper.PrivateImportMessage privateImportMessage = new MessageWrapper.PrivateImportMessage();
+                //2:表示历史消息导入,消息不计入未读计数,且消息不会推送到终端
+                privateImportMessage.setSyncFromOldSystem(2);
+                //设置发送人
+                privateImportMessage.setFromAccount(i.getFromUserId());
+                //设置接收人
+                privateImportMessage.setToAccount(i.getTargetId());
+                //设置随机数
+                privateImportMessage.setMsgRandom(new Random().nextInt());
+                //设置body
+
+                List<TencentRequest.MessageBody> list = new ArrayList<>();
+                list.stream().forEach(item->{
+                    item.setMsgContent(i.getContent());
+                    item.setMsgType(i.getClassname());
+                });
+                if (list.stream().map(TencentRequest.MessageBody::getMsgType).collect(Collectors.toList()).get(0).equals("RC:TxtMsg")){
+                    //文本对象
+                    list.stream().forEach(item -> item.setMsgType("TIMTextElem"));
+                } else if (list.stream().map(TencentRequest.MessageBody::getMsgType).collect(Collectors.toList()).get(0).equals("RC:ImgMsg")){
+                    //图文对象
+                    list.stream().forEach(item -> item.setMsgType("TIMImageElem"));
+                }
+                privateImportMessage.setTencentMessageBody(list);
+                try {
+                    imPluginContext.getPluginService().importPrivateMessage(privateImportMessage);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+
+
+            } else if (type == 3) {
+                //群组会话
+                MessageWrapper.GroupImportMessage groupImportMessage = new MessageWrapper.GroupImportMessage();
+                List<MessageWrapper.GroupImportMessageData> list = new ArrayList<>();
+                MessageWrapper.GroupImportMessageData data1 = new MessageWrapper.GroupImportMessageData();
+                List<TencentRequest.MessageBody> bodyList = new ArrayList<>();
+                //设置群组Id
+                groupImportMessage.setGroupId(i.getGroupId());
+                //设置发送人
+                data1.setFromAccount(i.getFromUserId());
+                //设置随机数
+                data1.setRandom(new Random().nextInt());
+                //设置发送时间
+                data1.setSendTime(Long.parseLong(i.getDateTime()));
+                bodyList.stream().forEach(item->{
+                            item.setMsgContent(i.getContent());
+                            item.setMsgType(i.getClassname());
+                        });
+                if (bodyList.stream().map(TencentRequest.MessageBody::getMsgType).collect(Collectors.toList()).get(0).equals("RC:TxtMsg")){
+                    bodyList.stream().forEach(item -> item.setMsgType("TIMTextElem"));
+                } else if (bodyList.stream().map(TencentRequest.MessageBody::getMsgType).collect(Collectors.toList()).get(0).equals("RC:ImgMsg")){
+                    //暂未支持图文对象
+                    bodyList.stream().forEach(item -> item.setMsgType("TIMCustomElem"));
+                }
+
+                data1.setTencentMessageBody(bodyList);
+
+                list.add(data1);
+                //导入消息列表
+                groupImportMessage.setMsgList(list);
+                try {
+                    imPluginContext.getPluginService().importGroupMessage(groupImportMessage);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
 
 
-            //创建群聊
-            ImGroup imGroup = new ImGroup();
-            imGroup.setCreateBy(teacher.getUserId());
-            imGroup.setAutoPassFlag(true);
-            imGroup.setMemberNum(studentIdList.size() + 1);
-            imGroup.setName(sysUser.getUsername() + "的粉丝群");
-            imGroup.setType(ImGroupType.FAN);
-            imGroup.setCreateTime(new Date());
-            imGroup.setUpdateTime(new Date());
-            imGroup.setCreateBy(teacher.getUserId());
-            String imGroupId = UUID.randomUUID() + imGroup.getType().getCode();
-            imGroup.setId(imGroupId);
-            this.baseMapper.insert(imGroup);
-            List<GroupMember> groupMembers = imGroupMemberService.initGroupMember(imGroupId, imGroup.getCreateBy(), true, ImGroupMemberRoleType.TEACHER);
-            if (!CollectionUtils.isEmpty(studentIdList)) {
-                groupMembers.addAll(imGroupMemberService.initGroupMembers(imGroupId, studentIdList, ImGroupMemberRoleType.STUDENT));
             }
-            //创建融云群
-            this.rtcCreate(sysUser.getId(), imGroupId, imGroup.getName());
-            //加入融云群
-            imGroupMemberService.join(groupMembers, imGroupId);
+        });
 
-        }
 
     }
 
+
+    private History getHistory(){
+        RongCloud rongCloud = RongCloud.getInstance(appKey, appSecret);
+        History history = new History(appKey, appSecret);
+        history.setRongCloud(rongCloud);
+        return history;
+    }
 }
 

+ 9 - 6
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImNetworkRoomMemberServiceImpl.java

@@ -1,21 +1,19 @@
 package com.yonge.cooleshow.biz.dal.service.impl;
 
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.yonge.cooleshow.auth.api.entity.SysUser;
 import com.yonge.cooleshow.biz.dal.dao.ImNetworkRoomMemberDao;
 import com.yonge.cooleshow.biz.dal.dto.BasicUserInfo;
 import com.yonge.cooleshow.biz.dal.entity.ImNetworkRoomMember;
 import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
 import com.yonge.cooleshow.biz.dal.enums.UserRoleEnum;
+import com.yonge.cooleshow.biz.dal.service.ImGroupService;
 import com.yonge.cooleshow.biz.dal.service.ImNetworkRoomMemberService;
-import org.springframework.stereotype.Service;
-
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
-import java.text.MessageFormat;
-import java.util.Date;
 import java.util.Objects;
 
 /**
@@ -29,6 +27,9 @@ public class ImNetworkRoomMemberServiceImpl extends ServiceImpl<ImNetworkRoomMem
 
     private final static Logger log = LoggerFactory.getLogger(ImNetworkRoomMemberServiceImpl.class);
 
+    @Autowired
+    private ImGroupService imGroupService;
+
     @Override
     public ImNetworkRoomMemberDao getDao() {
         return this.baseMapper;
@@ -57,7 +58,9 @@ public class ImNetworkRoomMemberServiceImpl extends ServiceImpl<ImNetworkRoomMem
 
         String imUserId = String.valueOf(sysUser.getUserId());
         if (UserRoleEnum.STUDENT == userRole) {
-            imUserId = MessageFormat.format("{0}:{1}", imUserId, ClientEnum.STUDENT.name());
+            imUserId = imGroupService.getImUserId(imUserId, ClientEnum.STUDENT.name());
+        }else if(UserRoleEnum.TEACHER == userRole){
+            imUserId = imGroupService.getImUserId(imUserId, ClientEnum.TEACHER.name());
         }
         roomMember.setImUserId(imUserId);
 

+ 28 - 13
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImNetworkRoomServiceImpl.java

@@ -20,6 +20,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
@@ -67,6 +68,9 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
     @Resource
     private RedisTemplate<String,Object> redisTemplate;
 
+    @Autowired
+    private ImGroupService imGroupService;
+
     private String QUIT_ROOM_SUCCESS = "quitRoomSuccess:";
 
     @Override
@@ -113,10 +117,10 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
         for (ImNetworkRoomMember item : roomMemberList) {
             if (EStatus.ENABLE.match(item.getRole())) {
                 // 老师
-                item.setImUserId(String.valueOf(item.getUserId()));
+                item.setImUserId(imGroupService.getImUserId(String.valueOf(item.getUserId()),ClientEnum.TEACHER.name()));
             } else {
                 // 学生
-                item.setImUserId(MessageFormat.format("{0}:{1}", String.valueOf(item.getUserId()), ClientEnum.STUDENT.name()));
+                item.setImUserId(imGroupService.getImUserId(String.valueOf(item.getUserId()),ClientEnum.STUDENT.name()));
             }
 
         }
@@ -136,7 +140,9 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
         // IM用户ID
         String imUserId = sysUser.getUserId().toString();
         if (UserRoleEnum.STUDENT == userRole) {
-            imUserId = MessageFormat.format("{0}:{1}", imUserId, ClientEnum.STUDENT.name());
+            imUserId = imGroupService.getImUserId(imUserId, ClientEnum.STUDENT.name());
+        } else {
+            imUserId = imGroupService.getImUserId(imUserId, ClientEnum.TEACHER.name());
         }
         roomMember.setImUserId(imUserId);
 
@@ -321,7 +327,9 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
 
         String imUserId = String.valueOf(userId);
         if (ClientEnum.STUDENT == customMessage.getClientType()) {
-            imUserId = MessageFormat.format("{0}:{1}", imUserId, ClientEnum.STUDENT.name());
+            imUserId = imGroupService.getImUserId(imUserId, ClientEnum.STUDENT.name());
+        }else if(ClientEnum.TEACHER == customMessage.getClientType()){
+            imUserId = imGroupService.getImUserId(imUserId, ClientEnum.TEACHER.name());
         }
         // 用户ID
         imHelper.publishMessage(imUserId, roomId.toString(), displayMessage, 1);
@@ -371,7 +379,9 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
         // IM用户ID
         String imUserId = String.valueOf(userId);
         if (ClientEnum.STUDENT == musicSheetDto.getClientType()) {
-            imUserId = MessageFormat.format("{0}:{1}", imUserId, ClientEnum.STUDENT.name());
+            imUserId= imGroupService.getImUserId(imUserId,ClientEnum.STUDENT.name());
+        }else if (ClientEnum.TEACHER == musicSheetDto.getClientType()) {
+            imUserId= imGroupService.getImUserId(imUserId,ClientEnum.TEACHER.name());
         }
 
         imHelper.publishMessage(imUserId, courseScheduleId.toString(), msg, 0);
@@ -410,7 +420,9 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
 
         String imUserId = String.valueOf(userId);
         if (ClientEnum.STUDENT == displayData.getClientType()) {
-            imUserId = MessageFormat.format("{0}:{1}", imUserId, ClientEnum.STUDENT.name());
+            imUserId = imGroupService.getImUserId(imUserId, ClientEnum.STUDENT.name());
+        } else if (ClientEnum.TEACHER == displayData.getClientType()) {
+            imUserId = imGroupService.getImUserId(imUserId, ClientEnum.TEACHER.name());
         }
 
         this.updateDisplay(imUserId,room);
@@ -562,9 +574,10 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
         String imUserId = String.valueOf(sysUser.getId());
         UserRoleEnum userRole = UserRoleEnum.TEACHER;
         if (ClientEnum.STUDENT == deviceControl.getClientType()) {
-            imUserId = MessageFormat.format("{0}:{1}", imUserId, ClientEnum.STUDENT.name());
-
+            imUserId = imGroupService.getImUserId(imUserId, ClientEnum.STUDENT.name());
             userRole = UserRoleEnum.STUDENT;
+        }else {
+            imUserId = imGroupService.getImUserId(imUserId, ClientEnum.TEACHER.name());
         }
 
         ImNetworkRoomMember roomMember = Optional.ofNullable(imNetworkRoomMemberService.getDao().findByRidAndUid(deviceControl.getRoomId(), sysUser.getId(),
@@ -611,9 +624,11 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
         UserRoleEnum userRole = UserRoleEnum.TEACHER;
         String imUserId = String.valueOf(userId);
         if (ClientEnum.STUDENT == deviceStatusSync.getClientType()) {
-            imUserId = MessageFormat.format("{0}:{1}", imUserId, ClientEnum.STUDENT.name());
+            imUserId = imGroupService.getImUserId(imUserId,ClientEnum.STUDENT.name());
 
             userRole = UserRoleEnum.STUDENT;
+        }else {
+            imUserId = imGroupService.getImUserId(imUserId, ClientEnum.TEACHER.name());
         }
 
         ImNetworkDeviceTypeEnum deviceType = deviceStatusSync.getDeviceType();
@@ -656,9 +671,7 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
         List<BasicUserInfo> userInfos = courseScheduleStudentPaymentService.getDao().queryNoJoinStu(roomId);
 
         for (BasicUserInfo item : userInfos) {
-
-            item.setImUserId(MessageFormat.format("{0}:{1}", String.valueOf(item.getUserId()),
-                    ClientEnum.STUDENT.name()));
+            item.setImUserId(imGroupService.getImUserId(String.valueOf(item.getUserId()),ClientEnum.STUDENT.name()));
         }
 
         return userInfos;
@@ -714,7 +727,9 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
         // IM用户ID
         String imUserId = String.valueOf(userId);
         if (ClientEnum.STUDENT == musicSheetDto.getClientType()) {
-            imUserId = MessageFormat.format("{0}:{1}", imUserId, ClientEnum.STUDENT.name());
+            imUserId = imGroupService.getImUserId(imUserId, ClientEnum.STUDENT.name());
+        }else {
+            imUserId = imGroupService.getImUserId(imUserId, ClientEnum.TEACHER.name());
         }
 
         imHelper.publishMessage(imUserId, courseSchedule.getTeacherId().toString(), roomId.toString(), statusMessage);

+ 70 - 38
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImUserFriendServiceImpl.java

@@ -6,10 +6,14 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
+import com.microsvc.toolkit.middleware.im.ImPluginContext;
+import com.microsvc.toolkit.middleware.im.impl.TencentCloudImPlugin;
+import com.microsvc.toolkit.middleware.im.message.ETencentMessage;
+import com.microsvc.toolkit.middleware.im.message.MessageWrapper;
+import com.microsvc.toolkit.middleware.im.message.TencentRequest;
 import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
 import com.yonge.cooleshow.auth.api.entity.SysUser;
 import com.yonge.cooleshow.auth.config.CustomerServiceConfig;
-import com.yonge.cooleshow.biz.dal.config.RongCloudConfig;
 import com.yonge.cooleshow.biz.dal.dao.ImUserFriendDao;
 import com.yonge.cooleshow.biz.dal.dao.TeacherDao;
 import com.yonge.cooleshow.biz.dal.dto.BasicUserInfo;
@@ -17,6 +21,7 @@ import com.yonge.cooleshow.biz.dal.entity.ImUserFriend;
 import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
 import com.yonge.cooleshow.biz.dal.enums.MK;
 import com.yonge.cooleshow.biz.dal.mapper.SysUserMapper;
+import com.yonge.cooleshow.biz.dal.service.ImGroupService;
 import com.yonge.cooleshow.biz.dal.service.ImUserFriendService;
 import com.yonge.cooleshow.biz.dal.wrapper.im.CustomerService;
 import com.yonge.cooleshow.biz.dal.wrapper.im.ImUserWrapper;
@@ -46,6 +51,7 @@ import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
 
+
 /**
  * 用户通讯录表(ImUserFriend)表服务实现类
  *
@@ -67,6 +73,11 @@ public class ImUserFriendServiceImpl extends ServiceImpl<ImUserFriendDao, ImUser
     @Autowired
     private ImUserFriendService imUserFriendService;
 
+    @Autowired
+    private ImGroupService imGroupService;
+
+    @Autowired
+    private ImPluginContext imPluginContext;
     @Override
     public ImUserFriendDao getDao() {
         return this.baseMapper;
@@ -225,21 +236,26 @@ public class ImUserFriendServiceImpl extends ServiceImpl<ImUserFriendDao, ImUser
                     new PushExt.APNs("", ""),
                     new PushExt.OPPO(""));
 
-            String imUserId = String.valueOf(userId);
-            if (ClientEnum.STUDENT == clientType) {
-                imUserId = MessageFormat.format("{0}:{1}", imUserId, ClientEnum.STUDENT.name());
-            }
-            // 发送用户IM通知消息
-            PrivateMessage privateMessage = new PrivateMessage()
-                    .setSenderId(String.valueOf(friendIds.get(0)))
-                    .setTargetId(new String[]{imUserId})
-                    .setObjectName(txtMessage.getType())
-                    .setContent(txtMessage)
-                    .setPushExt(pushExt)
-                    .setIsIncludeSender(1);
-
-            ResponseResult privateResult = RongCloudConfig.rongCloud.message.msgPrivate.send(privateMessage);
-            log.info("registerUserBindCustomerService userId={}, ret={}", userId, privateResult.getCode());
+            MessageWrapper.PrivateMessage build = MessageWrapper.PrivateMessage.builder()
+                    .senderId(imGroupService.getImUserId(friendIds.get(0).toString(),ClientEnum.TEACHER.getCode()))
+                    .targetIds(Lists.newArrayList(imGroupService.getImUserId(userId.toString(),clientType.getCode())))
+                    .objectName(txtMessage.getType())
+                    .rongCloueMessage(txtMessage)
+                    .pushExt(JSON.toJSONString(pushExt))
+                    .includeSender(0)
+                    .build();
+
+
+            // 腾讯云消息
+            TencentRequest.MessageBody tencentMessages = TencentRequest.MessageBody.builder()
+                    .msgType(ETencentMessage.TIMTextElem.name())
+                    .msgContent(customerMessage)
+                    .build();
+            build.setTencentMessage(tencentMessages);
+
+
+            Boolean ret = imPluginContext.getPluginService().sendPrivateMessage(build);
+            log.info("batchSendPrivateMessage GROUP senderId={},data= {} ret={}", userId,build, ret);
         } catch (Exception e) {
             log.error("registerUserBindCustomerService userId={}", userId, e);
         }
@@ -271,11 +287,9 @@ public class ImUserFriendServiceImpl extends ServiceImpl<ImUserFriendDao, ImUser
             receiveUserIds = sysUserMapper.selectMessageReceives(info).stream()
                     .map(x -> String.valueOf(x.getUserId()))
                     .collect(Collectors.toList());
-            if (ClientEnum.STUDENT == info.getClientType()) {
-                receiveUserIds = receiveUserIds.stream()
-                        .map(x -> MessageFormat.format("{0}:STUDENT", x))
-                        .collect(Collectors.toList());
-            }
+            receiveUserIds = receiveUserIds.stream()
+                    .map(x -> imGroupService.getImUserId(x, ClientEnum.STUDENT.getCode()))
+                    .collect(Collectors.toList());
         } else {
 
             LambdaQueryWrapper<com.yonge.cooleshow.biz.dal.entity.SysUser> wrapper =
@@ -301,7 +315,11 @@ public class ImUserFriendServiceImpl extends ServiceImpl<ImUserFriendDao, ImUser
         // 发送文本消息
         TxtMessage txtMessage = new TxtMessage(info.getTxtMessage(), "");
 
-        List<BaseMessage> messages = Lists.newArrayList(imgMessage, txtMessage);
+        List<BaseMessage> baseMessages = Lists.newArrayList(imgMessage, txtMessage);
+
+        List<TencentRequest.MessageBody> tencentMessages = Lists.newArrayList();
+        tencentMessages.add(CustomerServiceBatchSendingServiceImpl.getTimTextElem(info.getTxtMessage()));
+        tencentMessages.add(CustomerServiceBatchSendingServiceImpl.getTimImageElem(info.getImgMessage()));
 
         // 拓展消息
         PushExt pushExt = PushExt.build(info.getTitle(), 1,
@@ -309,26 +327,40 @@ public class ImUserFriendServiceImpl extends ServiceImpl<ImUserFriendDao, ImUser
                 new PushExt.APNs("", ""),
                 new PushExt.OPPO(""));
 
-        String senderId = String.valueOf(senderUser.getId());
-        PrivateMessage privateMessage;
-        ResponseResult privateResult;
+        String senderId = imGroupService.getImUserId(senderUser.getId().toString(), ClientEnum.TEACHER.getCode());
         for (List<String> item : Lists.partition(receiveUserIds, 100)) {
 
             try {
 
-                for (BaseMessage message : messages) {
-
-                    // 发送用户IM通知消息
-                    privateMessage = new PrivateMessage()
-                            .setSenderId(senderId)
-                            .setTargetId(item.toArray(new String[0]))
-                            .setObjectName(message.getType())
-                            .setContent(message)
-                            .setPushExt(pushExt)
-                            .setIsIncludeSender(0);
-
-                    privateResult = RongCloudConfig.rongCloud.message.msgPrivate.send(privateMessage);
-                    log.info("sendCustomerServiceNotifyMessage senderId={}, ret={}", senderId, privateResult.getCode());
+                // 发送用户IM通知消息
+                MessageWrapper.PrivateMessage build = MessageWrapper.PrivateMessage.builder()
+                        .senderId(senderId)
+                        .targetIds(item)
+//                        .objectName(message.getType())
+//                        .rongCloueMessage(message)
+                        .pushExt(JSON.toJSONString(pushExt))
+                        .includeSender(0)
+//                        .tencentMessage()
+                        .build();
+
+                if (TencentCloudImPlugin.PLUGIN_NAME.equals(imPluginContext.defaultService())) {
+                    // 腾讯云消息
+                    for (TencentRequest.MessageBody message : tencentMessages) {
+                        if (message == null) {
+                            continue;
+                        }
+                        Boolean ret = imPluginContext.getPluginService().sendPrivateMessage(build.objectName(message.getMsgType()).tencentMessage(message));
+                        log.info("batchSendPrivateMessage GROUP tencentCloud senderId={}, ret={}", senderId, ret);
+                    }
+                } else {
+                    // 融云消息
+                    for (BaseMessage message : baseMessages) {
+                        if (message == null){
+                            continue;
+                        }
+                        Boolean ret = imPluginContext.getPluginService().sendPrivateMessage(build.objectName(message.getType()).rongCloueMessage(message));
+                        log.info("batchSendPrivateMessage GROUP rongCloud senderId={}, ret={}", senderId, ret);
+                    }
                 }
 
             } catch (Exception e) {

+ 598 - 90
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/LiveRoomServiceImpl.java

@@ -1,6 +1,5 @@
 package com.yonge.cooleshow.biz.dal.service.impl;
 
-import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.COOLESHOW;
 import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.LIVE_ROOM_HEART_BEAT;
 import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.LIVE_ROOM_INFO;
 import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.LIVE_ROOM_LIKE;
@@ -15,14 +14,10 @@ import static com.yonge.cooleshow.biz.dal.constant.LiveRoomConstant.USER_ID;
 import static com.yonge.cooleshow.common.constant.SysConfigConstant.DESTROY_EXPIRED_LIVE_ROOM_MINUTE;
 import static com.yonge.cooleshow.common.constant.SysConfigConstant.PRE_CREATE_LIVE_ROOM_MINUTE;
 
+import java.text.MessageFormat;
 import java.time.LocalDate;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
+import java.time.ZoneId;
+import java.util.*;
 import java.util.concurrent.TimeUnit;
 import java.util.function.BiFunction;
 import java.util.function.BiPredicate;
@@ -31,10 +26,23 @@ import java.util.function.Function;
 import java.util.stream.Collectors;
 
 import com.alibaba.fastjson.JSON;
+import com.microsvc.toolkit.middleware.live.LivePluginContext;
+import com.microsvc.toolkit.middleware.live.LivePluginService;
+import com.microsvc.toolkit.middleware.live.impl.RongCloudLivePlugin;
+import com.microsvc.toolkit.middleware.live.impl.TencentCloudLivePlugin;
+import com.microsvc.toolkit.middleware.live.message.*;
+import com.yonge.cooleshow.biz.dal.dto.LiveRoomStatus;
+import com.yonge.cooleshow.biz.dal.dto.TencentData;
+import com.yonge.cooleshow.biz.dal.entity.*;
+import com.yonge.cooleshow.biz.dal.enums.live.EAnchorStatus;
+import com.yonge.cooleshow.biz.dal.enums.live.EOnOffStatus;
 import com.yonge.cooleshow.biz.dal.mapper.SysUserMapper;
+import com.yonge.cooleshow.biz.dal.service.*;
 import com.yonge.cooleshow.biz.dal.wrapper.liveroom.LiveRoomWrapper;
+import com.yonge.cooleshow.common.enums.EGroupDefinedDataType;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.joda.time.DateTime;
 import org.redisson.api.RBucket;
 import org.redisson.api.RMap;
 import org.redisson.api.RedissonClient;
@@ -52,17 +60,6 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
 import com.yonge.cooleshow.auth.api.entity.SysUser;
 import com.yonge.cooleshow.biz.dal.dao.LiveRoomDao;
-import com.yonge.cooleshow.biz.dal.entity.CourseSchedule;
-import com.yonge.cooleshow.biz.dal.entity.CourseScheduleStudentPayment;
-import com.yonge.cooleshow.biz.dal.entity.CourseScheduleTeacherSalary;
-import com.yonge.cooleshow.biz.dal.entity.IMApiResultInfo;
-import com.yonge.cooleshow.biz.dal.entity.ImRoomMessage;
-import com.yonge.cooleshow.biz.dal.entity.ImUserStateSync;
-import com.yonge.cooleshow.biz.dal.entity.LiveRoom;
-import com.yonge.cooleshow.biz.dal.entity.RoomInfoCache;
-import com.yonge.cooleshow.biz.dal.entity.RoomUserInfoCache;
-import com.yonge.cooleshow.biz.dal.entity.StudentAttendance;
-import com.yonge.cooleshow.biz.dal.entity.TeacherAttendance;
 import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
 import com.yonge.cooleshow.biz.dal.enums.CourseScheduleEnum;
 import com.yonge.cooleshow.biz.dal.enums.InOrOutEnum;
@@ -70,17 +67,6 @@ import com.yonge.cooleshow.biz.dal.enums.MessageTypeEnum;
 import com.yonge.cooleshow.biz.dal.enums.OrderStatusEnum;
 import com.yonge.cooleshow.biz.dal.enums.RoomTypeEnum;
 import com.yonge.cooleshow.biz.dal.enums.TeacherSalaryEnum;
-import com.yonge.cooleshow.biz.dal.service.CourseGroupService;
-import com.yonge.cooleshow.biz.dal.service.CourseScheduleService;
-import com.yonge.cooleshow.biz.dal.service.CourseScheduleStudentPaymentService;
-import com.yonge.cooleshow.biz.dal.service.CourseScheduleTeacherSalaryService;
-import com.yonge.cooleshow.biz.dal.service.LiveBroadcastRoomMemberService;
-import com.yonge.cooleshow.biz.dal.service.LiveRoomService;
-import com.yonge.cooleshow.biz.dal.service.StudentAttendanceService;
-import com.yonge.cooleshow.biz.dal.service.SysConfigService;
-import com.yonge.cooleshow.biz.dal.service.SysMessageService;
-import com.yonge.cooleshow.biz.dal.service.TeacherAttendanceService;
-import com.yonge.cooleshow.biz.dal.service.UserOrderService;
 import com.yonge.cooleshow.biz.dal.support.IMHelper;
 import com.yonge.cooleshow.biz.dal.support.WrapperUtil;
 import com.yonge.cooleshow.biz.dal.vo.TeacherLivingInfoVo;
@@ -131,6 +117,14 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
     private LiveBroadcastRoomMemberService liveBroadcastRoomMemberService;
     @Autowired
     private SysUserMapper sysUserMapper;
+    @Autowired
+    private LivePluginContext livePluginContext;
+
+    @Autowired
+    private LiveRoomService liveRoomService;
+
+    @Autowired
+    private LiveRoomVideoService liveRoomVideoService;
     /**
      * 生成房间UID
      *
@@ -138,7 +132,7 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
      * <p> en :房间类型
      */
     private final BiFunction<Long, RoomTypeEnum, String> GenRoomUid = (userId, en) -> String
-            .join("-", COOLESHOW, en.getCode(), userId.toString(), new Date().getTime() + "");
+            .join("-", en.getCode(), userId.toString(), new Date().getTime() + "");
 
     @Override
     public LiveRoomDao getDao() {
@@ -253,38 +247,49 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
         RoomTypeEnum en = RoomTypeEnum.LIVE;
         Date now = new Date();
         courseScheduleList.forEach(c -> {
-            //课程开始时间-提前创建时间 = 创建房间时间
-            Date createRoomTime = DateUtil.addMinutes(c.getStartTime(), -Integer.parseInt(preCreateRoomMinute));
-            if (now.getTime() < createRoomTime.getTime()) {
-                return;
-            }
-            //避免重复创建直播间
-            int count = this.count(Wrappers.<LiveRoom>lambdaQuery().eq(LiveRoom::getCourseId, c.getId()));
-            if (count > 0) {
-                return;
+            try {
+                liveRoomService.updateLiveCourseSchedule(preCreateRoomMinute, titleMap, remarkMap, en, now, c);
+            } catch (Exception e) {
+                log.error("createCourseLiveRoom>>>生成直播间失败:{}", c, e);
             }
-            LiveRoom room = new LiveRoom();
-            room.setCourseGroupId(c.getCourseGroupId());
-            room.setCourseId(c.getId());
-            room.setRoomUid(GenRoomUid.apply(c.getTeacherId(), en));
-            room.setRoomTitle(titleMap.get(c.getCourseGroupId()));
-            room.setLiveRemark(remarkMap.get(c.getCourseGroupId()));
-            room.setSpeakerId(c.getTeacherId());
-            room.setLiveStartTime(c.getStartTime());
-            room.setLiveEndTime(c.getEndTime());
-            room.setLiveState(1);
-            room.setRoomState(0);
-            room.setType(en.getCode());
-            room.setCreatedBy(-2L);
-            room.setCreatedTime(now);
-            this.save(room);
-            //去融云创建房间及创建房间缓存信息
-            this.createLiveRoomInfo(room);
-            //开课提醒
-            this.pushLiveCreateRoom(room);
         });
     }
 
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void updateLiveCourseSchedule(String preCreateRoomMinute, Map<Long, String> titleMap, Map<Long, String> remarkMap, RoomTypeEnum en, Date now, CourseSchedule c) {
+        //课程开始时间-提前创建时间 = 创建房间时间
+        Date createRoomTime = DateUtil.addMinutes(c.getStartTime(), -Integer.parseInt(preCreateRoomMinute));
+        if (now.getTime() < createRoomTime.getTime()) {
+            return;
+        }
+        //避免重复创建直播间
+        if (StringUtils.isNotBlank(c.getRoomUid())) {
+            log.info("createCourseLiveRoom>>>roomUid:{} 已存在", c.getRoomUid());
+            return;
+        }
+        LiveRoom room = new LiveRoom();
+        room.setRoomUid(GenRoomUid.apply(c.getTeacherId(), en));
+        room.setRoomTitle(titleMap.get(c.getCourseGroupId()));
+        room.setLiveRemark(remarkMap.get(c.getCourseGroupId()));
+        room.setSpeakerId(c.getTeacherId());
+        room.setLiveStartTime(c.getStartTime());
+        room.setLiveEndTime(c.getEndTime());
+        room.setLiveState(1);
+        room.setRoomState(0);
+        room.setType(en.getCode());
+        room.setCreatedBy(-2L);
+        room.setCreatedTime(now);
+        this.save(room);
+        // 更新课程房间信息
+        c.setRoomUid(room.getRoomUid());
+        courseScheduleService.updateById(c);
+        //去融云创建房间及创建房间缓存信息
+        this.createLiveRoomInfo(room);
+        //开课提醒
+        this.pushLiveCreateRoom(room);
+    }
+
     /**
      * 开课提醒
      */
@@ -342,8 +347,6 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
         RoomTypeEnum en = RoomTypeEnum.TEMP;
         String roomUid = GenRoomUid.apply(teacherId, en);
         LiveRoom room = new LiveRoom();
-        room.setCourseGroupId(-1L);
-        room.setCourseId(-1L);
         room.setRoomUid(roomUid);
         room.setRoomTitle(roomTitle);
         room.setLiveRemark(liveRemark);
@@ -380,12 +383,111 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
             //生成主讲人信息
             this.createRoomInfoCache(room, sysUser);
             //去融云创建房间
-            this.createLiveRoom(room.getRoomUid(), room.getRoomTitle());
+
+            //去融云创建房间
+            LivePluginService pluginService = livePluginContext.getPluginService(room.getServiceProvider());
+
+            // 注册主播用户信息到三方平台
+            pluginService.register(sysUser.getId().toString(), sysUser.getUsername(), sysUser.getAvatar());
+            // 创建直播间IM群
+            pluginService.chatRoomCreate(room.getRoomUid(), room.getRoomTitle(),sysUser.getId().toString());
+            // 腾讯云直播,提前生成录制规则
+            if (room.getServiceProvider().equals(TencentCloudLivePlugin.PLUGIN_NAME)) {
+
+                DateTime now = DateTime.now();
+
+                RTCRequest.RecordStart recordStart = RTCRequest.RecordStart.builder()
+                        .streamName(MessageFormat.format("{0}_{1}", room.getRoomUid(), room.getSpeakerId().toString()))
+                        .extra("")
+                        .startTime(now.toDateTime().getMillis())
+                        .endTime(now.plusDays(1).toDateTime().getMillis())
+                        .build();
+
+                if (StringUtils.isBlank(room.getVideoRecord())) {
+
+                    // 生成录制任务
+                    log.info("createLiveRoom>>>>>> recordStart:{}", recordStart.jsonString());
+                    RTCRoom.RecordResp resp = pluginService.rtcRoomRecordStart(recordStart);
+
+                    // 设置录制任务Id
+                    LiveRoom update = new LiveRoom();
+                    update.setId(room.getId());
+                    if (StringUtils.isBlank(room.getVideoRecord())) {
+                        update.setVideoRecord(resp.getRecordId());
+                    } else {
+                        update.setVideoRecord(room.getVideoRecord() + "," + resp.getRecordId());
+                    }
+                    updateById(update);
+                }
+                // 设置直播间房间属性默认值
+                setDefaultRoomDefinedInfo(room);
+            }
         } catch (Exception e) {
             throw new BizException("创建直播间失败!", e.getCause());
         }
     }
 
+
+    private void setDefaultRoomDefinedInfo(LiveRoom roomVo) {
+        List<TencentRequest.ChatRoomGroupDefinedData> appDefinedData = new ArrayList<>();
+        Arrays.stream(EGroupDefinedDataType.values()).forEach(key -> {
+            switch (key) {
+                case ANCHOR_STATUS:
+                    appDefinedData.add(TencentRequest.ChatRoomGroupDefinedData.builder()
+                            .key(key.getCode())
+                            .value("OFFLINE")
+                            .build());
+                    break;
+                case GLOBAL_BAN:
+                    appDefinedData.add(TencentRequest.ChatRoomGroupDefinedData.builder()
+                            .key(key.getCode())
+                            .value("OFF")
+                            .build());
+                    break;
+                case LIKES:
+                    appDefinedData.add(TencentRequest.ChatRoomGroupDefinedData.builder()
+                            .key(key.getCode())
+                            .value("0")
+                            .build());
+                    break;
+
+                case MEMBER_ONLINE:
+                    appDefinedData.add(TencentRequest.ChatRoomGroupDefinedData.builder()
+                            .key(key.getCode())
+                            .value("0")
+                            .build());
+                    break;
+                case MEMBER_TOTAL:
+                    appDefinedData.add(TencentRequest.ChatRoomGroupDefinedData.builder()
+                            .key(key.getCode())
+                            .value("0")
+                            .build());
+                    break;
+                case LIVE_STATUS:
+                    appDefinedData.add(TencentRequest.ChatRoomGroupDefinedData.builder()
+                            .key(key.getCode())
+                            .value("OFF")
+                            .build());
+                    break;
+                case ANCHOR_CAMERA:
+                    appDefinedData.add(TencentRequest.ChatRoomGroupDefinedData.builder()
+                            .key(key.getCode())
+                            .value("ON")
+                            .build());
+                    break;
+            }
+        });
+        try {
+            livePluginContext.getPluginService(roomVo.getServiceProvider())
+                    .chatRoomGroupDefinedData(TencentRequest.ChatRoomGroup.builder()
+                            .groupId(roomVo.getRoomUid())
+                            .appDefinedData(appDefinedData)
+                            .build());
+        } catch (Exception e) {
+            log.error("设置直播群配置失败", e);
+        }
+    }
+
     //生成主讲人信息
     private void createRoomInfoCache(LiveRoom room, SysUser sysUser) {
         RoomInfoCache roomCache = new RoomInfoCache();
@@ -404,8 +506,8 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
         roomCache.setExpiredMinute(Integer.parseInt(configValue));
         roomCache.setExpiredCloseMinute(Integer.parseInt(expireCloseMinute));
         roomCache.setRoomType(room.getType());
-        roomCache.setCourseId(room.getCourseId());
-        roomCache.setCourseGroupId(room.getCourseGroupId());
+//        roomCache.setCourseId(room.getCourseId());
+//        roomCache.setCourseGroupId(room.getCourseGroupId());
 
         //写入房间信息
         getLiveRoomInfo(room.getRoomUid()).set(roomCache, 2L, TimeUnit.DAYS);
@@ -439,6 +541,7 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
     /**
      * 定时任务-清理过期的房间
      */
+    @Override
     public void destroyExpiredLiveRoom() {
         //查询房间过期时间
         String expiredMinuteStr = sysConfigService.findConfigValue(DESTROY_EXPIRED_LIVE_ROOM_MINUTE);
@@ -464,7 +567,7 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
             }
             //当前时间 大于 直播间过期时间
             if (now.getTime() >= expiredDate.getTime()) {
-                this.destroyLiveRoom(room);
+                liveRoomService.destroyLiveRoom(room.getRoomUid());
             }
         });
     }
@@ -481,6 +584,20 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
         this.destroyLiveRoom(liveRoom);
     }
 
+
+    private CourseSchedule getCourseScheduleByRoomUid(String roomUid) {
+        CourseSchedule one = courseScheduleService.lambdaQuery()
+                .eq(CourseSchedule::getRoomUid, roomUid)
+                .last("limit 1")
+                .one();
+        if (Objects.isNull(one)) {
+            one = new CourseSchedule();
+            one.setId(-1L);
+            one.setCourseGroupId(-1L);
+        }
+
+        return one;
+    }
     /**
      * 关闭直播间
      *
@@ -490,11 +607,12 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
         if (Objects.isNull(room)) {
             return;
         }
+        CourseSchedule schedule = getCourseScheduleByRoomUid(room.getRoomUid());
         //直播课
         if (room.getType().equals(RoomTypeEnum.LIVE.getCode())) {
             //查询老师分润表
             List<CourseScheduleTeacherSalary> salaryList = courseScheduleTeacherSalaryService.list(Wrappers.<CourseScheduleTeacherSalary>lambdaQuery()
-                    .eq(CourseScheduleTeacherSalary::getCourseScheduleId, room.getCourseId())
+                    .eq(CourseScheduleTeacherSalary::getCourseScheduleId,schedule.getId())
             );
             if (CollectionUtils.isEmpty(salaryList)) {
                 return;
@@ -532,7 +650,7 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
         //给老师签退
         teacherAttendanceService.update(Wrappers.<TeacherAttendance>lambdaUpdate()
                 .eq(TeacherAttendance::getTeacherId, room.getSpeakerId())
-                .eq(TeacherAttendance::getCourseScheduleId, room.getCourseId())
+                .eq(TeacherAttendance::getCourseScheduleId, schedule.getId())
                 .set(TeacherAttendance::getSignOutTime, now));
         //删除老师与房间关联关系
         deleteUserRoomCache.accept(speakerIdStr);
@@ -544,7 +662,7 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
             //观看者签退
             studentAttendanceService.update(Wrappers.<StudentAttendance>lambdaUpdate()
                     .eq(StudentAttendance::getStudentId, id)
-                    .eq(StudentAttendance::getCourseScheduleId, room.getCourseId())
+                    .eq(StudentAttendance::getCourseScheduleId,schedule.getId())
                     .set(StudentAttendance::getSignOutTime, now));
         });
         //删除房间在线人员缓存
@@ -603,7 +721,7 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
             if (!userRoom.isExists()) {
                 return;
             }
-            String roomUid = userRoom.get();
+            String roomUid = Optional.ofNullable(user.getRoomUid()).orElse(userRoom.get());
 
             Date now = new Date();
             //获取当前用户状态变更的时间
@@ -613,6 +731,7 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
                 //缓存的时间比当前传入时间大则放弃这条数据
                 long cacheTime = userStateTimeCache.get();
                 if (cacheTime > userStateTime) {
+                    log.debug("opsRoom>>>> cacheTime > userStateTime, cacheTime: {}, userStateTime: {}", cacheTime, userStateTime);
                     return;
                 }
             }
@@ -623,6 +742,19 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
             if (!roomInfoCache.isExists()) {
                 return;
             }
+
+            // 查询房间信息
+            LiveRoom roomVo = getByRoomUid(roomUid);
+            if (Objects.isNull(roomVo)) {
+                log.info("opsRoom>>>> roomVo is null, userState: {}", JSON.toJSONString(userState));
+                return;
+            }
+
+            // 腾讯云直播不需要处理融云im的消息
+            if (roomVo.getServiceProvider().equals(TencentCloudLivePlugin.PLUGIN_NAME) && StringUtils.isNotBlank(user.getClientIp())) {
+                return;
+            }
+
             RoomInfoCache roomInfo = roomInfoCache.get();
             // 查询userId是不是主讲人 ,如果是主讲人则返回
             if (roomInfo.getSpeakerId().toString().equals(userIdStr)) {
@@ -654,13 +786,14 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
             if (!onlineUserInfo.isExists()) {
                 return;
             }
+            CourseSchedule schedule = getCourseScheduleByRoomUid(roomUid);
             liveBroadcastRoomMemberService.saveRecord(roomUid,userId, InOrOutEnum.OUT, YesOrNoEnum.NO);
             //从在线人员列表删除该人员
             onlineUserInfo.fastRemove(userId);
             //学员退出 写学生考勤表
             studentAttendanceService.update(Wrappers.<StudentAttendance>lambdaUpdate()
                     .eq(StudentAttendance::getStudentId, userId)
-                    .eq(StudentAttendance::getCourseScheduleId, roomInfo.getCourseId())
+                    .eq(StudentAttendance::getCourseScheduleId, schedule.getId())
                     .set(StudentAttendance::getSignOutTime, now));
             //向直播间发送当前在线人数消息
             this.sendOnlineUserCount(roomUid, userId, onlineUserInfo.size());
@@ -682,16 +815,26 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
 
     }
 
+    private LiveRoom getByRoomUid(String roomUid) {
+        return this.lambdaQuery()
+                .eq(LiveRoom::getRoomUid, roomUid)
+                .last("limit 1")
+                .one()  ;
+    }
+
     /**
      * 主讲人
      */
     private void opsSpeaker(RBucket<RoomInfoCache> roomInfoCache, ImUserStateSync user, Date now, String userIdStr) {
         RoomInfoCache roomInfo = roomInfoCache.get();
         String roomUid = roomInfo.getRoomUid();
+
+
+        CourseSchedule schedule = getCourseScheduleByRoomUid(roomUid);
         //进退房间写老师考勤表
         TeacherAttendance teacherAttendance = teacherAttendanceService.getOne(Wrappers.<TeacherAttendance>lambdaQuery()
                 .eq(TeacherAttendance::getTeacherId, userIdStr)
-                .eq(TeacherAttendance::getCourseScheduleId, roomInfo.getCourseId()));
+                .eq(TeacherAttendance::getCourseScheduleId, schedule.getId()));
 
         //最后一次进入房间的clientIp
         RBucket<String> lastClientIp = redissonClient.getBucket(LIVE_USER_LAST_CLIENT_IP.replace(ROOM_UID, roomUid).replace(USER_ID, userIdStr));
@@ -703,9 +846,9 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
             if (StringUtils.isNotBlank(user.getClientIp())) {
                 lastClientIp.set(user.getClientIp());
             }
-            //查询老师是否有进入过放假,没有则写老师考勤表的进入时间
+            //查询老师是否有进入过房间,没有则写老师考勤表的进入时间
             if (Objects.isNull(teacherAttendance)) {
-                this.setTeacherAttendance(Long.parseLong(userIdStr), roomInfo.getCourseGroupId(), roomInfo.getCourseId());
+                this.setTeacherAttendance(Long.parseLong(userIdStr), schedule.getCourseGroupId(), schedule.getId());
             }
             roomInfoCache.set(roomInfo);
             log.info("opsRoom>>>> join speakerCache {}", JSONObject.toJSONString(roomInfo));
@@ -731,8 +874,8 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
             teacherAttendance = new TeacherAttendance();
             teacherAttendance.setTeacherId(Long.parseLong(userIdStr));
             teacherAttendance.setCourseGroupType(CourseScheduleEnum.LIVE.getCode());
-            teacherAttendance.setCourseGroupId(roomInfo.getCourseGroupId());
-            teacherAttendance.setCourseScheduleId(roomInfo.getCourseId());
+            teacherAttendance.setCourseGroupId(schedule.getCourseGroupId());
+            teacherAttendance.setCourseScheduleId(schedule.getId());
             teacherAttendance.setSignInTime(now);
             teacherAttendance.setSignOutTime(now);
             teacherAttendance.setCreateTime(now);
@@ -745,6 +888,29 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
         }
         log.info("opsRoom>>>> exit speakerCache {}", JSONObject.toJSONString(roomInfo));
         roomInfoCache.set(roomInfo);
+
+
+        // 设置直播群组自定义数据
+        setGroupDefinedData(getByRoomUid(roomUid),EGroupDefinedDataType.ANCHOR_STATUS,"OFFLINE");
+
+    }
+
+    private void setGroupDefinedData(LiveRoom roomVo, EGroupDefinedDataType type, String value) {
+        List<TencentRequest.ChatRoomGroupDefinedData> appDefinedData = new ArrayList<>();
+        appDefinedData.add(TencentRequest.ChatRoomGroupDefinedData.builder()
+                .key(type.getCode())
+                .value(value)
+                .build());
+        try {
+            livePluginContext.getPluginService(roomVo.getServiceProvider())
+                    .chatRoomGroupDefinedData(TencentRequest.ChatRoomGroup.builder()
+                            .groupId(roomVo.getRoomUid())
+                            .appDefinedData(appDefinedData)
+                            .build());
+            log.info("设置直播群配置成功 roomUid:{}, data {}",roomVo.getRoomUid(), JSONObject.toJSONString(appDefinedData));
+        } catch (Exception e) {
+            log.error("设置直播群配置失败", e);
+        }
     }
 
     /**
@@ -780,12 +946,14 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
      */
     public RoomInfoCache speakerJoinRoom(String roomUid) {
         RoomInfoCache roomInfo = this.speakerCheckRoomInfo(roomUid);
+
+        CourseSchedule schedule = getCourseScheduleByRoomUid(roomUid);
         Date now = new Date();
         roomInfo.setSpeakerState(0);
         roomInfo.setJoinRoomTime(now);
         liveBroadcastRoomMemberService.saveRecord(roomUid, roomInfo.getSpeakerId(), InOrOutEnum.IN, YesOrNoEnum.NO);
         //查询老师是否有进入过,没有则写老师考勤表的进入时间
-        this.setTeacherAttendance(roomInfo.getSpeakerId(), roomInfo.getCourseGroupId(), roomInfo.getCourseId());
+        this.setTeacherAttendance(roomInfo.getSpeakerId(), schedule.getCourseGroupId(), schedule.getId());
         //记录当前用户对应的房间uid
         redissonClient.getBucket(LIVE_USER_ROOM.replace(USER_ID, roomInfo.getSpeakerId().toString())).set(roomUid, 2L, TimeUnit.DAYS);
         return roomInfo;
@@ -820,8 +988,11 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
             userInfo.setUserName(sysUser.getRealName());
             userInfo.setFirstJoinTime(now);
         }
+
+
+        CourseSchedule schedule = getCourseScheduleByRoomUid(roomUid);
         //查询学生是否有进入过,没有则写学生考勤表的进入时间
-        this.setStudentAttendance(userId, roomInfo.getCourseGroupId(), roomInfo.getCourseId());
+        this.setStudentAttendance(userId, schedule.getCourseGroupId(), schedule.getId());
         userInfo.setDynamicJoinTime(now);
         //用户json信息
         String userJsonStr = JSONObject.toJSONString(userInfo);
@@ -848,13 +1019,15 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
             throw new BizException("直播还未开始或已结束!");
         }
         RoomInfoCache roomInfo = roomInfoCache.get();
+
+        CourseSchedule schedule = getCourseScheduleByRoomUid(roomUid);
         //校验观看者是否买过课,如果是非临时直播间,则校验是否已经买过课
         if (!roomInfo.getRoomType().equals(RoomTypeEnum.TEMP.getCode())) {
             // 查询该学员成功购买课程
             CourseScheduleStudentPayment studentPayment = courseScheduleStudentPaymentService.getOne(Wrappers.<CourseScheduleStudentPayment>lambdaQuery()
                     .eq(CourseScheduleStudentPayment::getUserId, sysUser.getId())
-                    .eq(CourseScheduleStudentPayment::getCourseId, roomInfo.getCourseId())
-                    .eq(CourseScheduleStudentPayment::getCourseGroupId, roomInfo.getCourseGroupId())
+                    .eq(CourseScheduleStudentPayment::getCourseId, schedule.getId())
+                    .eq(CourseScheduleStudentPayment::getCourseGroupId, schedule.getCourseGroupId())
                     .eq(CourseScheduleStudentPayment::getCourseType, CourseScheduleEnum.LIVE.getCode()));
             if (Objects.nonNull(studentPayment)) {
                 UserOrderVo detail = userOrderService.detail(studentPayment.getOrderNo(), studentPayment.getUserId());
@@ -967,16 +1140,41 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
     public void publishRoomMessage(ImRoomMessage message) {
         String msgStr = JSONObject.toJSONString(message);
         log.info("publishRoomMessage message : {}", msgStr);
-        IMApiResultInfo resultInfo;
+        // 消息发送用户
+        LiveRoomMessage.MessageUser messageUser = null;
+        SysUser sysUser = sysUserFeignService.queryUserById(Long.parseLong(message.getFromUserId()));
+        if (Objects.nonNull(sysUser)) {
+            // 发送用户信息
+            messageUser = LiveRoomMessage.MessageUser
+                    .builder()
+                    .sendUserId(sysUser.getId().toString())
+                    .sendUserName(sysUser.getUsername())
+                    .avatarUrl(sysUser.getAvatar())
+                    .build();
+        }
+
+        LiveRoomMessage.MessageContent messageContent = LiveRoomMessage.MessageContent
+                .builder()
+                .targetId(sysUser.getId().toString())
+                .sendUserInfo(messageUser)
+                .build();
+
+        LiveRoomMessage build = LiveRoomMessage.builder()
+                .isIncludeSender(1)
+                .objectName(message.getObjectName())
+                .fromUserId(message.getFromUserId())
+                .toChatRoomId(message.getToChatroomId())
+                .content(messageContent)
+                .build();
         try {
-            resultInfo = imHelper.publishRoomMessage(message.getFromUserId(), message.getToChatroomId(), message);
+
+            LivePluginService pluginService = livePluginContext.getPluginService(message.getServiceProvider());
+            pluginService.sendChatRoomMessage(build);
+            log.info("sendLiveRoomLoginOutMessage>>>> looker {} : roomId={}, userId={}",message.getObjectName(), message.getToChatroomId(), message.getFromUserId());
         } catch (Exception e) {
-            throw new BizException("消息发送失败" + e.getMessage());
+            log.error("sendLiveRoomMessage>>>>  looker error {} {}", message.getObjectName(),e.getMessage());
+            log.error("sendLiveRoomMessage>>>>  looker error sendMessage {} : {} : : roomId={}, userId={}",message.getObjectName(), message, message.getToChatroomId(), message.getFromUserId());
         }
-        if (!resultInfo.isSuccess()) {
-            throw new BizException("消息发送失败!" + resultInfo.getErrorMessage());
-        }
-        log.info("publishRoomMessage success: {}", msgStr);
     }
 
     private SysUser getSysUser(Long userId) {
@@ -1129,10 +1327,10 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
         return baseMapper.getLiveInRoomBySpeakerId(speakerId);
     }
 
-	@Override
-	public List<TeacherLivingInfoVo> queryTeacherLivingList() {
-		return baseMapper.queryTeacherLivingList();
-	}
+    @Override
+    public List<TeacherLivingInfoVo> queryTeacherLivingList() {
+        return baseMapper.queryTeacherLivingList();
+    }
     @Override
     public void whetherMic(String roomUid, Integer whetherMic) {
         //校验房间是否存在
@@ -1181,5 +1379,315 @@ public class LiveRoomServiceImpl extends ServiceImpl<LiveRoomDao, LiveRoom> impl
 
         return roomWrapper;
     }
+
+
+    /**
+     * 生成直播录制信息
+     *
+     * @param event TencentData.CallbackSteamRecordEvent
+     */
+    @Override
+    public void createLiveRoomVideoRecord(TencentData.CallbackSteamRecordEvent event) {
+
+        // 直播间ROOM_UID
+        String roomId = event.getStreamId().split("_")[0];
+
+        // 录制开始时间
+        long startTime =  (event.getEndTime() - event.getDuration()) * 1000L;
+        //保存切片
+        LiveRoomVideo imLiveRoomVideo = initImLiveRoomVideo(roomId, event.getTaskId(), DateTime.now().toDate());
+        imLiveRoomVideo.setStartTime(new DateTime(startTime).toDate());
+        imLiveRoomVideo.setEndTime(new DateTime(event.getEndTime() * 1000L).toDate());
+        imLiveRoomVideo.setUrl(event.getVideoUrl());
+        imLiveRoomVideo.setType(2);
+
+        // 回放记录已存在,直接忽略
+        LiveRoomVideo video = liveRoomVideoService.lambdaQuery()
+                .eq(LiveRoomVideo::getRoomUid, imLiveRoomVideo.getRoomUid())
+                .eq(LiveRoomVideo::getRecordId, imLiveRoomVideo.getRecordId())
+                .last("LIMIT 1")
+                .one();
+        if (Objects.isNull(video)) {
+
+            LiveRoom room = this.getByRoomUid(imLiveRoomVideo.getRoomUid());
+
+            if (org.apache.commons.lang.StringUtils.isBlank(room.getVideoRecord()) || Arrays.stream(room.getVideoRecord().split(","))
+                    .anyMatch(x -> x.equals(imLiveRoomVideo.getRecordId()))) {
+                liveRoomVideoService.save(imLiveRoomVideo);
+            }
+
+        }
+
+    }
+    private LiveRoomVideo initImLiveRoomVideo(String roomId, String recordId, Date now) {
+        LiveRoomVideo video = new LiveRoomVideo();
+        video.setRoomUid(roomId);
+        video.setRecordId(recordId);
+        video.setStartTime(now);
+        video.setType(0);
+        video.setCreatedTime(now);
+        return video;
+    }
+
+
+    /**
+     * 直播群成员在线状态回调处理
+     *
+     */
+    @Override
+    @Transactional
+    public void callbackOnMemberStateChange(TencentData.CallbackOnMemberStateChange callbackOnMemberStateChange) {
+
+        if (callbackOnMemberStateChange == null) {
+            return;
+        }
+
+        int onlineStatus = callbackOnMemberStateChange.getEventType().equals("Online")?1:0;
+
+        // 用户id
+        if (CollectionUtils.isEmpty(callbackOnMemberStateChange.getMemberList())) {
+            return;
+        }
+        List<Long> userIds = callbackOnMemberStateChange.getMemberList()
+                .stream()
+                .map(o -> Long.parseLong(o.getMemberAccount()))
+                .collect(Collectors.toList());
+
+        List<ImUserStateSync> imUserStates = new ArrayList<>();
+        for (Long userId : userIds) {
+
+            ImUserStateSync imUserState = new ImUserStateSync();
+            imUserState.setUserid(userId.toString());
+            imUserState.setStatus(onlineStatus == 1?"0":"1");
+            imUserState.setOs(callbackOnMemberStateChange.getOptPlatform());
+            imUserState.setTime(new Date().getTime());
+            imUserState.setRoomUid(callbackOnMemberStateChange.getGroupId());
+            imUserStates.add(imUserState);
+        }
+        opsRoom(imUserStates);
+    }
+
+    @Override
+    @Transactional
+    public void callbackAfterMemberExit(TencentData.CallbackAfterMemberExit callbackAfterMemberExit) {
+        if (callbackAfterMemberExit == null) {
+            return;
+        }
+        // 用户id
+        if (CollectionUtils.isEmpty(callbackAfterMemberExit.getExitMemberList())) {
+            return;
+        }
+        List<Long> userIds = callbackAfterMemberExit.getExitMemberList()
+                .stream()
+                .map(o -> Long.parseLong(o.getMemberAccount()))
+                .collect(Collectors.toList());
+
+
+        List<ImUserStateSync> imUserStates = new ArrayList<>();
+        for (Long userId : userIds) {
+            ImUserStateSync imUserState = new ImUserStateSync();
+            imUserState.setUserid(userId.toString());
+            imUserState.setStatus("3");
+            imUserState.setOs(callbackAfterMemberExit.getOptPlatform());
+            imUserState.setTime(callbackAfterMemberExit.getEventTime().atZone(ZoneId.systemDefault()).toEpochSecond());
+            imUserState.setRoomUid(callbackAfterMemberExit.getGroupId());
+            imUserStates.add(imUserState);
+        }
+        opsRoom(imUserStates);
+    }
+
+    @Override
+    @Transactional
+    public void callbackAfterNewMemberJoin(TencentData.CallbackAfterNewMemberJoin callbackAfterNewMemberJoin) {
+        if (callbackAfterNewMemberJoin == null) {
+            return;
+        }
+
+        // 用户id
+        if (CollectionUtils.isEmpty(callbackAfterNewMemberJoin.getNewMemberList())) {
+            return;
+        }
+        List<Long> userIds = callbackAfterNewMemberJoin.getNewMemberList()
+                .stream()
+                .map(o -> Long.parseLong(o.getMemberAccount()))
+                .collect(Collectors.toList());
+
+        List<ImUserStateSync> imUserStates = new ArrayList<>();
+        for (Long userId : userIds) {
+            ImUserStateSync imUserState = new ImUserStateSync();
+            imUserState.setUserid(userId.toString());
+            imUserState.setStatus("0");
+            imUserState.setOs(callbackAfterNewMemberJoin.getOptPlatform());
+            imUserState.setTime(callbackAfterNewMemberJoin.getEventTime().atZone(ZoneId.systemDefault()).toEpochSecond());
+            imUserState.setRoomUid(callbackAfterNewMemberJoin.getGroupId());
+            imUserStates.add(imUserState);
+        }
+        opsRoom(imUserStates);
+    }
+
+    // 定时任务凌晨2点,关闭腾讯直播间, 融云直播间自动关闭,不做处理
+    @Override
+    public void destroyLiveRoom() {
+        List<LiveRoom> liveRooms = this.lambdaQuery()
+                .eq(LiveRoom::getLiveState, 2)
+                .ne(LiveRoom::getRoomState,2)
+                .list();
+        for (LiveRoom liveRoom : liveRooms) {
+            liveRoomService.tryDestroyLiveRoom(liveRoom);
+        }
+    }
+
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public boolean tryDestroyLiveRoom(LiveRoom liveRoom) {
+        LivePluginService pluginService = livePluginContext.getPluginService(
+                liveRoom.getServiceProvider());
+        try {
+            if (pluginService == null) {
+                log.error("查询直播间流失败,未找到对应的插件");
+                return false;
+            }
+            if (pluginService.pluginName().equals(TencentCloudLivePlugin.PLUGIN_NAME)) {
+                TencentWrapper.LiveStreamState liveStreamState = pluginService.liveStreamState(
+                        getStreamId(liveRoom.getRoomUid(), liveRoom.getSpeakerId()));
+                if (liveStreamState == null) {
+                    log.error("查询直播间流失败,返回结果为空");
+                    return false;
+                }
+                log.info("查询直播间流状态:{},roomUid:{}", JSON.toJSONString(liveStreamState), liveRoom.getRoomUid());
+                if (!"active".equals(liveStreamState.getStreamState())) {
+                    return roomDestroy(liveRoom);
+                }
+            } else if (pluginService.pluginName().equals(RongCloudLivePlugin.PLUGIN_NAME)) {
+                // 融云走原有逻辑 融云自动销毁
+                // 销毁状态改为2
+                LiveRoom liveRoomUpdate = new LiveRoom();
+                liveRoomUpdate.setId(liveRoom.getId());
+                liveRoomUpdate.setRoomState(2);
+                liveRoomService.updateById(liveRoomUpdate);
+            }
+        } catch (Exception e) {
+
+            log.error("查询直播间流失败", e);
+            return false;
+        }
+        return true;
+    }
+
+    private String getStreamId(String roomUid, Long speakerId) {
+        return roomUid + "_" + speakerId;
+    }
+
+
+    public boolean roomDestroy(LiveRoom room) {
+        //10秒内同一个房间不能重复销毁-防重复销毁
+        RBucket<Object> bucket = redissonClient.getBucket("IM:ROOMDESTROY:" + room.getRoomUid());
+        if (!bucket.trySet(1, 10, TimeUnit.SECONDS)) {
+            return false;
+        }
+        log.error("roomDestroy>>>> room : {}", JSONObject.toJSONString(room));
+        String roomUid = room.getRoomUid();
+
+        //将房间状态改为已销毁
+        LiveRoom liveRoomUpdate = new LiveRoom();
+        liveRoomUpdate.setId(room.getId());
+
+        Date date = new Date();
+        liveRoomUpdate.setRoomState(2);
+        liveRoomUpdate.setLiveState(2);
+        Long userId = -1L;
+        try {
+            userId = getSysUser().getId();
+        } catch (Exception ignored) {
+        }
+        liveRoomUpdate.setUpdatedBy(userId);
+        liveRoomUpdate.setUpdatedTime(date);
+        liveRoomUpdate.setLiveEndTime(date);
+
+        this.updateById(liveRoomUpdate);
+
+
+        //向聊天室发自定义消息踢出所有人
+        try {
+            LivePluginService pluginService = livePluginContext.getPluginService(room.getServiceProvider());
+
+            //销毁直播间
+            pluginService.chatRoomDestroy(roomUid);
+
+            TencentWrapper.LiveStreamState liveStreamState = pluginService.liveStreamState(
+                    getStreamId(room.getRoomUid(), room.getSpeakerId()));
+            if (liveStreamState == null) {
+                log.error("查询直播间流失败,返回结果为空");
+            } else if ("active".equals(liveStreamState.getStreamState())) {
+                pluginService.liveStreamStop(getStreamId(room.getRoomUid(), room.getSpeakerId()));
+            }
+
+            // 录制任务Id
+            if (room.getServiceProvider().equals(TencentCloudLivePlugin.PLUGIN_NAME)) {
+
+                List<String> collect = liveRoomService.lambdaQuery()
+                        .eq(LiveRoom::getRoomUid, roomUid).list().stream()
+                        .map(LiveRoom::getVideoRecord)
+                        .filter(StringUtils::isNotEmpty)
+                        .distinct().collect(Collectors.toList());
+
+                for (String taskId : collect) {
+                    // 删除录制任务
+                    pluginService.rtcRoomRecordStop(taskId);
+                }
+            }
+        } catch (Exception e) {
+            log.error("roomDestroy>>>> errorMsg{}", e.getMessage(), e.getCause());
+            return false;
+        }
+        return true;
+    }
+
+
+    /**
+     * 更新主播直播间状态
+     *
+     * @param liveRoom 直播间状态
+     */
+    @Override
+    public Boolean updateRoomStatus(LiveRoomStatus liveRoom) {
+        // 获取直播间信息
+        LiveRoom room = this.getByRoomUid(liveRoom.getRoomUid());
+        if (room == null) {
+            throw new BizException("直播间不存在");
+        }
+        // 设置直播群组自定义数据
+        if (liveRoom.getSpeakerStatus() != null) {
+            setGroupDefinedData(room, EGroupDefinedDataType.ANCHOR_STATUS,
+                    liveRoom.getSpeakerStatus() == 0 ? EAnchorStatus.OFFLINE.getCode() : EAnchorStatus.ONLINE.getCode());
+        }
+
+        if (liveRoom.getPushStatus() != null) {
+            // 设置推流状态
+            setGroupDefinedData(room, EGroupDefinedDataType.LIVE_STATUS, liveRoom.getPushStatus() == 1 ?
+                    EOnOffStatus.ON.getCode() : EOnOffStatus.OFF.getCode());
+        }
+
+
+        if (liveRoom.getBanStatus() != null) {
+            setGroupDefinedData(room, EGroupDefinedDataType.GLOBAL_BAN, liveRoom.getBanStatus() == 1 ?
+                    EOnOffStatus.ON.getCode() : EOnOffStatus.OFF.getCode());
+        }
+
+        // 设置摄像头状态
+        if (liveRoom.getCameraStatus() != null) {
+            setGroupDefinedData(room, EGroupDefinedDataType.ANCHOR_CAMERA,
+                    liveRoom.getCameraStatus() == 1 ? EOnOffStatus.ON.getCode():EOnOffStatus.OFF.getCode());
+        }
+
+        // 设置全员闭麦状态
+        if (Objects.nonNull(liveRoom.getMicStatus())) {
+            setGroupDefinedData(room, EGroupDefinedDataType.ANCHOR_MIC,
+                    liveRoom.getMicStatus() == 1 ? EOnOffStatus.ON.getCode() : EOnOffStatus.OFF.getCode());
+        }
+        return true;
+    }
+
 }
 

+ 0 - 2
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/LiveRoomVideoServiceImpl.java

@@ -113,8 +113,6 @@ public class LiveRoomVideoServiceImpl extends ServiceImpl<LiveRoomVideoDao, Live
     private void insertVideo(String fileUrl, String recordId, Integer notifyType, LiveRoom room) {
         Date now = new Date();
         LiveRoomVideo roomVideo = new LiveRoomVideo();
-        roomVideo.setCourseGroupId(room.getCourseGroupId());
-        roomVideo.setCourseId(room.getCourseId());
         roomVideo.setRoomUid(room.getRoomUid());
         roomVideo.setRecordId(recordId);
         roomVideo.setUrl(fileUrl);

+ 3 - 2
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/StudentServiceImpl.java

@@ -26,6 +26,7 @@ import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
 import com.yonge.cooleshow.biz.dal.mapper.SysUserMapper;
 import com.yonge.cooleshow.biz.dal.service.ImGroupMemberService;
 import com.yonge.cooleshow.biz.dal.service.ImGroupService;
+import com.yonge.cooleshow.biz.dal.service.ImGroupService;
 import com.yonge.cooleshow.biz.dal.service.ImUserFriendService;
 import com.yonge.cooleshow.biz.dal.service.StudentService;
 import com.yonge.cooleshow.biz.dal.service.StudentTotalService;
@@ -102,6 +103,7 @@ public class StudentServiceImpl extends ServiceImpl<StudentDao, Student> impleme
         return baseMapper;
     }
 
+
     @Override
     public StudentVo detail(Long userId) {
         return baseMapper.detail(userId);
@@ -146,8 +148,7 @@ public class StudentServiceImpl extends ServiceImpl<StudentDao, Student> impleme
         studentHomeVo.setMusicSheetNum(null == total.getMusicSheetNum() ? 0 : total.getMusicSheetNum());
 
         // IM聊天用户ID
-        studentHomeVo.setImUserId(MessageFormat.format("{0}:{1}", String.valueOf(detail.getUserId()),
-                ClientEnum.STUDENT.name()));
+        studentHomeVo.setImUserId(imGroupService.getImUserId(String.valueOf(detail.getUserId()), ClientEnum.STUDENT.name()));
 
         // 判断是否是机构学生 机构学生 检测机构专辑购买记录
         TenantInfo tenantInfo = tenantInfoService.detail(detail.getTenantId());

+ 2 - 6
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/StudentStarServiceImpl.java

@@ -3,10 +3,7 @@ package com.yonge.cooleshow.biz.dal.service.impl;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.google.common.collect.Maps;
-import com.yonge.cooleshow.biz.dal.entity.ImGroup;
-import com.yonge.cooleshow.biz.dal.entity.StudentTotal;
-import com.yonge.cooleshow.biz.dal.entity.SysUser;
-import com.yonge.cooleshow.biz.dal.entity.TeacherTotal;
+import com.yonge.cooleshow.biz.dal.entity.*;
 import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
 import com.yonge.cooleshow.biz.dal.enums.ImGroupMemberRoleType;
 import com.yonge.cooleshow.biz.dal.enums.MessageTypeEnum;
@@ -24,7 +21,6 @@ import lombok.extern.slf4j.Slf4j;
 import org.redisson.api.RedissonClient;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
-import com.yonge.cooleshow.biz.dal.entity.StudentStar;
 import com.yonge.cooleshow.biz.dal.dao.StudentStarDao;
 import com.yonge.cooleshow.biz.dal.service.StudentStarService;
 
@@ -103,7 +99,7 @@ public class StudentStarServiceImpl extends ServiceImpl<StudentStarDao, StudentS
                 if (Objects.nonNull(imGroup)) {
 
                     //处理本地群成员
-                    List<GroupMember> groupMembers = imGroupMemberService.initGroupMember(imGroup.getId(),
+                    List<ImGroupMember> groupMembers = imGroupMemberService.initGroupMember(imGroup.getId(),
                             studentId, false, ImGroupMemberRoleType.STUDENT);
                     //同步群成员数量
                     imGroupService.getDao().updateMemberNum(imGroup.getId());

+ 37 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/im/ImGroupWrapper.java

@@ -0,0 +1,37 @@
+package com.yonge.cooleshow.biz.dal.wrapper.im;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.io.Serializable;
+
+/**
+ * 即时通讯群组
+ * 2022-11-26 10:55:18
+ */
+@ApiModel(value = "ImGroupWrapper对象", description = "即时通讯群组查询对象")
+public class ImGroupWrapper {
+
+    @Data
+    @Builder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @ApiModel("IM用户信息")
+    public static class ImUserInfo implements Serializable {
+
+        @ApiModelProperty("用户ID")
+        private String imUserId;
+
+        @ApiModelProperty("授权Token")
+        private String imToken;
+
+        public ImUserInfo imUserId(String imUserId) {
+            this.imUserId = imUserId;
+            return this;
+        }
+    }
+}

+ 38 - 0
cooleshow-user/user-biz/src/main/resources/config/mybatis/ImGroupMapper.xml

@@ -18,6 +18,22 @@
     <sql id="Base_Column_List">
         id_, name_, introduce_, member_num_, memo_, img_, type_,create_by_, create_time_, update_time_
     </sql>
+    <insert id="batchInsert" parameterType="com.yonge.cooleshow.biz.dal.entity.ImHistoryMessage">
+        insert into im_history_message(msgUID_,fromUserId_,targetId_,targetType_,
+                                       GroupId_,busChannel_,classname_,content_,
+                                       extraContent_,dateTime_,source_,isDiscard_,
+                                       isSensitiveWord_,isForbidden_,isNotForward_,
+                                       groupUserIds_,appId_)
+        values
+               <foreach collection="list" separator="," item="item" open="(" close=")">
+                #{item.msgUID,jdbcType=VARCHAR},#{item.fromUserId,jdbcType=VARCHAR},#{item.targetId,jdbcType=VARCHAR},
+                   #{item.targetType},#{item.groupId,jdbcType=VARCHAR},#{item.busChannel,jdbcType=VARCHAR},
+                   #{item.classname,jdbcType=VARCHAR},#{item.content,jdbcType=VARCHAR},#{item.extraContent,jdbcType=VARCHAR},
+                   #{item.dateTime,jdbcType=VARCHAR},#{item.source,jdbcType=VARCHAR},#{item.isDiscard,jdbcType=VARCHAR},
+                   #{item.isSensitiveWord,jdbcType=VARCHAR},#{item.isForbidden,jdbcType=VARCHAR},#{item.isNotForward,jdbcType=VARCHAR},
+                   #{item.groupUserIds,jdbcType=VARCHAR},#{item.appId,jdbcType=VARCHAR}
+               </foreach>
+    </insert>
 
     <update id="updateById" parameterType="com.yonge.cooleshow.biz.dal.entity.ImGroup">
         update im_group
@@ -92,4 +108,26 @@
         </if>
         GROUP BY ig.id_
     </select>
+    <select id="selectAll" resultType="com.yonge.cooleshow.biz.dal.entity.ImHistoryMessage">
+        select msgUID_          AS msgUID,
+               fromUserId_      AS fromUserId,
+               targetId_        AS targetId,
+               targetType_      AS targetType,
+               GroupId_         AS groupId,
+               busChannel_      AS busChannel,
+               classname_       AS classname,
+               content_         AS content,
+               extraContent_    AS extraContent,
+               dateTime_        AS dateTime,
+               source_          AS source,
+               isDiscard_       AS isDiscard,
+               isSensitiveWord_ AS isSensitiveWord,
+               isForbidden_     AS isForbidden,
+               isNotForward_    AS isNotForward,
+               groupUserIds_    AS groupUserIds,
+               appId_           AS appId
+
+        from im_history_message
+
+    </select>
 </mapper>

+ 1 - 5
cooleshow-user/user-biz/src/main/resources/config/mybatis/ImGroupMemberMapper.xml

@@ -50,11 +50,7 @@
         </where>
         LIMIT 1
     </select>
-    <resultMap id="GroupMember" type="io.rong.models.group.GroupMember">
-        <result property="id" column="user_id_"/>
-        <result property="groupId" column="group_id_"/>
-    </resultMap>
-    <select id="queryGroupMember" resultMap="GroupMember">
+    <select id="queryGroupMember" resultType="com.yonge.cooleshow.biz.dal.entity.ImGroupMember">
         SELECT user_id_,group_id_ FROM im_group_member WHERE group_id_ = #{groupId}
     </select>
     <resultMap id="ImGroupMember" type="com.yonge.cooleshow.biz.dal.entity.ImGroupMember">

+ 7 - 1
cooleshow-user/user-classroom/src/main/java/com/yonge/cooleshow/classroom/controller/ImNetworkRoomController.java

@@ -4,6 +4,7 @@ package com.yonge.cooleshow.classroom.controller;
 import com.alibaba.fastjson.JSONObject;
 import com.yonge.cooleshow.biz.dal.dto.*;
 import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
+import com.yonge.cooleshow.biz.dal.service.ImGroupService;
 import com.yonge.cooleshow.biz.dal.service.ImNetworkRoomService;
 import com.yonge.cooleshow.biz.dal.service.SysUserService;
 import com.yonge.cooleshow.common.controller.BaseController;
@@ -13,6 +14,7 @@ import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -42,6 +44,8 @@ public class ImNetworkRoomController extends BaseController {
     private ImNetworkRoomService imNetworkRoomService;
     @Resource
     private SysUserService sysUserService;
+    @Autowired
+    private ImGroupService imGroupService;
 
     @ApiOperation("加入网络教室")
     @PostMapping(value = "/join")
@@ -112,7 +116,9 @@ public class ImNetworkRoomController extends BaseController {
         // IM用户ID
         String imUserId = String.valueOf(userId);
         if (ClientEnum.STUDENT == imNetworkBaseDto.getClientType()) {
-            imUserId = MessageFormat.format("{0}:{1}", imUserId, ClientEnum.STUDENT.name());
+            imUserId = imGroupService.getImUserId(imUserId, ClientEnum.STUDENT.name());
+        }else {
+            imUserId = imGroupService.getImUserId(imUserId, ClientEnum.TEACHER.name());
         }
 
         imNetworkRoomService.quitRoomSuccess(Optional.ofNullable(imNetworkBaseDto)

+ 6 - 2
cooleshow-user/user-student/src/main/java/com/yonge/cooleshow/student/controller/CourseHomeworkController.java

@@ -5,9 +5,11 @@ import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
 import com.yonge.cooleshow.auth.api.entity.SysUser;
 import com.yonge.cooleshow.biz.dal.dto.CourseHomeworkSubmitDto;
 import com.yonge.cooleshow.biz.dal.dto.search.HomeworkSearch;
+import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
 import com.yonge.cooleshow.biz.dal.enums.CourseScheduleEnum;
 import com.yonge.cooleshow.biz.dal.service.CourseHomeworkService;
 import com.yonge.cooleshow.biz.dal.service.CourseScheduleService;
+import com.yonge.cooleshow.biz.dal.service.ImGroupService;
 import com.yonge.cooleshow.common.enums.YesOrNoEnum;
 import com.yonge.toolset.mybatis.support.PageUtil;
 import com.yonge.cooleshow.biz.dal.vo.CourseHomeworkDetailVo;
@@ -46,6 +48,9 @@ public class CourseHomeworkController extends BaseController {
     @Autowired
     private CourseHomeworkService courseHomeworkService;
 
+    @Autowired
+    private ImGroupService imGroupService;
+
 
     @ApiOperation(value = "首页-我的课程-课程详情(陪练课)-课后作业信息详情",notes = "传入课程编号ID")
     @GetMapping(value = "/detail/{courseId}")
@@ -98,8 +103,7 @@ public class CourseHomeworkController extends BaseController {
         if (CollectionUtils.isNotEmpty(page.getRecords())) {
 
             for (CourseHomeworkVo item : page.getRecords()) {
-
-                item.setImUserId(String.valueOf(item.getTeacherId()));
+                item.setImUserId(imGroupService.getImUserId(String.valueOf(item.getTeacherId()),ClientEnum.TEACHER.name()));
             }
         }
 

+ 15 - 21
cooleshow-user/user-student/src/main/java/com/yonge/cooleshow/student/controller/TeacherController.java

@@ -1,22 +1,5 @@
 package com.yonge.cooleshow.student.controller;
 
-import io.swagger.annotations.Api;
-import io.swagger.annotations.ApiOperation;
-import io.swagger.annotations.ApiParam;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.http.HttpStatus;
-import org.springframework.util.CollectionUtils;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RestController;
-
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
 import com.yonge.cooleshow.auth.api.entity.SysUser;
@@ -24,7 +7,6 @@ import com.yonge.cooleshow.biz.dal.dto.search.TeacherStyleSearch;
 import com.yonge.cooleshow.biz.dal.entity.TeacherStyleVideo;
 import com.yonge.cooleshow.biz.dal.enums.AuthStatusEnum;
 import com.yonge.cooleshow.biz.dal.service.AppVersionInfoService;
-import com.yonge.cooleshow.biz.dal.service.StudentService;
 import com.yonge.cooleshow.biz.dal.service.StudentStarService;
 import com.yonge.cooleshow.biz.dal.service.TeacherService;
 import com.yonge.cooleshow.biz.dal.service.TeacherStyleVideoService;
@@ -36,6 +18,21 @@ import com.yonge.cooleshow.common.entity.HttpResponseResult;
 import com.yonge.cooleshow.common.enums.YesOrNoEnum;
 import com.yonge.toolset.base.page.PageInfo;
 import com.yonge.toolset.mybatis.support.PageUtil;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.util.CollectionUtils;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+import java.util.stream.Collectors;
 
 @RestController
 @RequestMapping("/teacher")
@@ -51,9 +48,6 @@ public class TeacherController extends BaseController {
     private StudentStarService studentStarService;
     @Autowired
     private AppVersionInfoService appVersionInfoService;
-    
-    @Autowired
-    private StudentService studentService;
 
     @ApiOperation(value = "老师风采-分页")
     @PostMapping("/stylePage")

+ 13 - 6
cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/controller/CourseHomeworkController.java

@@ -11,24 +11,29 @@ import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
 import com.yonge.cooleshow.biz.dal.enums.CourseScheduleEnum;
 import com.yonge.cooleshow.biz.dal.service.CourseHomeworkService;
 import com.yonge.cooleshow.biz.dal.service.CourseScheduleService;
-import com.yonge.cooleshow.common.enums.YesOrNoEnum;
-import com.yonge.toolset.mybatis.support.PageUtil;
+import com.yonge.cooleshow.biz.dal.service.ImGroupService;
 import com.yonge.cooleshow.biz.dal.vo.CountVo;
 import com.yonge.cooleshow.biz.dal.vo.CourseHomeworkDetailVo;
 import com.yonge.cooleshow.biz.dal.vo.CourseHomeworkVo;
 import com.yonge.cooleshow.biz.dal.vo.CourseScheduleHomeworkVo;
 import com.yonge.cooleshow.common.controller.BaseController;
 import com.yonge.cooleshow.common.entity.HttpResponseResult;
+import com.yonge.cooleshow.common.enums.YesOrNoEnum;
 import com.yonge.toolset.base.page.PageInfo;
+import com.yonge.toolset.mybatis.support.PageUtil;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
 import org.apache.commons.collections.CollectionUtils;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
 
 import javax.validation.Valid;
-import java.text.MessageFormat;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -52,6 +57,9 @@ public class CourseHomeworkController extends BaseController {
     @Autowired
     private CourseHomeworkService courseHomeworkService;
 
+    @Autowired
+    private ImGroupService imGroupService;
+
     @ApiOperation(value = "未布置的课后作业数量")
     @GetMapping(value="/count")
     public HttpResponseResult<CountVo> countTeacherNoDecorateHomework() {
@@ -145,8 +153,7 @@ public class CourseHomeworkController extends BaseController {
         if (CollectionUtils.isNotEmpty(page.getRecords())) {
 
             for (CourseHomeworkVo item : page.getRecords()) {
-
-                item.setImUserId(MessageFormat.format("{0}:{1}", String.valueOf(item.getStudentId()), ClientEnum.STUDENT.name()));
+                item.setImUserId(imGroupService.getImUserId(String.valueOf(item.getStudentId()),ClientEnum.STUDENT.name()));
             }
         }
 

+ 11 - 0
cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/controller/TeacherLiveRoomController.java

@@ -1,6 +1,7 @@
 package com.yonge.cooleshow.teacher.controller;
 
 import com.alibaba.fastjson.JSONObject;
+import com.yonge.cooleshow.biz.dal.dto.LiveRoomStatus;
 import com.yonge.cooleshow.biz.dal.entity.ImUserStateSync;
 import com.yonge.cooleshow.biz.dal.entity.LiveRoom;
 import com.yonge.cooleshow.biz.dal.entity.RoomInfoCache;
@@ -14,6 +15,7 @@ import org.slf4j.LoggerFactory;
 import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
+import javax.validation.Valid;
 import java.util.List;
 import java.util.Map;
 
@@ -109,6 +111,15 @@ public class TeacherLiveRoomController extends BaseController {
         liveRoomService.opsRoom(userState);
     }
 
+
+
+    @ApiOperation("更新直播间状态")
+    @PostMapping("/updateRoomStatus")
+    public HttpResponseResult<Boolean> updateRoomStatus(@RequestBody @Valid LiveRoomStatus status ) {
+        return succeed(liveRoomService.updateRoomStatus(status));
+    }
+
+
     @ApiOperation("方便测试观察房间数据的方法")
     @GetMapping("/test")
     public Object destroyExpiredLiveRoom(@RequestParam("roomUid") String roomUid) {

+ 1 - 1
cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/task/TaskController.java

@@ -55,7 +55,7 @@ public class TaskController extends BaseController {
         return HttpResponseResult.succeed();
     }
 
-    @ApiOperation("定时任务-销毁房间-直播间")
+    @ApiOperation("定时任务-关闭-直播间")
     @GetMapping("/destroyExpiredLiveRoom")
     public void destroyExpiredLiveRoom() {
         liveRoomService.destroyExpiredLiveRoom();