浏览代码

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/ImGroupService.java
#	cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImGroupServiceImpl.java
#	cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/im/ImGroupWrapper.java
yuanliang 2 年之前
父节点
当前提交
e2287500ff
共有 95 个文件被更改,包括 4037 次插入1657 次删除
  1. 3 0
      .gitignore
  2. 2 2
      Jenkinsfile
  3. 11 1
      audio-analysis/src/main/java/com/yonge/netty/dto/UserChannelContext.java
  4. 11 0
      audio-analysis/src/main/java/com/yonge/netty/entity/MusicXmlBasicInfo.java
  5. 10 0
      audio-analysis/src/main/java/com/yonge/netty/entity/MusicXmlNote.java
  6. 1 1
      audio-analysis/src/main/java/com/yonge/netty/server/service/AudioCompareHandler.java
  7. 2 1
      audio-analysis/src/main/resources/logback-spring.xml
  8. 5 0
      cooleshow-api/src/main/java/com/yonge/cooleshow/api/feign/AdminFeignService.java
  9. 16 0
      cooleshow-api/src/main/java/com/yonge/cooleshow/api/feign/dto/ImUserInfo.java
  10. 6 0
      cooleshow-api/src/main/java/com/yonge/cooleshow/api/feign/fallback/AdminFeignServiceFallback.java
  11. 20 8
      cooleshow-auth/auth-server/src/main/java/com/yonge/cooleshow/auth/web/controller/TokenController.java
  12. 1 1
      cooleshow-cms/src/main/resources/bootstrap-dev.properties
  13. 76 0
      cooleshow-common/src/main/java/com/yonge/cooleshow/common/enums/EGroupDefinedDataType.java
  14. 7 15
      cooleshow-common/src/main/java/com/yonge/cooleshow/common/enums/HardLevelEnum.java
  15. 0 1
      cooleshow-mall/mall-admin/src/main/java/com/yonge/cooleshow/admin/dto/PmsBrandParam.java
  16. 1 1
      cooleshow-mall/mall-admin/src/main/resources/logback-spring.xml
  17. 7 0
      cooleshow-mall/mall-portal/src/main/java/com/yonge/cooleshow/portal/controller/PaymentController.java
  18. 12 0
      cooleshow-mall/mall-portal/src/main/java/com/yonge/cooleshow/portal/dto/OrderCreate.java
  19. 7 3
      cooleshow-mall/mall-portal/src/main/java/com/yonge/cooleshow/portal/service/impl/OmsPortalOrderServiceImpl.java
  20. 1 1
      cooleshow-task/src/main/resources/logback-spring.xml
  21. 97 0
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/ImGroupController.java
  22. 6 1
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/MusicSheetController.java
  23. 267 0
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/open/ImController.java
  24. 0 96
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/open/ImUserFriendController.java
  25. 3 0
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/open/UserOrderClient.java
  26. 16 0
      cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/task/TaskController.java
  27. 16 0
      cooleshow-user/user-biz/pom.xml
  28. 24 24
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/config/RongCloudConfig.java
  29. 9 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dao/ImGroupDao.java
  30. 1 1
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dao/ImGroupMemberDao.java
  31. 6 79
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/ImNetworkDeviceControlDto.java
  32. 11 37
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/ImNetworkRoomResult.java
  33. 41 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/LiveRoomStatus.java
  34. 521 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/TencentData.java
  35. 40 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/TencentImCallbackResult.java
  36. 3 191
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/CourseGroup.java
  37. 15 217
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/CourseSchedule.java
  38. 91 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/ImHistoryMessage.java
  39. 10 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/ImRoomMessage.java
  40. 3 39
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/ImUserStateSync.java
  41. 17 142
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/LiveRoom.java
  42. 7 23
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/LiveRoomVideo.java
  43. 36 36
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/MusicSheet.java
  44. 7 164
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/RoomInfoCache.java
  45. 36 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/RoleEnum.java
  46. 1 1
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/RoomTypeEnum.java
  47. 30 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/im/ETencentGroupType.java
  48. 31 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/im/ETencentImCallbackCommand.java
  49. 43 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/live/EAnchorStatus.java
  50. 40 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/live/EOnOffStatus.java
  51. 11 10
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/ActivityEvaluationRecordService.java
  52. 8 8
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/ImGroupMemberService.java
  53. 77 5
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/ImGroupService.java
  54. 2 1
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/ImNetworkRoomMemberService.java
  55. 41 4
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/LiveRoomService.java
  56. 2 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/MusicSheetService.java
  57. 38 37
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ActivityEvaluationRecordServiceImpl.java
  58. 17 3
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ActivityEvaluationServiceImpl.java
  59. 1 1
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ActivityPlanServiceImpl.java
  60. 8 1
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ActivityRewardServiceImpl.java
  61. 4 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ActivityUserRewardServiceImpl.java
  62. 2 2
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/CourseGroupServiceImpl.java
  63. 5 5
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/CourseHomeworkServiceImpl.java
  64. 8 12
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/CourseScheduleServiceImpl.java
  65. 109 48
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/CustomerServiceBatchSendingServiceImpl.java
  66. 3 2
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImGroupMemberAuditServiceImpl.java
  67. 39 35
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImGroupMemberServiceImpl.java
  68. 620 55
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImGroupServiceImpl.java
  69. 9 11
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImNetworkRoomMemberServiceImpl.java
  70. 504 93
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImNetworkRoomServiceImpl.java
  71. 70 38
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ImUserFriendServiceImpl.java
  72. 598 90
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/LiveRoomServiceImpl.java
  73. 0 2
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/LiveRoomVideoServiceImpl.java
  74. 1 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/MusicTagServiceImpl.java
  75. 3 2
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/StudentServiceImpl.java
  76. 2 6
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/StudentStarServiceImpl.java
  77. 25 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/VideoLessonGroupServiceImpl.java
  78. 1 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/TeacherHomeVo.java
  79. 51 48
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/StudentWrapper.java
  80. 23 0
      cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/im/ImGroupWrapper.java
  81. 7 1
      cooleshow-user/user-biz/src/main/resources/config/mybatis/CourseScheduleMapper.xml
  82. 38 0
      cooleshow-user/user-biz/src/main/resources/config/mybatis/ImGroupMapper.xml
  83. 1 5
      cooleshow-user/user-biz/src/main/resources/config/mybatis/ImGroupMemberMapper.xml
  84. 1 0
      cooleshow-user/user-biz/src/main/resources/config/mybatis/SubjectMapper.xml
  85. 7 1
      cooleshow-user/user-classroom/src/main/java/com/yonge/cooleshow/classroom/controller/ImNetworkRoomController.java
  86. 6 2
      cooleshow-user/user-student/src/main/java/com/yonge/cooleshow/student/controller/CourseHomeworkController.java
  87. 15 21
      cooleshow-user/user-student/src/main/java/com/yonge/cooleshow/student/controller/TeacherController.java
  88. 17 0
      cooleshow-user/user-student/src/main/java/com/yonge/cooleshow/student/controller/open/OpenClient.java
  89. 13 6
      cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/controller/CourseHomeworkController.java
  90. 11 0
      cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/controller/TeacherLiveRoomController.java
  91. 0 1
      cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/controller/open/OpenShareController.java
  92. 1 1
      cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/task/TaskController.java
  93. 7 13
      cooleshow-user/user-website/src/main/java/com/yonge/cooleshow/website/controller/open/OpenClient.java
  94. 1 0
      pom.xml
  95. 3 1
      service.md

+ 3 - 0
.gitignore

@@ -1,6 +1,8 @@
 /target/
 logback-test.xml
 
+logback-test.xml
+
 ### STS ###
 .apt_generated
 .apt_generated_tests
@@ -39,6 +41,7 @@ mvnw.cmd
 **/target/
 **/mvnw
 **/mvnw.cmd
+**/*.log
 
 
 ### NetBeans ###

+ 2 - 2
Jenkinsfile

@@ -4,10 +4,10 @@ pipeline {
     environment {
 
        BUILD_USER = ""
-       REMOTE_PATH = "/mnt/datadisk/prod/jenkins"
+       REMOTE_PATH = "/mdata/cooleshow-test/jenkins"
        BUILD_FILE = "libs.tar.gz"
        ROBOT_ID = "a346e21b-706e-4881-8e3b-6c48d16d0c3b"
-       SSH_AUTH = "ECS-CooleShow-47.98.131.38-pre"
+       SSH_AUTH = "WH-COOLESHOW-TEST"
     }
 
     stages {

+ 11 - 1
audio-analysis/src/main/java/com/yonge/netty/dto/UserChannelContext.java

@@ -685,7 +685,7 @@ public class UserChannelContext {
 		
 		double correctedStartTime = queryFirstNoteStartTime(chunkAnalysisList, musicXmlNote, floatingRange);
 		double correctedEndTime = correctedStartTime + musicXmlNote.getDuration();
-		
+
 		//重新计算延迟
 		dynamicOffset = correctedStartTime - musicXmlNote.getTimeStamp();
 		dynamicOffset = dynamicOffset < 0 ? 0 : dynamicOffset;
@@ -699,6 +699,16 @@ public class UserChannelContext {
 			return -1;
 		}
 		
+		// 颤音频率只要有一个符合 就符合
+		if (musicXmlNote.isOrnament()) {
+			for (ChunkAnalysis chunkAnalysis : chunkAnalysisList) {
+				NoteFrequencyRange noteFrequencyRange = new NoteFrequencyRange(standardFrequecy, musicXmlNote.getFrequency());
+				if((noteFrequencyRange.getMinFrequency() < chunkAnalysis.getFrequency() && chunkAnalysis.getFrequency() < noteFrequencyRange.getMaxFrequency())){
+					return Float.valueOf(musicXmlNote.getFrequency()).intValue();
+				}
+			}
+		}
+		
 		ChunkAnalysis firstChunkAnalysis = chunkAnalysisList.get(0);
 		
 		LOGGER.debug("------Pitch------startTime:{}  endTime:{}------", firstChunkAnalysis.getStartTime(), chunkAnalysisList.get(chunkAnalysisList.size() - 1)

+ 11 - 0
audio-analysis/src/main/java/com/yonge/netty/entity/MusicXmlBasicInfo.java

@@ -42,10 +42,21 @@ public class MusicXmlBasicInfo {
 	
 	private Integer reactionTimeMs;//用户设置的反应时间(毫秒)
 
+	// 是否颤音
+	private boolean isOrnament;
+
 	private List<MusicXmlNote> musicXmlInfos = new ArrayList<MusicXmlNote>();
 
 	private Map<Integer, MusicXmlSection> musicXmlSectionMap = new HashMap<Integer, MusicXmlSection>();
 
+	public boolean isOrnament() {
+		return isOrnament;
+	}
+
+	public void setIsOrnament(boolean isOrnament) {
+		this.isOrnament = isOrnament;
+	}
+
 	public String getCustomConfiguration() {
 		return customConfiguration;
 	}

+ 10 - 0
audio-analysis/src/main/java/com/yonge/netty/entity/MusicXmlNote.java

@@ -33,6 +33,16 @@ public class MusicXmlNote {
 	private int denominator;
 	
 	private int measureRenderIndex;
+	// 是否颤音
+	private boolean isOrnament;
+
+	public boolean isOrnament() {
+		return isOrnament;
+	}
+
+	public void setIsOrnament(boolean isOrnament) {
+		this.isOrnament = isOrnament;
+	}
 
 	public double getTimeStamp() {
 		return timeStamp;

+ 1 - 1
audio-analysis/src/main/java/com/yonge/netty/server/service/AudioCompareHandler.java

@@ -83,7 +83,7 @@ public class AudioCompareHandler implements MessageHandler {
 	/**
 	 * @describe 采样大小
 	 */
-	private int bufferSize = 1024 * 2;
+	private int bufferSize = 1024 * 2 ;
 
 	private boolean signed = true;
 

+ 2 - 1
audio-analysis/src/main/resources/logback-spring.xml

@@ -27,7 +27,8 @@
 		</encoder>
 	</appender>
 
-	<logger name="com.yonge" level="info" />
+	<logger name="com.yonge" level="debug" />
+	<logger name="com.yonge.netty" level="debug" />
 
 	<!--开发环境:打印控制台 -->
 	<springProfile name="local">

+ 5 - 0
cooleshow-api/src/main/java/com/yonge/cooleshow/api/feign/AdminFeignService.java

@@ -7,6 +7,7 @@ import com.yonge.cooleshow.api.feign.dto.StudentWrapper;
 import com.yonge.cooleshow.api.feign.dto.TeacherApi;
 import com.yonge.cooleshow.api.feign.dto.TenantWrapper;
 import com.yonge.cooleshow.api.feign.dto.UserFriendInfoVO;
+import com.yonge.cooleshow.api.feign.dto.*;
 import com.yonge.cooleshow.api.feign.fallback.AdminFeignServiceFallback;
 import com.yonge.cooleshow.common.constant.AppConstant;
 import com.yonge.cooleshow.common.entity.ContractDto;
@@ -171,4 +172,8 @@ public interface AdminFeignService {
 
     @PostMapping("/open/adminClient/unionStudent")
     HttpResponseResult<StudentWrapper.UnionStudentResp> unionStudent(@RequestBody StudentWrapper.UnionStudent info);
+
+
+    @PostMapping(value = "/open/im/register")
+    ImUserInfo register(@RequestParam("userId") String userId, @RequestParam("clientType") String clientType, @RequestParam("username") String username, @RequestParam("avatar") String avatar);
 }

+ 16 - 0
cooleshow-api/src/main/java/com/yonge/cooleshow/api/feign/dto/ImUserInfo.java

@@ -0,0 +1,16 @@
+package com.yonge.cooleshow.api.feign.dto;
+
+import lombok.Data;
+
+@Data
+public class ImUserInfo {
+
+    private String imUserId;
+
+    private String imToken;
+
+    public ImUserInfo imUserId(String imUserId) {
+        this.imUserId = imUserId;
+        return this;
+    }
+}

+ 6 - 0
cooleshow-api/src/main/java/com/yonge/cooleshow/api/feign/fallback/AdminFeignServiceFallback.java

@@ -8,6 +8,7 @@ import com.yonge.cooleshow.api.feign.dto.StudentWrapper;
 import com.yonge.cooleshow.api.feign.dto.TeacherApi;
 import com.yonge.cooleshow.api.feign.dto.TenantWrapper;
 import com.yonge.cooleshow.api.feign.dto.UserFriendInfoVO;
+import com.yonge.cooleshow.api.feign.dto.*;
 import com.yonge.cooleshow.common.entity.ContractDto;
 import com.yonge.cooleshow.common.entity.HttpResponseResult;
 import com.yonge.cooleshow.common.entity.MallOrderItemDto;
@@ -161,4 +162,9 @@ public class AdminFeignServiceFallback implements AdminFeignService {
     public HttpResponseResult<StudentWrapper.UnionStudentResp> unionStudent(StudentWrapper.UnionStudent info) {
         return null;
     }
+
+    @Override
+    public ImUserInfo register(String userId, String clientType, String username, String avatar) {
+        return null;
+    }
 }

+ 20 - 8
cooleshow-auth/auth-server/src/main/java/com/yonge/cooleshow/auth/web/controller/TokenController.java

@@ -3,6 +3,8 @@ package com.yonge.cooleshow.auth.web.controller;
 import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.fasterxml.jackson.databind.ObjectMapper;
+import com.yonge.cooleshow.api.feign.AdminFeignService;
+import com.yonge.cooleshow.api.feign.dto.ImUserInfo;
 import com.yonge.cooleshow.auth.api.dto.QRLoginDto;
 import com.yonge.cooleshow.auth.api.entity.SysUser;
 import com.yonge.cooleshow.auth.api.vo.SysUserVo;
@@ -40,6 +42,7 @@ import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.client.RestTemplate;
 
+import javax.annotation.Resource;
 import javax.servlet.http.HttpServletRequest;
 import java.io.IOException;
 import java.text.MessageFormat;
@@ -70,6 +73,8 @@ public class TokenController extends BaseController {
 
     @Autowired
     private RedissonClient redissonClient;
+    @Resource
+    private AdminFeignService adminFeignService;
 
     @PostMapping(value = "/smsLogin", consumes = MediaType.APPLICATION_JSON_VALUE)
     @ApiOperation(value = "短信验证码的方式登录")
@@ -99,23 +104,30 @@ public class TokenController extends BaseController {
         return null;
     }
 
+
     @ApiOperation(value = "获取用户信息")
     @GetMapping("/api/queryUserInfo")
-    public Object apiQueryUserInfo() {
+    public Object apiQueryUserInfo(@RequestParam(value = "clientType", required = false, defaultValue = "TEACHER") String clientType) {
+
+        // 校验客户端类型
+        if (EClientType.invalid(clientType)) {
+            return failed("无效的客户端类型");
+        }
+
         AuthUser authUser = SecurityUtils.getUser();
         if (authUser != null) {
-            SysUser sysUser = userService.queryUserInfoWithIMToken(authUser.getUserId(), EClientType.valueOf(authUser.getClientId()));
-            sysUser.setTenantId(userService.getTenantByClient(authUser.getUserId(),authUser.getClientId()));
-            String imUserId = String.valueOf(sysUser.getId());
-            if (EClientType.STUDENT.match(authUser.getClientId())) {
-                imUserId = MessageFormat.format("{0}:{1}",imUserId, authUser.getClientId());
-            }
+            SysUser sysUser = userService.queryUserInfo(authUser.getUserId());
+
+            ImUserInfo register = adminFeignService.register(sysUser.getId().toString(), clientType, sysUser.getUsername(), sysUser.getAvatar());
+
+            sysUser.setImToken(register.getImToken());
             // 封装返回参数
-            return succeed(JSON.parseObject(JSON.toJSONString(sysUser), SysUserVo.class).imUserId(imUserId));
+            return succeed(JSON.parseObject(JSON.toJSONString(sysUser), SysUserVo.class).imUserId(register.getImUserId()));
         }
         return failed("获取用户信息失败");
     }
 
+
     @PostMapping(value = "/usernameLogin", consumes = MediaType.APPLICATION_JSON_VALUE)
     @ApiOperation(value = "手机号密码方式登录")
     @ApiImplicitParams({

+ 1 - 1
cooleshow-cms/src/main/resources/bootstrap-dev.properties

@@ -1,6 +1,6 @@
 
 #\u670d\u52a1\u5668\u5730\u5740
-spring.cloud.nacos.config.server-addr=10.206.0.17:8848
+spring.cloud.nacos.config.server-addr=43.137.4.92:8848
 #\u9ed8\u8ba4\u4e3aPublic\u547d\u540d\u7a7a\u95f4,\u53ef\u4ee5\u7701\u7565\u4e0d\u5199
 spring.cloud.nacos.config.namespace=6f8374a9-598f-4889-bb17-476070ffb8de
 #\u6307\u5b9a\u914d\u7f6e\u7fa4\u7ec4 --\u5982\u679c\u662fPublic\u547d\u540d\u7a7a\u95f4 \u5219\u53ef\u4ee5\u7701\u7565\u7fa4\u7ec4\u914d\u7f6e

+ 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;
+    }
+}

+ 7 - 15
cooleshow-common/src/main/java/com/yonge/cooleshow/common/enums/HardLevelEnum.java

@@ -5,25 +5,25 @@ import com.yonge.toolset.base.enums.BaseEnum;
 
 public enum HardLevelEnum implements BaseEnum<String, HardLevelEnum> {
 	/**
-	 * 入门级, 振幅阈值, 频率阈值 <br>
+	 * 入门级, 振幅阈值 <br>
 	 * 节奏有效范围(1分音符), 节奏有效范围(2分音符), 节奏有效范围(4分音符), 节奏有效范围(8分音符), 节奏有效范围(16分音符), 节奏有效范围(32分音符)<br>
 	 * 完成度范围, 未演奏的范围
 	 */
 	//BEGINNER("入门级", 3, 5, 5, 5, 10, 10, 13, 15, 60, 10), 
-	BEGINNER("入门级", 3, 5, 12, 18, 23, 28, 75, 25), 
+	BEGINNER("入门级", 3, 12, 18, 23, 28, 75, 25), 
 	/**
-	 * 进阶级, 振幅阈值, 频率阈值 <br>
+	 * 进阶级, 振幅阈值 <br>
 	 * 节奏有效范围(1分音符), 节奏有效范围(2分音符), 节奏有效范围(4分音符), 节奏有效范围(8分音符), 节奏有效范围(16分音符), 节奏有效范围(32分音符)<br>
 	 * 完成度范围, 未演奏的范围
 	 */
-	ADVANCED("进阶级", 3, 5, 10, 15, 20, 25, 85, 15),
+	ADVANCED("进阶级", 3, 10, 15, 20, 25, 85, 15),
 	//ADVANCED("进阶级", 3, 5, 50, 50, 50, 50, 50, 5, 80, 10),
 	/**
-	 * 大师级, 振幅阈值, 频率阈值 <br>
+	 * 大师级, 振幅阈值 <br>
 	 * 节奏有效范围(1分音符), 节奏有效范围(2分音符), 节奏有效范围(4分音符), 节奏有效范围(8分音符), 节奏有效范围(16分音符), 节奏有效范围(32分音符)<br>
 	 * 完成度范围, 未演奏的范围
 	 */
-	PERFORMER("大师级", 3, 3, 5, 10, 13, 15, 95, 10);
+	PERFORMER("大师级", 3, 5, 10, 13, 15, 95, 10);
 	@EnumValue
 	private String code;
 
@@ -31,8 +31,6 @@ public enum HardLevelEnum implements BaseEnum<String, HardLevelEnum> {
 
 	private int amplitudeThreshold;
 
-	private int frequencyThreshold;
-
 	private int tempoEffectiveRangeOf1;
 
 	private int tempoEffectiveRangeOf2;
@@ -50,7 +48,6 @@ public enum HardLevelEnum implements BaseEnum<String, HardLevelEnum> {
 	 * 
 	 * @param msg
 	 * @param amplitudeThreshold 振幅阈值
-	 * @param frequencyThreshold 频率阈值
 	 * @param tempoEffectiveRangeOf1 节奏偏移量百分比(在当前范围内节奏才算正确)
 	 * @param tempoEffectiveRangeOf2 节奏偏移量百分比(在当前范围内节奏才算正确)
 	 * @param tempoEffectiveRangeOf4 节奏偏移量百分比(在当前范围内节奏才算正确)
@@ -58,13 +55,12 @@ public enum HardLevelEnum implements BaseEnum<String, HardLevelEnum> {
 	 * @param integrityRange 完成度范围
 	 * @param notPlayRange 未演奏的范围
 	 */
-	HardLevelEnum(String msg, int amplitudeThreshold, int frequencyThreshold, int tempoEffectiveRangeOf1, int tempoEffectiveRangeOf2,
+	HardLevelEnum(String msg, int amplitudeThreshold, int tempoEffectiveRangeOf1, int tempoEffectiveRangeOf2,
 			int tempoEffectiveRangeOf4, int tempoEffectiveRangeOf8, int integrityRange,
 			int notPlayRange) {
 		this.code = this.name();
 		this.msg = msg;
 		this.amplitudeThreshold = amplitudeThreshold;
-		this.frequencyThreshold = frequencyThreshold;
 		this.tempoEffectiveRangeOf1 = tempoEffectiveRangeOf1;
 		this.tempoEffectiveRangeOf2 = tempoEffectiveRangeOf2;
 		this.tempoEffectiveRangeOf4 = tempoEffectiveRangeOf4;
@@ -81,10 +77,6 @@ public enum HardLevelEnum implements BaseEnum<String, HardLevelEnum> {
 		return amplitudeThreshold;
 	}
 
-	public int getFrequencyThreshold() {
-		return frequencyThreshold;
-	}
-	
 	public int getTempoEffectiveRange(int denominator, double duration) {
 		
 		int tempoEffectiveRange = 0;

+ 0 - 1
cooleshow-mall/mall-admin/src/main/java/com/yonge/cooleshow/admin/dto/PmsBrandParam.java

@@ -27,7 +27,6 @@ public class PmsBrandParam {
     @FlagValidator(value = {"0","1"}, message = "显示状态不正确")
     @ApiModelProperty(value = "是否进行显示")
     private Integer showStatus;
-    @NotEmpty
     @ApiModelProperty(value = "品牌logo",required = true)
     private String logo;
     @ApiModelProperty(value = "品牌大图")

+ 1 - 1
cooleshow-mall/mall-admin/src/main/resources/logback-spring.xml

@@ -27,7 +27,7 @@
 		</encoder>
 	</appender>
 
-	<logger name="com.yonge" level="info" />
+	<logger name="com.yonge" level="INFO" />
 
 	<!--本地环境:打印控制台 -->
 	<springProfile name="local">

+ 7 - 0
cooleshow-mall/mall-portal/src/main/java/com/yonge/cooleshow/portal/controller/PaymentController.java

@@ -31,6 +31,7 @@ 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.http.HttpStatus;
 import org.springframework.web.bind.annotation.*;
 
@@ -68,6 +69,9 @@ public class PaymentController extends BaseController {
     @Autowired
     private RedissonClient redissonClient;
 
+    @Value("${message.debugMode}")
+    private boolean debugMode;
+
     @ApiOperation(value = "订单付款")
     @PostMapping("/orderPay")
     public HttpResponseResult<OrderPayRes> orderPay(@Valid @RequestBody OrderPayReq payReq, HttpServletRequest request) {
@@ -144,6 +148,9 @@ public class PaymentController extends BaseController {
             @PathVariable("payMethod") String payMethod,
             HttpServletRequest request
     ) {
+        // if (debugMode) {
+        //     return null;
+        // }
         BaseResult<PaymentCallBack> res = paymentClient.analysisNotice(
                 OpenEnum.valueOf(openType), PaymentClientEnum.valueOf(client),
                 PayChannelEnum.valueOf(payChannel), MethodNameEnum.valueOf(payMethod), request);

+ 12 - 0
cooleshow-mall/mall-portal/src/main/java/com/yonge/cooleshow/portal/dto/OrderCreate.java

@@ -44,12 +44,24 @@ public class OrderCreate {
     @ApiModelProperty(value = "区")
     private String receiverRegion;
 
+
+    @ApiModelProperty(value = "订单来源 JMEDU: 管乐团 COOLESHOW_EDU:课堂乐器")
+    private String platformType = "JMEDU";
+
     @ApiModelProperty(value = "详细地址")
     private String receiverDetailAddress;
 
     @ApiModelProperty("商品详情")
     private List<OrderItem> orderItemList;
 
+    public String getPlatformType() {
+        return platformType;
+    }
+
+    public void setPlatformType(String platformType) {
+        this.platformType = platformType;
+    }
+
     public static class OrderItem implements Serializable {
 
 

+ 7 - 3
cooleshow-mall/mall-portal/src/main/java/com/yonge/cooleshow/portal/service/impl/OmsPortalOrderServiceImpl.java

@@ -377,7 +377,9 @@ public class OmsPortalOrderServiceImpl implements OmsPortalOrderService {
 
     private void setPromoterRecord(OmsOrderDetail detail,PostStatusEnum status) {
 
+        LOG.info("orderItemList detail {}",detail);
         List<OmsOrderItem> orderItemList = detail.getOrderItemList();
+        LOG.info("orderItemList info {}",orderItemList);
 
         // orderItemList = orderItemList.stream().filter(omsOrderItem -> omsOrderItem.getPromoterId() != null).collect(Collectors.toList());
         if (CollectionUtils.isEmpty(orderItemList) ) {
@@ -397,6 +399,7 @@ public class OmsPortalOrderServiceImpl implements OmsPortalOrderService {
             teacherShareDto.setUserId(detail.getMemberId());
             list.add(teacherShareDto);
         }
+        LOG.info("list info {}",list);
         if (CollectionUtils.isEmpty(list)) {
             return;
         }
@@ -627,6 +630,7 @@ public class OmsPortalOrderServiceImpl implements OmsPortalOrderService {
         example.createCriteria().andOrderIdEqualTo(orderId);
         List<OmsOrderItem> orderItemList = orderItemMapper.selectByExample(example);
         OmsOrderDetail orderDetail = new OmsOrderDetail();
+        LOG.info("orderItemList orderItemList {}",orderItemList);
         BeanUtil.copyProperties(omsOrder, orderDetail);
         orderDetail.setOrderItemList(orderItemList);
         return orderDetail;
@@ -951,7 +955,7 @@ public class OmsPortalOrderServiceImpl implements OmsPortalOrderService {
 
         detail = new OmsOrder();
         detail.setMemberId(umsMember.getId());
-        detail.setPlatformType("JMEDU");
+        detail.setPlatformType(order.getPlatformType());
         detail.setOrderSn(order.getOrderNo());
         detail.setCreateTime(new Date());
         detail.setMemberUsername(umsMember.getUsername());
@@ -1499,7 +1503,7 @@ public class OmsPortalOrderServiceImpl implements OmsPortalOrderService {
      * 对优惠券优惠进行处理
      *
      * @param orderItemList       order_item列表
-     * @param couponHistoryDetail 可用优惠券详情
+     * @param couponAmount 可用优惠券详情
      */
     private void handleCouponAmount(List<OmsOrderItem> orderItemList, BigDecimal couponAmount) {
         // SmsCoupon coupon = couponHistoryDetail.getCoupon();
@@ -1525,7 +1529,6 @@ public class OmsPortalOrderServiceImpl implements OmsPortalOrderService {
      * @param orderItemList 可用优惠券的下单商品商品
      */
     private void calcPerCouponAmount(List<OmsOrderItem> orderItemList, BigDecimal coupon) {
-
         BigDecimal totalAmount = calcTotalAmount(orderItemList);
         for (OmsOrderItem orderItem : orderItemList) {
             //(商品价格/可用商品总价)*优惠券面额
@@ -1533,6 +1536,7 @@ public class OmsPortalOrderServiceImpl implements OmsPortalOrderService {
                                                .divide(totalAmount, 2, BigDecimal.ROUND_DOWN)
                                                .multiply(coupon);
             orderItem.setCouponAmount(couponAmount);
+
             orderItem.setPrecisionAmount(BigDecimal.ZERO);
 
         }

+ 1 - 1
cooleshow-task/src/main/resources/logback-spring.xml

@@ -27,7 +27,7 @@
 		</encoder>
 	</appender>
 
-	<logger name="com.yonge" level="info" />
+	<logger name="com.yonge" level="INFO" />
 
 	<!--本地环境:打印控制台 -->
 	<springProfile name="local">

+ 97 - 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,81 @@ 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.DATE, -1);
+
+        // 获取减少一天后的日期
+        Date tommorow = calendar.getTime();
+
+        calendar.setTime(currentDate);
+
+        //按照小时递减
+        while (currentDate.after(tommorow))
+        {
+            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;
+                }*/
+                if (!StringUtils.isEmpty(url)){
+                    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);
+
+
+    }
+
+    @GetMapping(value = "/groupTransfer")
+    @ApiOperation("群导入")
+    public void groupTransfer() {
+        imGroupService.groupTransfer();
+    }
+
+
+
+
 }
 

+ 6 - 1
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/MusicSheetController.java

@@ -43,6 +43,11 @@ import com.yonge.cooleshow.biz.dal.enums.OrderTypeEnum;
 import com.yonge.cooleshow.biz.dal.enums.SourceTypeEnum;
 import com.yonge.cooleshow.biz.dal.service.MusicSheetService;
 import com.yonge.cooleshow.biz.dal.service.MusicTagService;
+import com.yonge.cooleshow.common.enums.YesOrNoEnum;
+import com.yonge.toolset.base.exception.BizException;
+import com.yonge.toolset.base.page.QueryInfo;
+import com.yonge.toolset.mybatis.support.PageUtil;
+import com.yonge.cooleshow.biz.dal.vo.*;
 import com.yonge.cooleshow.biz.dal.vo.MusicSheetDetailVo;
 import com.yonge.cooleshow.biz.dal.vo.MusicSheetStudentByMessage;
 import com.yonge.cooleshow.biz.dal.vo.MusicSheetVo;
@@ -214,7 +219,7 @@ public class MusicSheetController extends BaseController {
             return failed("缺少ID");
         }
         MusicSheet musicSheet = musicSheetService.getById(musicSheetRenderDto.getMusicSheetId());
-        
+
         if(musicSheet == null){
         	return failed("参数异常");
         }

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

@@ -0,0 +1,267 @@
+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.api.feign.dto.ImUserInfo;
+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.ImGroupService;
+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.biz.dal.wrapper.im.ImGroupWrapper;
+import com.yonge.cooleshow.common.controller.BaseController;
+import com.yonge.cooleshow.common.entity.HttpResponseResult;
+import com.yonge.toolset.base.exception.BizException;
+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;
+
+    @Autowired
+    private ImGroupService imGroupService;
+
+    @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();
+    }
+
+    @ApiOperation("注册im用户")
+    @PostMapping(value = "/im/register")
+    public ImUserInfo register(String userId, ClientEnum clientType, String username, String avatar) {
+
+        log.info("注册im用户");
+
+        try {
+            ImGroupWrapper.ImUserInfo register = imGroupService.register(userId, clientType, username, avatar);
+            return JSON.parseObject(JSON.toJSONString(register), ImUserInfo.class);
+        } catch (Exception e) {
+            log.error("注册im用户失败", e);
+            throw new BizException(e.getMessage());
+        }
+    }
+
+}
+

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

@@ -1,96 +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.beans.factory.annotation.Value;
-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();
-    }
-}
-

+ 3 - 0
cooleshow-user/user-admin/src/main/java/com/yonge/cooleshow/admin/controller/open/UserOrderClient.java

@@ -87,6 +87,9 @@ public class UserOrderClient extends BaseController {
             @PathVariable("payMethod") String payMethod,
             HttpServletRequest request
     ) {
+        // if (debugMode) {
+        //     return null;
+        // }
         //支付回调:openType is ORIGINAL ,paymentClient is STUDENT,payChannel is ali_app,payMethod is executePayment
         log.info("支付回调:openType is {} ,paymentClient is {},payChannel is {},payMethod is {}", openType, client, payChannel, payMethod);
         BaseResult<PaymentCallBack> res = paymentClient.analysisNotice(

+ 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();
+    }
+
 }

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

@@ -108,6 +108,22 @@
             <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>
+        <dependency>
+            <groupId>com.microsvc.toolkit.middleware</groupId>
+            <artifactId>microsvc-middleware-rtc</artifactId>
+            <version>1.0.0</version>
+        </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: 获取群成员列表

+ 6 - 79
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/ImNetworkDeviceControlDto.java

@@ -3,7 +3,9 @@ package com.yonge.cooleshow.biz.dal.dto;
 import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
 import com.yonge.cooleshow.biz.dal.enums.ImNetworkDeviceTypeEnum;
 import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
 
+@Data
 public class ImNetworkDeviceControlDto {
 	@ApiModelProperty(value = "需要操作的类型",required = true)
 	private ImNetworkDeviceTypeEnum deviceType;
@@ -35,83 +37,8 @@ public class ImNetworkDeviceControlDto {
 	@ApiModelProperty("客户端类型 ")
 	private ClientEnum clientType;
 
-	public ImNetworkDeviceTypeEnum getDeviceType() {
-		return deviceType;
-	}
-
-	public void setDeviceType(ImNetworkDeviceTypeEnum deviceType) {
-		this.deviceType = deviceType;
-	}
-
-	public Boolean getEnable() {
-		return enable;
-	}
-
-	public void setEnable(Boolean enable) {
-		this.enable = enable;
-	}
-
-	public String getRoomId() {
-		return roomId;
-	}
-
-	public void setRoomId(String roomId) {
-		this.roomId = roomId;
-	}
-
-	public String getUserId() {
-		return userId;
-	}
-
-	public void setUserId(String userId) {
-		this.userId = userId;
-	}
-
-	public String getTicket() {
-		return ticket;
-	}
-
-	public void setTicket(String ticket) {
-		this.ticket = ticket;
-	}
-
-	public Integer getStatus() {
-		return status;
-	}
-
-	public void setStatus(Integer status) {
-		this.status = status;
-	}
-
-	public Integer getMusicSheetId() {
-		return musicSheetId;
-	}
-
-	public void setMusicSheetId(Integer musicSheetId) {
-		this.musicSheetId = musicSheetId;
-	}
-
-	public Integer getMusicSheetAccompanimentId() {
-		return musicSheetAccompanimentId;
-	}
-
-	public void setMusicSheetAccompanimentId(Integer musicSheetAccompanimentId) {
-		this.musicSheetAccompanimentId = musicSheetAccompanimentId;
-	}
-
-	public Integer getSoundVolume() {
-		return soundVolume;
-	}
-
-	public void setSoundVolume(Integer soundVolume) {
-		this.soundVolume = soundVolume;
-	}
-
-	public ClientEnum getClientType() {
-		return clientType;
-	}
-
-	public void setClientType(ClientEnum clientType) {
-		this.clientType = clientType;
-	}
+	// 发送用户信息
+	private String sendUserId;
+	private String sendUserName;
+	private String avatar;
 }

+ 11 - 37
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/dto/ImNetworkRoomResult.java

@@ -1,11 +1,16 @@
 package com.yonge.cooleshow.biz.dal.dto;
 
+import com.microsvc.toolkit.middleware.rtc.message.RTCRoomConfig;
 import com.yonge.cooleshow.biz.dal.entity.ImNetworkRoom;
 import com.yonge.cooleshow.biz.dal.entity.ImNetworkRoomMember;
 import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
 
 import java.util.List;
 
+@Data
 public class ImNetworkRoomResult extends ImNetworkRoom {
 
     @ApiModelProperty(value = "陪练课结束后,XX分钟关闭房间")
@@ -23,43 +28,12 @@ public class ImNetworkRoomResult extends ImNetworkRoom {
     @ApiModelProperty(value = "节拍器参数")
     private ImNetworkCustomMessage midiJson = new ImNetworkCustomMessage();
 
-    public ImNetworkCustomMessage getMidiJson() {
-        return midiJson;
-    }
+    @ApiModelProperty("RTC接入参数")
+    private RTCRoomConfig rtcRoomConfig;
 
-    public void setMidiJson(ImNetworkCustomMessage midiJson) {
-        this.midiJson = midiJson;
-    }
+    @ApiModelProperty("直播间用户签名")
+    private String userSig;
 
-    public Integer getSurplusTime() {
-        return surplusTime;
-    }
-
-    public void setSurplusTime(Integer surplusTime) {
-        this.surplusTime = surplusTime;
-    }
-
-    public String getAutoCloseNetworkRoomTime() {
-        return autoCloseNetworkRoomTime;
-    }
-
-    public void setAutoCloseNetworkRoomTime(String autoCloseNetworkRoomTime) {
-        this.autoCloseNetworkRoomTime = autoCloseNetworkRoomTime;
-    }
-
-    public List<ImNetworkRoomMember> getRoomMemberList() {
-        return roomMemberList;
-    }
-
-    public void setRoomMemberList(List<ImNetworkRoomMember> roomMemberList) {
-        this.roomMemberList = roomMemberList;
-    }
-
-    public ImNetworkRoomMember getRoomMember() {
-        return roomMember;
-    }
-
-    public void setRoomMember(ImNetworkRoomMember roomMember) {
-        this.roomMember = roomMember;
-    }
+    @ApiModelProperty("群组id")
+    private String groupId;
 }

+ 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;
-    }
 }
 

+ 15 - 217
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,10 @@ 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 +91,17 @@ 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;
-    }
+    @TableField("service_provider_")
+    @ApiModelProperty("服务提供方")
+    private String serviceProvider;
 
-    public void setUpdatedTime(Date updatedTime) {
-        this.updatedTime = updatedTime;
-    }
+    @TableField("room_id_")
+    @ApiModelProperty("房间号")
+    private String roomId;
 
-    public CourseSchedule updatedTime(Date updatedTime) {
-        this.updatedTime = updatedTime;
-        return this;
-    }
+    @ApiModelProperty("全员静音")
+    @TableField("mute_all_")
+    private Boolean muteAll;
 
 }
 

+ 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;
     }

+ 36 - 36
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/entity/MusicSheet.java

@@ -217,16 +217,16 @@ public class MusicSheet implements Serializable {
 	@TableField("ext_config_json_")
 	@ApiModelProperty("曲目配置信息")
 	private String extConfigJson;
-	
+
 	@TableField("music_json_")
 	private String musicJSON;
-	
+
 	@TableField("music_svg_")
 	private String musicSvg;
-	
+
 	@TableField("music_jian_svg_")
 	private String musicJianSvg;
-	
+
 	@TableField("music_first_svg_")
 	private String musicFirstSvg;
 
@@ -607,6 +607,38 @@ public class MusicSheet implements Serializable {
 		this.paymentType = paymentType;
 	}
 
+	public String getMusicJSON() {
+		return musicJSON;
+	}
+
+	public void setMusicJSON(String musicJSON) {
+		this.musicJSON = musicJSON;
+	}
+
+	public String getMusicSvg() {
+		return musicSvg;
+	}
+
+	public void setMusicSvg(String musicSvg) {
+		this.musicSvg = musicSvg;
+	}
+
+	public String getMusicJianSvg() {
+		return musicJianSvg;
+	}
+
+	public void setMusicJianSvg(String musicJianSvg) {
+		this.musicJianSvg = musicJianSvg;
+	}
+
+	public String getMusicFirstSvg() {
+		return musicFirstSvg;
+	}
+
+	public void setMusicFirstSvg(String musicFirstSvg) {
+		this.musicFirstSvg = musicFirstSvg;
+	}
+
 	public String getFirstTone() {
 		return firstTone;
 	}
@@ -658,38 +690,6 @@ public class MusicSheet implements Serializable {
 		return this;
 	}
 
-	public String getMusicJSON() {
-		return musicJSON;
-	}
-
-	public void setMusicJSON(String musicJSON) {
-		this.musicJSON = musicJSON;
-	}
-
-	public String getMusicSvg() {
-		return musicSvg;
-	}
-
-	public void setMusicSvg(String musicSvg) {
-		this.musicSvg = musicSvg;
-	}
-
-	public String getMusicJianSvg() {
-		return musicJianSvg;
-	}
-
-	public void setMusicJianSvg(String musicJianSvg) {
-		this.musicJianSvg = musicJianSvg;
-	}
-
-	public String getMusicFirstSvg() {
-		return musicFirstSvg;
-	}
-
-	public void setMusicFirstSvg(String musicFirstSvg) {
-		this.musicFirstSvg = musicFirstSvg;
-	}
-
 	public MusicSheet firstTone(String firstTone) {
 		this.firstTone = firstTone;
 		return this;

+ 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;
-    }
 }

+ 36 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/enums/RoleEnum.java

@@ -0,0 +1,36 @@
+package com.yonge.cooleshow.biz.dal.enums;
+
+import com.baomidou.mybatisplus.extension.exceptions.ApiException;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Created by weiqinxiao on 2019/2/28.
+ */
+public enum RoleEnum {
+    RoleAssistant("RoleAssistant", 1),
+    RoleTeacher("RoleTeacher", 2),
+    RoleStudent("RoleStudent", 3),
+    RoleAudience("RoleAudience", 4);
+
+    private @Getter
+    @Setter(AccessLevel.PRIVATE) String msg;
+    private @Getter
+    @Setter(AccessLevel.PRIVATE) int value;
+
+    RoleEnum(String msg, int value) {
+        this.msg = msg;
+        this.value = value;
+    }
+
+    public static RoleEnum getEnumByValue(int v) {
+        for(RoleEnum item : RoleEnum.values()) {
+            if(item.getValue() == v) {
+                return item;
+            }
+        }
+
+        throw new ApiException(v + " not valid role");
+    }
+}

+ 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;
+    }
+}

+ 11 - 10
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/ActivityEvaluationRecordService.java

@@ -52,15 +52,6 @@ public interface ActivityEvaluationRecordService extends IService<ActivityEvalua
 
 
 
-	/**
-	 * 添加活动参与记录
-	 *
-	 * @param activityId 活动id
-	 * @param userId 用户id
-	 * @param resourceId 资源id
-	 * @param resourceType 资源类型
-	 */
-    void saveActivityRecord(Long activityId, Long userId, Long resourceId, ActivityResourceEnum resourceType);
 
 	/**
 	 * 查询指定活动指定曲目的排行榜
@@ -71,6 +62,7 @@ public interface ActivityEvaluationRecordService extends IService<ActivityEvalua
 	 */
 	List<ActivityRankingVo> queryRankingList(Long activityPlanId, Long activityEvaluationId, int limit);
 	
+	
 	/**
 	 * 查询指定用户的排名
 	 * @param activityPlanId
@@ -81,6 +73,16 @@ public interface ActivityEvaluationRecordService extends IService<ActivityEvalua
 	ActivityRankingVo queryUserRanking(Long activityPlanId, Long activityEvaluationId, Long userId);
 
 	/**
+	 * 添加活动参与记录
+	 *
+	 * @param activityId 活动id
+	 * @param userId 用户id
+	 * @param resourceId 资源id
+	 * @param resourceType 资源类型
+	 */
+    void saveActivityRecord(Long activityId, Long userId, Long resourceId, ActivityResourceEnum resourceType);
+
+	/**
 	 * 用户活动最高排名信息
 	 * @param activityId 活动ID
 	 * @param userIds 用户ID
@@ -88,5 +90,4 @@ public interface ActivityEvaluationRecordService extends IService<ActivityEvalua
 	 */
 	List<ActivityEvaluationRecord> queryActivityUserHighestRankingInfo(Long activityId, List<Long> userIds);
 
-
 }

+ 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: 删除群成员

+ 77 - 5
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/ImGroupService.java

@@ -1,13 +1,20 @@
 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.enums.RoleEnum;
+import com.yonge.cooleshow.biz.dal.wrapper.im.ImGroupWrapper;
+import io.rong.models.Result;
 import com.yonge.cooleshow.biz.dal.wrapper.im.ImGroupWrapper;
 
+import java.io.File;
 import java.util.List;
 import java.util.Set;
 
@@ -21,6 +28,37 @@ 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);
+
+
+    String getImUserId(Long userId, RoleEnum userRole);
+
+
+    String getImUserId(Long userId, ClientEnum clientEnum);
+
+    /**
+     * 解析IM用户规则
+     * @param imUserId IM用户Id
+     * @return String
+     */
+    String analysisImUserId(String imUserId);
+
     /**
     * @description: 用户主动创建群聊
      * @param imGroup
@@ -38,6 +76,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
@@ -72,11 +114,6 @@ public interface ImGroupService extends IService<ImGroup> {
      */
     ImGroup getByCourseGroupId(Long courseGroupId);
 
-    /**
-     * 旧数据创建粉丝群
-     */
-    void setTeacherFansGroup() throws Exception;
-
     //退群
     void quit(String groupId, Long userId, ClientEnum clientType) throws Exception;
 
@@ -87,6 +124,11 @@ public interface ImGroupService extends IService<ImGroup> {
      * @return ImGroup
      */
     ImGroup findGroupInfoById(String groupId, Long userId);
+    /**
+     * 旧数据创建粉丝群
+     */
+    void setTeacherFansGroup() throws Exception;
+
 
     /**
      * 添加群成员
@@ -94,5 +136,35 @@ 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;
+
+    /**
+     * 群迁移,融云->腾讯
+     */
+    void groupTransfer();
 }
 

+ 2 - 1
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/ImNetworkRoomMemberService.java

@@ -5,6 +5,7 @@ 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.RoleEnum;
 import com.yonge.cooleshow.biz.dal.enums.UserRoleEnum;
 
 import java.util.Date;
@@ -20,7 +21,7 @@ public interface ImNetworkRoomMemberService extends IService<ImNetworkRoomMember
 
     ImNetworkRoomMemberDao getDao();
 
-    ImNetworkRoomMember initRoomMember(String roomId, BasicUserInfo sysUser, UserRoleEnum userRole);
+    ImNetworkRoomMember initRoomMember(String roomId, BasicUserInfo sysUser, RoleEnum userRole);
 
 }
 

+ 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 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/MusicSheetService.java

@@ -1,5 +1,7 @@
 package com.yonge.cooleshow.biz.dal.service;
 
+import java.util.List;
+
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.yonge.cooleshow.auth.api.entity.SysUser;

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

@@ -59,19 +59,21 @@ public class ActivityEvaluationRecordServiceImpl extends ServiceImpl<ActivityEva
     @Autowired
     private SubjectService subjectService;
 
-	@Override
+    @Override
     public ActivityEvaluationRecordVo detail(Long id) {
         return baseMapper.detail(id);
     }
-    
+
     @Override
-    public IPage<ActivityEvaluationRecordVo> selectPage(IPage<ActivityEvaluationRecordVo> page, ActivityEvaluationRecordSearch query){
+    public IPage<ActivityEvaluationRecordVo>
+    selectPage(IPage<ActivityEvaluationRecordVo> page,
+               ActivityEvaluationRecordSearch query) {
         return page.setRecords(baseMapper.selectPage(page, query));
     }
 
     @Override
     public List<Long> getJoinEvaluationIdList(Long activityId, Long userId) {
-        return baseMapper.getJoinEvaluationIdList(activityId,userId);
+        return baseMapper.getJoinEvaluationIdList(activityId, userId);
     }
 
     @Override
@@ -91,13 +93,13 @@ public class ActivityEvaluationRecordServiceImpl extends ServiceImpl<ActivityEva
         activityEvaluationRecord.setEvaluationId(evaluationId);
         activityEvaluationRecord.setResourceId(activityEvaluation.getMusicSheetId());
         activityEvaluationRecord.setCreateTime(new Date());
-        activityEvaluationRecord.setScore(score == null ? 0:score.doubleValue());
-        
+        activityEvaluationRecord.setScore(score == null ? 0 : score.doubleValue());
+
         ActivityEvaluationRecord lastestRecord = baseMapper.queryLastestRecord(activityEvaluation.getActivityId(), userId);
-        if(lastestRecord == null){
-        	activityEvaluationRecord.setTimes(1);
-        }else{
-        	activityEvaluationRecord.setTimes(lastestRecord.getTimes() + 1);
+        if (lastestRecord == null) {
+            activityEvaluationRecord.setTimes(1);
+        } else {
+            activityEvaluationRecord.setTimes(lastestRecord.getTimes() + 1);
         }
         save(activityEvaluationRecord);
 
@@ -192,17 +194,15 @@ public class ActivityEvaluationRecordServiceImpl extends ServiceImpl<ActivityEva
 
             });
         }
-
     }
 
-
     @Override
     public void saveActivityRecord(Long activityId, Long userId, Long resourceId, ActivityResourceEnum resourceType) {
         List<ActivityEvaluation> list = activityEvaluationService.lambdaQuery()
-                                                                 .eq(ActivityEvaluation::getActivityId, activityId)
-                                                                 .eq(ActivityEvaluation::getMusicSheetId, resourceId)
-                                                                 .eq(ActivityEvaluation::getResourceType, resourceType)
-                                                                 .list();
+                .eq(ActivityEvaluation::getActivityId, activityId)
+                .eq(ActivityEvaluation::getMusicSheetId, resourceId)
+                .eq(ActivityEvaluation::getResourceType, resourceType)
+                .list();
         ActivityEvaluation activityEvaluation = new ActivityEvaluation();
         if (!CollectionUtils.isEmpty(list)) {
             activityEvaluation = list.get(0);
@@ -301,27 +301,28 @@ public class ActivityEvaluationRecordServiceImpl extends ServiceImpl<ActivityEva
         return userRanking;
 	}
 
-    /**
-     * 用户活动最高排名信息
-     *
-     * @param activityId 活动ID
-     * @param userIds 用户ID
-     * @return List<ActivityEvaluationRecord>
-     */
-    @Override
-    public List<ActivityEvaluationRecord> queryActivityUserHighestRankingInfo(Long activityId, List<Long> userIds) {
-
-        if (CollectionUtils.isEmpty(userIds)) {
-            return Lists.newArrayList();
-        }
-        ActivityPlan activityPlan = activityPlanService.getById(activityId);
-        if (activityPlan == null) {
-            throw new BizException("活动已结束");
-        }
-        // 用户最高排名信息
-        List<ActivityEvaluationRecord> records = getBaseMapper().selectUserHighestRankingInfo(activityId, userIds,activityPlan.getRankingRule());
-
-        return Optional.ofNullable(records).orElse(Lists.newArrayList());
+  /**
+   * 用户活动最高排名信息
+   *
+   * @param activityId 活动ID
+   * @param userIds 用户ID
+   * @return List<ActivityEvaluationRecord>
+   */
+  @Override
+  public List<ActivityEvaluationRecord> queryActivityUserHighestRankingInfo(Long activityId, List<Long> userIds) {
+
+    if (CollectionUtils.isEmpty(userIds)) {
+      return Lists.newArrayList();
+    }
+    ActivityPlan activityPlan = activityPlanService.getById(activityId);
+    if (activityPlan == null) {
+      throw new BizException("活动已结束");
     }
+    // 用户最高排名信息
+    List<ActivityEvaluationRecord> records =
+        getBaseMapper().selectUserHighestRankingInfo(
+            activityId, userIds, activityPlan.getRankingRule());
 
+    return Optional.ofNullable(records).orElse(Lists.newArrayList());
+  }
 }

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

@@ -1,5 +1,18 @@
 package com.yonge.cooleshow.biz.dal.service.impl;
 
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+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.stereotype.Service;
 import com.alibaba.fastjson.JSON;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@@ -47,17 +60,18 @@ import java.util.Objects;
 import java.util.Optional;
 import java.util.stream.Collectors;
 
+@Slf4j
 @Service
 public class ActivityEvaluationServiceImpl extends ServiceImpl<ActivityEvaluationDao, ActivityEvaluation> implements ActivityEvaluationService {
 
     @Autowired
     private ActivityPlanService activityPlanService;
-
+    
     @Autowired
-    private SubjectService subjectService;    
+    private ActivityEvaluationRecordService activityEvaluationRecordService;
 
     @Autowired
-    private ActivityEvaluationRecordService activityEvaluationRecordService;
+    private SubjectService subjectService;
 
     @Autowired
     private ActivityEvaluationService activityEvaluationService;

+ 1 - 1
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/service/impl/ActivityPlanServiceImpl.java

@@ -844,6 +844,7 @@ public class ActivityPlanServiceImpl extends ServiceImpl<ActivityPlanDao, Activi
         Long activityId = saveOrUpdateRewardDto.getActivityId();
         List<ActivityPlanRewardDto> updateRewardDtoList = saveOrUpdateRewardDto.getUpdateRewardDtoList();
 
+
 		if(CollectionUtils.isEmpty(updateRewardDtoList)){
             updateRewardDtoList = new ArrayList<>();
         }
@@ -902,7 +903,6 @@ public class ActivityPlanServiceImpl extends ServiceImpl<ActivityPlanDao, Activi
                 activityPlanRewardService.updateById(item);
             }
         }
-
         //还有删除的
         List<Long> rewardIds = updateRewardDtoList.stream().map(ActivityPlanRewardDto::getRewardId).collect(Collectors.toList());
         List<ActivityPlanReward> delList = activityPlanRewardService.getDelRewardList(activityId, rewardIds);

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

@@ -8,6 +8,7 @@ import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
 import com.yonge.cooleshow.auth.api.entity.SysUser;
 import com.yonge.cooleshow.biz.dal.dao.ActivityRewardChangeStockDao;
 import com.yonge.cooleshow.biz.dal.dao.ActivityRewardDao;
+import com.yonge.cooleshow.biz.dal.dto.ActivityPlanRewardDto;
 import com.yonge.cooleshow.biz.dal.dto.ActivityRewardDto;
 import com.yonge.cooleshow.biz.dal.dto.UserParam;
 import com.yonge.cooleshow.biz.dal.dto.search.ActivityRewardChangeStockSearch;
@@ -48,9 +49,15 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import javax.annotation.Resource;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
 import java.util.function.Function;
 import java.util.stream.Collectors;
+import java.util.*;
 
 
 @Service

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

@@ -8,6 +8,10 @@ import com.yonge.cooleshow.biz.dal.dao.ActivityUserRewardDao;
 import com.yonge.cooleshow.biz.dal.dto.search.ActivityUserRewardSearch;
 import com.yonge.cooleshow.biz.dal.entity.ActivityEvaluationRecord;
 import com.yonge.cooleshow.biz.dal.entity.ActivityPlan;
+import com.yonge.cooleshow.biz.dal.entity.ActivityUserReward;
+import com.yonge.cooleshow.biz.dal.service.ActivityPlanService;
+import com.yonge.cooleshow.biz.dal.service.ActivityUserRewardService;
+import com.yonge.cooleshow.biz.dal.vo.ActivityUserRewardVo;
 import com.yonge.cooleshow.biz.dal.entity.ActivityReward;
 import com.yonge.cooleshow.biz.dal.entity.ActivityUserReward;
 import com.yonge.cooleshow.biz.dal.enums.ClientEnum;

+ 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()));
                 }
             }
         }

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

@@ -1,49 +1,83 @@
 package com.yonge.cooleshow.biz.dal.service.impl;
 
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
 import com.alibaba.fastjson.JSON;
 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.auth.config.AppGlobalServiceConfig;
-import com.yonge.cooleshow.biz.dal.config.RongCloudConfig;
 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.*;
+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.CourseGroupService;
+import com.yonge.cooleshow.biz.dal.service.ImGroupMemberService;
+import com.yonge.cooleshow.biz.dal.service.ImGroupService;
+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.CourseGroup;
 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.entity.StudentStar;
 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.enums.RoleEnum;
 import com.yonge.cooleshow.biz.dal.service.CourseGroupService;
+import com.yonge.cooleshow.biz.dal.service.CustomerServiceBatchSendingService;
 import com.yonge.cooleshow.biz.dal.service.ImGroupMemberService;
 import com.yonge.cooleshow.biz.dal.service.ImGroupService;
 import com.yonge.cooleshow.biz.dal.service.ImUserFriendService;
 import com.yonge.cooleshow.biz.dal.service.StudentStarService;
+import com.yonge.cooleshow.biz.dal.service.SysConfigService;
 import com.yonge.cooleshow.biz.dal.service.SysUserService;
 import com.yonge.cooleshow.biz.dal.service.TeacherService;
 import com.yonge.cooleshow.biz.dal.wrapper.im.ImGroupWrapper;
 import com.yonge.toolset.base.exception.BizException;
 import com.yonge.toolset.base.util.ThreadPool;
+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.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.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
 import java.text.MessageFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.*;
 import java.util.Collections;
 import java.util.Date;
 import java.util.List;
@@ -53,6 +87,10 @@ import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipInputStream;
+import java.util.stream.Collectors;
 
 /**
  * 即时通讯群组(ImGroup)表服务实现类
@@ -92,11 +130,118 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
     @Autowired
     private StudentStarService studentStarService;
 
+    @Autowired
+    private CustomerServiceBatchSendingService customerServiceBatchSendingService;
+
+
+    @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;
+    }
+
+    @Override
+    public String getImUserId(Long userId, RoleEnum userRole) {
+        String imUserId = String.valueOf(userId);
+        String clientType = "STUDENT";
+        if(userRole.RoleTeacher == userRole){
+            clientType = "TEACHER";
+        }
+        if (StringUtils.isNotBlank(imConfig.getAppPrefix()) && !imUserId.startsWith(imConfig.getAppPrefix())) {
+            imUserId = MessageFormat.format("{0}_{1}_{2}", imConfig.getAppPrefix(), userId, clientType);
+        }
+        return imUserId;
+    }
+
+    @Override
+    public String getImUserId(Long userId, ClientEnum clientEnum) {
+        String imUserId = String.valueOf(userId);
+        String clientType = "STUDENT";
+        if(clientEnum.TEACHER == clientEnum){
+            clientType = "TEACHER";
+        }
+        if (StringUtils.isNotBlank(imConfig.getAppPrefix()) && !imUserId.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(ImGroupWrapper.ImGroup imGroup) throws Exception {
@@ -111,10 +256,10 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
         ImGroup group = JSON.parseObject(JSON.toJSONString(imGroup), ImGroup.class);
         this.baseMapper.insert(group);
         //处理本地群成员列表
-        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);
         }
@@ -136,8 +281,8 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
             throw new BizException("添加的群成员不能为空");
 
         }
-        List<GroupMember> groupMemberList = imGroupMemberService.initGroupMembers(groupId,
-                studentIdList, ImGroupMemberRoleType.STUDENT);
+        List<ImGroupMember> groupMemberList = imGroupMemberService.initGroupMembers(groupId,
+                imGroup.getStudentIdList(), ImGroupMemberRoleType.STUDENT);
         imGroupMemberService.join(groupMemberList, groupId);
     }
 
@@ -175,9 +320,13 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
         imUserFriendService.saveUserFriend(teacherId, studentIds);
         //处理本地群成员列表
         // 添加老师
-        List<GroupMember> groupMembers = imGroupMemberService.initGroupMember(imGroupId, imGroup.getCreateBy(), true, ImGroupMemberRoleType.TEACHER);
-        // 添加学生
-        groupMembers.addAll(imGroupMemberService.initGroupMembers(imGroupId, studentIds, ImGroupMemberRoleType.STUDENT));
+        List<ImGroupMember> groupMembers = imGroupMemberService.initGroupMember(imGroupId, imGroup.getCreateBy(),
+                true, ImGroupMemberRoleType.TEACHER);
+        if (CollectionUtils.isNotEmpty(studentIds)) {
+            // 添加学生
+            groupMembers.addAll(imGroupMemberService.initGroupMembers(imGroupId, studentIds,
+                    ImGroupMemberRoleType.STUDENT));
+        }
         //创建融云群
         this.rtcCreate(courseGroup.getTeacherId(), imGroupId, imGroup.getName());
         //加入融云群
@@ -185,46 +334,130 @@ 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("创建群聊失败,请联系管理员");
+    private void rtcCreate(Long userId, String imGroupId, String imGroupName) throws Exception {
+        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)).
                 map(ImGroup::getCreateBy).
                 orElseThrow(() -> new BizException("操作失败:群组不存在"));
-        if(!sysUserService.getUserId().equals(createBy)){
+        if (!sysUserService.getUserId().equals(createBy)) {
             throw new BizException("操作失败:您没有操作权限");
         }
         //删除入群申请
         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
@@ -241,7 +474,7 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
     @Override
     public ImGroup getByCourseGroupId(Long courseGroupId) {
         return this.lambdaQuery()
-                .eq(ImGroup::getCourseGroupId,courseGroupId)
+                .eq(ImGroup::getCourseGroupId, courseGroupId)
                 .last("limit 1")
                 .one();
     }
@@ -265,20 +498,19 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
             imUserId = MessageFormat.format("{0}:{1}", imUserId, ClientEnum.STUDENT.name());
         }
 
-        if(imGroupMemberService.getDao().delByGroupIdAndUserId(groupId, userId, roleType) < 1){
+        if (imGroupMemberService.getDao().delByGroupIdAndUserId(groupId, userId, roleType) < 1) {
             throw new BizException("操作失败:用户不在此群组");
         }
         //修改群成员数
         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
@@ -292,10 +524,10 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
 
             // 已经有群的不操作
             Integer count = this.lambdaQuery()
-                                .eq(ImGroup::getCreateBy, teacher.getUserId())
-                                .eq(ImGroup::getAutoPassFlag, true)
-                                .eq(ImGroup::getType, ImGroupType.FAN)
-                                .count();
+                    .eq(ImGroup::getCreateBy, teacher.getUserId())
+                    .eq(ImGroup::getAutoPassFlag, true)
+                    .eq(ImGroup::getType, ImGroupType.FAN)
+                    .count();
             if (count > 0) {
                 continue;
             }
@@ -309,8 +541,8 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
 
             // 查询老师的粉丝,添加到群
             List<StudentStar> list = studentStarService.lambdaQuery()
-                                                       .eq(StudentStar::getTeacherId, teacher.getUserId())
-                                                       .list();
+                    .eq(StudentStar::getTeacherId, teacher.getUserId())
+                    .list();
             Set<Long> studentIdList = list.stream().map(StudentStar::getStudentId).collect(Collectors.toSet());
 
 
@@ -327,9 +559,11 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
             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);
+            List<ImGroupMember> groupMembers = imGroupMemberService.initGroupMember(imGroupId, imGroup.getCreateBy(),
+                    true, ImGroupMemberRoleType.TEACHER);
             if (!CollectionUtils.isEmpty(studentIdList)) {
-                groupMembers.addAll(imGroupMemberService.initGroupMembers(imGroupId, studentIdList, ImGroupMemberRoleType.STUDENT));
+                groupMembers.addAll(imGroupMemberService.initGroupMembers(imGroupId, studentIdList,
+                        ImGroupMemberRoleType.STUDENT));
             }
             //创建融云群
             this.rtcCreate(sysUser.getId(), imGroupId, imGroup.getName());
@@ -370,7 +604,8 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
 
                     if (Objects.nonNull(groupMember)) {
 
-                        String imIdentity = MessageFormat.format("{0}:{1}", String.valueOf(userId), ClientEnum.STUDENT.name());
+                        String imIdentity = MessageFormat.format("{0}:{1}", String.valueOf(userId),
+                                ClientEnum.STUDENT.name());
                         // 缓存用户重新入群标识
                         String userKey = MessageFormat.format("{0}:{1}", groupId, imIdentity);
                         RBucket<Object> bucket = redissonClient.getBucket(userKey);
@@ -379,7 +614,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)
@@ -388,11 +623,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天
@@ -407,5 +638,339 @@ public class ImGroupServiceImpl extends ServiceImpl<ImGroupDao, ImGroup> impleme
 
         return group;
     }
+
+    /**
+     * 同步即时通讯聊天记录
+     *
+     * @param date
+     * @return
+     */
+    @Override
+    public Result historyGet(String date) throws Exception {
+        return getHistory().get(date);
+
+    }
+
+    @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;
+            }
+            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;
+    }
+
+    /**
+     * IM导入消息
+     */
+    @Override
+    public void importInfo(List<ImHistoryMessage> info) throws Exception {
+
+        info.stream().forEach(i -> {
+            //判断消息类型
+            Integer type = i.getTargetType();
+            if (type == 1) {
+                //单聊会话
+                MessageWrapper.PrivateImportMessage privateImportMessage = new MessageWrapper.PrivateImportMessage();
+                //2:表示历史消息导入,消息不计入未读计数,且消息不会推送到终端
+                privateImportMessage.setSyncFromOldSystem(2);
+                //设置发送人
+                //根据发送者分场景讨论
+                String id = i.getFromUserId();
+                String imUserId;
+                if (id.contains(":student")){
+                    String[] split = id.split(":");
+                    String userId = split[0];
+                    String clientType = split[1];
+                    imUserId = getImUserId(userId,clientType);
+                } else {
+                    imUserId = getImUserId(id,"teacher");
+                }
+
+                privateImportMessage.setFromAccount(imUserId);
+                //设置接收人
+                privateImportMessage.setToAccount(i.getTargetId());
+                //设置随机数
+                privateImportMessage.setMsgRandom(new Random().nextInt());
+
+                //设置发送时间
+                String time = i.getDateTime();
+                SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                try {
+                    Date date = df.parse(time);
+                    long dateTime = date.getTime();
+                    dateTime = dateTime / (1000);
+                    privateImportMessage.setMsgTimeStamp(dateTime);
+                } catch (ParseException e) {
+                    e.printStackTrace();
+                }
+
+                //设置body
+                TencentRequest.MessageBody body = new TencentRequest.MessageBody();
+                List<TencentRequest.MessageBody> list = new ArrayList<>();
+
+                if (i.getClassname().equals("RC:TxtMsg")){
+                    TencentRequest.MessageBody body1 = CustomerServiceBatchSendingServiceImpl.getTimTextElem(i.getContent());
+                    list.add(body1);
+                } else if (i.getClassname().equals("RC:ImgMsg")){
+                    JSONObject jsonObject = JSONObject.parseObject(i.getContent());
+                    TencentRequest.MessageBody body1 = CustomerServiceBatchSendingServiceImpl.getTimImageElem(jsonObject.getJSONObject("user").getString("portraitUri"));
+                    list.add(body1);
+                }
+
+               /* body.setMsgType(i.getClassname());
+                body.setMsgContent(i.getContent());*/
+
+                /*list.add(body);*/
+               /* 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<>();
+                TencentRequest.MessageBody body = new TencentRequest.MessageBody();
+                //设置群组Id
+                groupImportMessage.setGroupId(i.getGroupId());
+                //设置发送人
+                String imUserId;
+                String id = i.getFromUserId();
+                //根据发送者分场景讨论
+                if (id.contains(":student")){
+                    String[] split = id.split(":");
+                    String userId = split[0];
+                    String clientType = split[1];
+                    imUserId = getImUserId(userId,clientType);
+                } else {
+                    imUserId = getImUserId(id,"teacher");
+                }
+
+
+                data1.setFromAccount(imUserId);
+                //设置随机数
+                data1.setRandom(new Random().nextInt());
+                //设置发送时间
+
+                String time = i.getDateTime();
+                SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+                try {
+                    Date date = df.parse(time);
+                    long dateTime = date.getTime();
+                    dateTime = dateTime / (1000);
+                    data1.setSendTime(dateTime);
+                } catch (ParseException e) {
+                    e.printStackTrace();
+                }
+                /*bodyList.stream().forEach(item->{
+                            item.setMsgContent(i.getContent());
+                            item.setMsgType(i.getClassname());
+                        });*/
+                if (i.getClassname().equals("RC:TxtMsg")){
+                    TencentRequest.MessageBody body1 = CustomerServiceBatchSendingServiceImpl.getTimTextElem(i.getContent());
+                    bodyList.add(body1);
+                } else if (i.getClassname().equals("RC:ImgMsg")){
+                    JSONObject jsonObject = JSONObject.parseObject(i.getContent());
+                    TencentRequest.MessageBody body1 = CustomerServiceBatchSendingServiceImpl.getTimImageElem(jsonObject.getJSONObject("user").getString("portraitUri"));
+                    bodyList.add(body1);
+                }
+                /*body.setMsgContent(i.getContent());
+                body.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();
+                }
+            }
+        });
+    }
+
+
+    private History getHistory() {
+        RongCloud rongCloud = RongCloud.getInstance(appKey, appSecret);
+        History history = new History(appKey, appSecret);
+        history.setRongCloud(rongCloud);
+        return history;
+    }
+
+
+    /**
+     * 群迁移
+     */
+    @Override
+    public void groupTransfer() {
+//        int page = 1;
+//        int size = 100;
+//        List<ImGroup> imGroups = this.lambdaQuery().orderByDesc(ImGroup::getCreateTime)
+//                .last("limit " + ((page - 1) * size) + "," + size).list();
+//        while (!imGroups.isEmpty()) {
+//            groupTransfer(imGroups);
+//
+//            page++;
+//            imGroups = this.lambdaQuery().orderByDesc(ImGroup::getCreateTime)
+//                    .last("limit " + ((page - 1) * size) + "," + size).list();
+//        }
+        ImGroup byId = this.getById("9f677fcd-9d80-4db0-8beb-0ff04e6a98f6FAN");
+        groupTransfer(Collections.singletonList(byId));
+    }
+
+    private void groupTransfer(List<ImGroup> records) {
+        for (ImGroup imGroup : records) {
+            List<ImGroupMember> memberList = imGroupMemberService.lambdaQuery()
+                    .eq(ImGroupMember::getGroupId, imGroup.getId())
+                    .list();
+            ImGroupMember admin = memberList.stream().filter(ImGroupMember::getIsAdmin).findFirst().orElse(null);
+            if (admin == null) {
+                continue;
+            }
+            try {
+                MessageWrapper.ImportGroup importGroup = new MessageWrapper.ImportGroup();
+                importGroup.setOwnerAccount(getImUserId(admin.getUserId().toString(), "TEACHER"));
+                importGroup.setType("Public");
+                importGroup.setGroupId(imGroup.getId());
+                importGroup.setName(imGroup.getName());
+                importGroup.setIntroduction(imGroup.getIntroduce());
+                importGroup.setNotification(imGroup.getMemo());
+                importGroup.setFaceUrl(imGroup.getImg());
+                if (StringUtils.isEmpty(imGroup.getImg())) {
+                    importGroup.setFaceUrl("https://gyt.ks3-cn-beijing.ksyuncs.com/example/group_default_avatar.png");
+                }
+                importGroup.setApplyJoinOption(Boolean.TRUE.equals(imGroup.getAutoPassFlag()) ? "FreeAccess" :
+                        "NeedPermission");
+                importGroup.setCreateTime(imGroup.getCreateTime().getTime() / 1000);
+
+                register(admin.getUserId(), imGroup.getId(), admin.getAvatar(), true, ClientEnum.TEACHER);
+                imPluginContext.getPluginService().importGroup(importGroup);
+
+                List<ImGroupMember> groupMembers = memberList.stream()
+                        .filter(next -> !next.getIsAdmin()).collect(Collectors.toList());
+                if (groupMembers.isEmpty()) {
+                    continue;
+                }
+                for (ImGroupMember member : groupMembers) {
+                    String avatar = member.getAvatar();
+                    if (StringUtils.isEmpty(avatar)) {
+                        avatar = "https://daya.ks3-cn-beijing.ksyun.com/202203/T1WgJaE.png";
+                    }
+                    register(member.getUserId(), imGroup.getId(), avatar, false,
+                            ImGroupMemberRoleType.STUDENT.equals(member.getRoleType()) ? ClientEnum.STUDENT :
+                                    ClientEnum.TEACHER);
+                }
+                MessageWrapper.ImportGroupMember importGroupMember = new MessageWrapper.ImportGroupMember();
+                importGroupMember.setGroupId(imGroup.getId());
+                List<MessageWrapper.ImportGroupMemberData> members =
+                        groupMembers.stream().map(next -> {
+                            MessageWrapper.ImportGroupMemberData data = new MessageWrapper.ImportGroupMemberData();
+                            data.setMemberAccount(getImUserId(next.getUserId().toString(),
+                                    ImGroupMemberRoleType.STUDENT.equals(next.getRoleType()) ?
+                                            ClientEnum.STUDENT.name() :
+                                            ClientEnum.TEACHER.name()));
+                            data.setJoinTime(next.getCreateTime().getTime() / 1000);
+                            data.setUnreadMsgNum(0);
+                            return data;
+                        }).collect(Collectors.toList());
+                List<List<MessageWrapper.ImportGroupMemberData>> partition = Lists.partition(members, 6);
+                for (List<MessageWrapper.ImportGroupMemberData> importGroupMemberData : partition) {
+                    importGroupMember.setMemberList(importGroupMemberData);
+                    imPluginContext.getPluginService().importGroupMember(importGroupMember);
+                }
+            } catch (Exception e) {
+                log.error(String.format("群迁移加入群聊失败:%s", e.getMessage()));
+            }
+        }
+    }
+
+    private void register(Long userId, String imGroupId, String avatar, Boolean admin, ClientEnum clientType) {
+        SysUser user = sysUserService.findUserById(userId);
+
+        // 群组默认头象
+        List<GroupMemberWrapper.ImGroupMember> groupMembers = Lists.newArrayList();
+        //记录群成员
+        GroupMemberWrapper.ImGroupMember groupMember = GroupMemberWrapper.ImGroupMember
+                .builder()
+                .id(IdWorker.getId())
+                .groupId(imGroupId)
+                .userId(userId)
+                .clientType(clientType.getCode())
+                .avatar(avatar)
+                .nickname(user.getUsername())
+                .isAdmin(admin)
+                .imUserId(getImUserId(userId.toString(), clientType.getCode()))
+                .roleType(clientType.getCode())
+                .build();
+        groupMembers.add(groupMember);
+
+        // 自动激活学生IM帐号
+        registerUser(groupMembers);
+    }
 }
 

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

@@ -1,21 +1,20 @@
 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.RoleEnum;
 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 +28,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;
@@ -36,7 +38,7 @@ public class ImNetworkRoomMemberServiceImpl extends ServiceImpl<ImNetworkRoomMem
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public ImNetworkRoomMember initRoomMember(String roomId, BasicUserInfo sysUser, UserRoleEnum userRole) {
+    public ImNetworkRoomMember initRoomMember(String roomId, BasicUserInfo sysUser, RoleEnum userRole) {
         ImNetworkRoomMember roomMember = baseMapper.findByRidAndUid(roomId, sysUser.getUserId(), userRole.ordinal());
         if(Objects.isNull(roomMember)){
             roomMember = new ImNetworkRoomMember();
@@ -55,11 +57,7 @@ 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());
-        }
-        roomMember.setImUserId(imUserId);
+        roomMember.setImUserId(imGroupService.getImUserId(sysUser.getUserId(),userRole));
 
         return roomMember;
     }

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

@@ -1,9 +1,19 @@
 package com.yonge.cooleshow.biz.dal.service.impl;
 
 import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.alibaba.fastjson.serializer.SerializerFeature;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.google.common.collect.Lists;
+import com.microsvc.toolkit.middleware.rtc.RTCRoomPluginContext;
+import com.microsvc.toolkit.middleware.rtc.RTCRoomPluginService;
+import com.microsvc.toolkit.middleware.rtc.enums.EMemberAction;
+import com.microsvc.toolkit.middleware.rtc.impl.TencentCloudRTCPlugin;
+import com.microsvc.toolkit.middleware.rtc.message.ImGroupMemberWrapper;
+import com.microsvc.toolkit.middleware.rtc.message.RTCRoomMessage;
 import com.yonge.cooleshow.auth.api.entity.SysUser;
 import com.yonge.cooleshow.biz.dal.dao.ImNetworkRoomDao;
+import com.yonge.cooleshow.biz.dal.dao.ImNetworkRoomMemberDao;
 import com.yonge.cooleshow.biz.dal.dao.SysConfigDao;
 import com.yonge.cooleshow.biz.dal.dao.TeacherDao;
 import com.yonge.cooleshow.biz.dal.dto.*;
@@ -11,6 +21,7 @@ import com.yonge.cooleshow.biz.dal.entity.*;
 import com.yonge.cooleshow.biz.dal.enums.*;
 import com.yonge.cooleshow.biz.dal.service.*;
 import com.yonge.cooleshow.biz.dal.support.IMHelper;
+import com.yonge.cooleshow.biz.dal.vo.TeacherVo;
 import com.yonge.cooleshow.common.constant.SysConfigConstant;
 import com.yonge.cooleshow.common.entity.HttpResponseResult;
 import com.yonge.cooleshow.common.enums.EStatus;
@@ -20,6 +31,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;
@@ -29,6 +41,7 @@ import javax.annotation.Resource;
 import java.text.MessageFormat;
 import java.util.*;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
 /**
@@ -64,9 +77,14 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
     private StudentAttendanceService studentAttendanceService;
     @Resource
     private TeacherAttendanceService teacherAttendanceService;
+    @Autowired
+    private RTCRoomPluginContext rtcRoomPluginContext;
     @Resource
     private RedisTemplate<String,Object> redisTemplate;
 
+    @Autowired
+    private ImGroupService imGroupService;
+
     private String QUIT_ROOM_SUCCESS = "quitRoomSuccess:";
 
     @Override
@@ -83,10 +101,10 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
         CourseSchedule courseSchedule = Optional.ofNullable(courseScheduleService.getById(courseScheduleId)).
                 orElseThrow(()->new BizException("房间信息不存在"));
 
-        UserRoleEnum userRole = UserRoleEnum.STUDENT;
+        RoleEnum userRole = RoleEnum.RoleStudent;
         if(courseSchedule.getTeacherId().equals(userId) && ClientEnum.TEACHER == clientType){
             // 与老师帐号匹配,且来自老师客户端
-            userRole = UserRoleEnum.TEACHER;
+            userRole = RoleEnum.RoleTeacher;
         }
         log.info("joinRoom params:courseScheduleId:{},userRole:{},userId:{}",courseScheduleId,userRole,userId);
         BasicUserInfo sysUser = Optional.ofNullable(teacherDao.getBasicUserInfo(userId)).
@@ -113,16 +131,15 @@ 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()));
             }
-
         }
         joinRoomResult.setRoomMemberList(roomMemberList);
         //如果是老师,重置节拍器数据
-        if(userRole == UserRoleEnum.TEACHER){
+        if(userRole == RoleEnum.RoleTeacher){
             courseScheduleStudentPaymentService.getDao().cleanPlayMidi(courseScheduleId);
             //获取所有学员的伴奏下载详情
             setMusicSheetList(roomMemberList,courseScheduleId);
@@ -134,16 +151,25 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
         joinRoomResult.setAutoCloseNetworkRoomTime(sysConfigDao.findConfigValue(SysConfigConstant.DESTROY_EXPIRED_PRACTICE_ROOM_MINUTE));
 
         // IM用户ID
-        String imUserId = sysUser.getUserId().toString();
-        if (UserRoleEnum.STUDENT == userRole) {
-            imUserId = MessageFormat.format("{0}:{1}", imUserId, ClientEnum.STUDENT.name());
-        }
+        String imUserId = imGroupService.getImUserId(sysUser.getUserId(),userRole);
         roomMember.setImUserId(imUserId);
+        // 创建IM群聊
+        this.joinImGroup(roomId, courseSchedule.getTeacherId().intValue(), courseSchedule);
+        RTCRoomPluginService pluginService = rtcRoomPluginContext.getPluginService(courseSchedule.getServiceProvider());
+        if (TencentCloudRTCPlugin.PLUGIN_NAME.equals(pluginService.pluginName())) {
+            // 腾讯云RTC
+            // 用户IM帐号创建
+            String userSig = "";
+            try {
+                userSig = pluginService.register(String.valueOf(sysUser.getUserId()), sysUser.getRealName(), sysUser.getAvatar());
+            } catch (Exception e) {
+                log.error("直播房间用户注册失败: userId={}", sysUser.getUserId(), e);
+            }
 
-        //创建、加入群聊
-        IMApiResultInfo resultInfo = imHelper.joinGroup(new String[]{imUserId}, roomId, roomId);
-        if(resultInfo.getCode() != 200){
-            log.error("加入群聊失败 resultInfo:{}",resultInfo);
+            // 返回配置参数
+            joinRoomResult.setUserSig(userSig);
+            joinRoomResult.setRtcRoomConfig(rtcRoomPluginContext.getPluginService().getRTCRoomConfig(String.valueOf(sysUser.getUserId())));
+            joinRoomResult.setGroupId(roomId);
         }
         return HttpResponseResult.succeed(joinRoomResult);
     }
@@ -169,7 +195,7 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
 
         String[] values = imUserId.split(":");
         // 用户ID
-        long userId = Long.parseLong(values[0]);
+        Long userId = Long.parseLong(values[0]);
 
         // 客户端类型
         ClientEnum clientType = ClientEnum.TEACHER;
@@ -184,11 +210,11 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
         BasicUserInfo sysUser = Optional.ofNullable(teacherDao.getBasicUserInfo(userId)).
                 orElseThrow(()-> new BizException("用户信息不存在"));
 
-        UserRoleEnum userRole = UserRoleEnum.STUDENT;
+        RoleEnum userRole = RoleEnum.RoleStudent;
         if(Objects.equals(courseSchedule.getTeacherId(),userId)
                 && ClientEnum.TEACHER == clientType){
 
-            userRole = UserRoleEnum.TEACHER;
+            userRole = RoleEnum.RoleTeacher;
             teacherAttendanceService.signIn(userId,courseSchedule);
         }else {
             studentAttendanceService.signIn(userId,courseSchedule);
@@ -201,11 +227,61 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
         // IM用户ID
         roomMember.setImUserId(imUserId);
         //发送人员变动消息
-        publishMemberChangedMessage(roomMember);
+        // 获取RTC服务提供方
+        String rtcServiceProvider = Optional.ofNullable(courseSchedule.getServiceProvider()).orElse("rongCloud");
+
+        RTCRoomPluginService pluginService = rtcRoomPluginContext.getPluginService(rtcServiceProvider);
+        if (rtcServiceProvider.equals(TencentCloudRTCPlugin.PLUGIN_NAME)) {
+            // 腾讯云RTC服务
+            RTCRoomMessage.MessageContent.MessageContentBuilder action = RTCRoomMessage.MessageContent.builder()
+                    .action(EMemberAction.JOIN.getValue());
+
+            if (userRole == RoleEnum.RoleTeacher) {
+                action.role(userRole.getValue());
+            } else {
+                action.role(userRole.getValue());
+            }
+            action.handUpOn(roomMember.isHandFlag())
+                    .microphone(roomMember.isMicFlag())
+                    .timestamp(now.getTime())
+                    .camera(true)
+                    .sendUserInfo(getSendUser(sysUserService.findUserById(userId),userRole));
+
+            // 开启全员静音,重置学员状态
+            if (courseSchedule.getMuteAll()) {
+                action.microphone(false);
+            }
+
+            RTCRoomMessage roomMessage = RTCRoomMessage.builder()
+                    .objectName(RTCRoomMessage.MEMBER_CHANGE_MESSAGE)
+                    .fromUserId(userId.toString())
+                    .content(action.build())
+                    .toChatRoomId(roomId)
+                    .isPersisted(1)
+                    .isIncludeSender(1)
+                    .build();
+
+            pluginService.sendChatRoomMessage(roomMessage);
+        } else {
+            publishMemberChangedMessage(roomMember);
+        }
         //sendDisplay
         this.sendDisplay(imUserId, room);
     }
 
+    private RTCRoomMessage.MessageUser getSendUser(SysUser sysUser,RoleEnum role) {
+
+        RTCRoomMessage.MessageUser build = RTCRoomMessage.MessageUser.builder()
+                .sendUserId(sysUser.getId().toString())
+                .sendUserName(sysUser.getUsername())
+                .avatarUrl(sysUser.getAvatar())
+                .build();
+        if (role == RoleEnum.RoleTeacher) {
+            build.setSendUserName(sysUser.getRealName());
+        }
+        return build;
+    }
+
     //发送人员变动消息
     private void publishMemberChangedMessage(ImNetworkRoomMember roomMember) throws Exception {
         ImNetworkRoomMemberChangedMessage msg = new ImNetworkRoomMemberChangedMessage(roomMember,ImNetworkRoomMemberChangedEnum.JOIN);
@@ -311,20 +387,65 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
         imNetworkRoomMemberService.getDao().deleteById(roomMember.getId());
     }
 
+
+    private String getImUserId(Long userId,ClientEnum clientEnum){
+        String imUserId = String.valueOf(userId);
+        if (ClientEnum.STUDENT == clientEnum) {
+            imUserId = MessageFormat.format("{0}:{1}", imUserId, ClientEnum.STUDENT.name());
+        }
+        return imUserId;
+    }
+
+    private String getImUserId(Long userId,RoleEnum clientEnum){
+        String imUserId = String.valueOf(userId);
+        if (RoleEnum.RoleStudent == clientEnum) {
+            imUserId = MessageFormat.format("{0}:{1}", imUserId, ClientEnum.STUDENT.name());
+        }
+        return imUserId;
+    }
+
+
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void sendImPlayMidiMessage(ImNetworkCustomMessage customMessage) throws Exception {
-        Long userId = sysUserService.getUserId();
+        SysUser user = sysUserService.getUser();
+        Long userId = user.getId();
         Long roomId = customMessage.getRoomId();
         log.info("sendImPlayMidiMessage: roomId={}, userId={}", roomId, userId);
         ImNetworkMetronomeMessage displayMessage = new ImNetworkMetronomeMessage(customMessage);
 
-        String imUserId = String.valueOf(userId);
-        if (ClientEnum.STUDENT == customMessage.getClientType()) {
-            imUserId = MessageFormat.format("{0}:{1}", imUserId, ClientEnum.STUDENT.name());
+        String imUserId = imGroupService.getImUserId(userId,customMessage.getClientType());
+        ImNetworkRoomMember roomMember = imNetworkRoomMemberService.lambdaQuery().eq(ImNetworkRoomMember::getUserId, userId)
+                .eq(ImNetworkRoomMember::getRoomId, roomId).last("LIMIT 1").one();
+        // 获取RTC服务提供方
+        CourseSchedule courseSchedule = courseScheduleService.getById(roomId);
+        RTCRoomPluginService pluginService = rtcRoomPluginContext.getPluginService(courseSchedule.getServiceProvider());
+        if (TencentCloudRTCPlugin.PLUGIN_NAME.equals(pluginService.pluginName())) {
+
+            // 腾讯云RTC服务
+            RTCRoomMessage.MessageContent messageContent = RTCRoomMessage.MessageContent
+                    .builder()
+                    .enable(customMessage.getEnable())
+                    .rate(customMessage.getRate())
+                    .customType(customMessage.getCustomType())
+                    .userId(customMessage.getUserId())
+                    .playVolume(customMessage.getPlayVolume())
+                    .sendUserInfo(getSendUser(user, RoleEnum.getEnumByValue(roomMember.getRole())))
+                    .build();
+
+            RTCRoomMessage roomMessage = RTCRoomMessage.builder()
+                    .objectName(RTCRoomMessage.PLAY_MIDI_MESSAGE)
+                    .content(messageContent)
+                    .toChatRoomId(roomMember.getRoomId())
+                    .fromUserId(roomMember.getImUserId())
+                    .isIncludeSender(1)
+                    .isPersisted(1)
+                    .build();
+
+            pluginService.sendChatRoomMessage(roomMessage);
+        } else {
+            imHelper.publishMessage(imUserId, roomId.toString(), displayMessage, 1);
         }
-        // 用户ID
-        imHelper.publishMessage(imUserId, roomId.toString(), displayMessage, 1);
 
         //记录节拍器信息
         String collect = Arrays.stream(customMessage.getUserId().split(","))
@@ -339,7 +460,7 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
         Long courseScheduleId = Optional.ofNullable(musicSheetDto).map(ImNetworkBaseDto::getRoomId).orElseThrow(() -> new BizException("房间编号不能为空"));
         Long accompanimentId = Optional.ofNullable(musicSheetDto).map(ImNetworkMusicSheetDto::getAccompanimentId).orElseThrow(() -> new BizException("伴奏编号不能为空"));
         log.info("pushDownloadMusicSheetMsg: courseScheduleId:{} ,accompanimentId:{} ,deviceType:{}", courseScheduleId,accompanimentId);
-        Long userId = sysUserService.getUserId();
+        SysUser user = sysUserService.getUser();
         List<CourseScheduleStudentMusicSheetResult> scheduleStudentMusicSheetResults = courseScheduleStudentMusicSheetService.getDao().
                 queryBySheetIdAndCourseId(accompanimentId, courseScheduleId, null, null, 0);
 
@@ -360,23 +481,53 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
             musicSheet.setMusicSheetAccompanimentId(accompanimentId);
             musicSheet.setSpeed(accompaniment.getSpeed());
             musicSheet.setCourseScheduleId(courseScheduleId);
-            musicSheet.setUserId(userId);
+            musicSheet.setUserId(user.getId());
             musicSheet.setUserType(1);
             scheduleStudentMusicSheetResults.add(musicSheet);
             courseScheduleStudentMusicSheetService.getDao().batchInsert(scheduleStudentMusicSheetResults);
         }
-        ImNetworkMusicSheetDownloadMessage msg = new ImNetworkMusicSheetDownloadMessage(
-                JSON.parseObject(JSON.toJSONString(accompaniment), ImNetworkMusicSheetDownloadMessageContent.class));
-
         // IM用户ID
-        String imUserId = String.valueOf(userId);
-        if (ClientEnum.STUDENT == musicSheetDto.getClientType()) {
-            imUserId = MessageFormat.format("{0}:{1}", imUserId, ClientEnum.STUDENT.name());
-        }
+        String imUserId = imGroupService.getImUserId(user.getId(),musicSheetDto.getClientType());
+        ImNetworkMusicSheetDownloadMessageContent content = JSON.parseObject(JSON.toJSONString(accompaniment), ImNetworkMusicSheetDownloadMessageContent.class);
+        // 发送消息
+        CourseSchedule courseSchedule = courseScheduleService.getById(courseScheduleId);
+        if (TencentCloudRTCPlugin.PLUGIN_NAME.equals(courseSchedule.getServiceProvider())) {
+
+            RTCRoomPluginService pluginService = rtcRoomPluginContext.getPluginService(courseSchedule.getServiceProvider());
+            // 腾讯消息推送
+            RTCRoomMessage.MessageContent messageContent = RTCRoomMessage.MessageContent
+                    .builder()
+                    .url(content.getUrl())
+                    .mp3Url(content.getMp3Url())
+                    .songId(String.valueOf(content.getId()))
+                    .speed(content.getSpeed())
+                    .sendUserInfo(RTCRoomMessage.MessageUser.builder()
+                            .sendUserId(imUserId)
+                            .sendUserName(user.getUsername())
+                            .avatarUrl(user.getAvatar())
+                            .build())
+                    .build();
+
+            RTCRoomMessage roomMessage = RTCRoomMessage.builder()
+                    .objectName(RTCRoomMessage.MUSIC_SCORE_MESSAGE)
+                    .content(messageContent)
+                    .toChatRoomId(courseScheduleId.toString())
+                    .fromUserId(imUserId)
+                    .isIncludeSender(1)
+                    .isPersisted(1)
+                    .build();
+
+            // 发送消息
+            pluginService.sendChatRoomMessage(roomMessage);
 
-        imHelper.publishMessage(imUserId, courseScheduleId.toString(), msg, 0);
+        } else {
+            ImNetworkMusicSheetDownloadMessage msg = new ImNetworkMusicSheetDownloadMessage(content);
+            // 融云消息推送
+            imHelper.publishMessage(imUserId, courseScheduleId.toString(), msg, 0);
+        }
     }
 
+
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void display(ImNetworkDisplayDataDto displayData) throws Exception {
@@ -408,10 +559,7 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
         ImNetworkRoom room = baseMapper.findByRoomId(displayData.getRoomId());
         room.setDisplay(display.toString());
 
-        String imUserId = String.valueOf(userId);
-        if (ClientEnum.STUDENT == displayData.getClientType()) {
-            imUserId = MessageFormat.format("{0}:{1}", imUserId, ClientEnum.STUDENT.name());
-        }
+        String imUserId = imGroupService.getImUserId(userId,displayData.getClientType());
 
         this.updateDisplay(imUserId,room);
     }
@@ -465,6 +613,40 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
         }
         // IM用户ID
         String imUserId = deviceControl.getUserId();
+        // RTC服务对象
+        CourseSchedule courseSchedule = courseScheduleService.getById(roomId);
+        SysUser teacher = sysUserService.findUserById(courseSchedule.getTeacherId());
+        if (Objects.nonNull(sysUser)) {
+            deviceControl.setSendUserId(teacher.getId().toString());
+            deviceControl.setSendUserName(teacher.getRealName());
+            deviceControl.setAvatar(teacher.getAvatar());
+        }
+        RTCRoomPluginService pluginService = rtcRoomPluginContext.getPluginService(courseSchedule.getServiceProvider());
+        // 通知消息
+        RTCRoomMessage.MessageContent notifyContent = RTCRoomMessage.MessageContent
+                .builder()
+                .type(deviceControl.getDeviceType().ordinal())
+                .enable(enable)
+                .targetId(userId.toString())
+                .targetName(sysUser.getUsername())
+                .songId(Optional.ofNullable(deviceControl.getMusicSheetAccompanimentId()).map(String::valueOf).orElse(null))
+                .songVolume(deviceControl.getSoundVolume())
+                .sendUserInfo(RTCRoomMessage.MessageUser.builder()
+                        .sendUserId(deviceControl.getSendUserId())
+                        .sendUserName(deviceControl.getSendUserName())
+                        .avatarUrl(deviceControl.getAvatar())
+                        .build())
+                .build();
+
+        // 腾讯云消息推送
+        RTCRoomMessage message = RTCRoomMessage.builder()
+                .objectName(RTCRoomMessage.CONTROL_DEVICE_NOTIFY_MESSAGE)
+                .fromUserId(userId.toString())
+                .toChatRoomId(roomId)
+                .content(notifyContent)
+                .isPersisted(1)
+                .isIncludeSender(0)
+                .build();
 
         if(enable){
             long scheduleId = Long.parseLong(roomId);
@@ -475,7 +657,15 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
                     ImNetworkRoomMusicSheetDownloadData msg = courseScheduleStudentPaymentService.getMemberExamSong(scheduleId, userId);
                     msg.setEnable(enable);
                     courseScheduleStudentPaymentService.getDao().adjustExamSong(scheduleId,userId, JSON.toJSONString(msg));
-                    imHelper.publishMessage(imUserId, roomId, deviceResourceMessage, 1);
+
+                    // 消息发送
+                    if (TencentCloudRTCPlugin.PLUGIN_NAME.equals(pluginService.pluginName())) {
+                        // 腾讯云推送
+                        pluginService.sendChatRoomMessage(message.objectName(RTCRoomMessage.CONTROL_DEVICE_NOTIFY_MESSAGE).content(notifyContent));
+                    } else {
+                        // 融云推送
+                        imHelper.publishMessage(imUserId, roomId, deviceResourceMessage, 1);
+                    }
                     break;
                 case MUSIC_SHEET:
                     Integer musicSheetId = Optional.ofNullable(deviceControl.getMusicSheetAccompanimentId()).
@@ -487,7 +677,13 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
                     deviceResourceMessage.setMusicSheetAccompanimentId(musicSheetId);
                     deviceResourceMessage.setUserId(imUserId);
                     deviceResourceMessage.setSoundVolume(deviceControl.getSoundVolume());
-                    imHelper.publishMessage(sysUser.getId().toString(), roomId, deviceResourceMessage, 1);
+                    if (TencentCloudRTCPlugin.PLUGIN_NAME.equals(pluginService.pluginName())) {
+                        // 腾讯云推送
+                        pluginService.sendChatRoomMessage(message.objectName(RTCRoomMessage.CONTROL_DEVICE_NOTIFY_MESSAGE).content(notifyContent));
+                    } else {
+                        // 融云推送
+                        imHelper.publishMessage(sysUser.getId().toString(), roomId, deviceResourceMessage, 1);
+                    }
                     break;
                 case ACCOMPANIMENT:
                   Integer musicSheetAccompanimentId = Optional.ofNullable(deviceControl.getMusicSheetAccompanimentId()).
@@ -499,15 +695,28 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
                     deviceResourceMessage.setMusicSheetAccompanimentId(musicSheetAccompanimentId);
                     deviceResourceMessage.setUserId(imUserId);
                     deviceResourceMessage.setSoundVolume(deviceControl.getSoundVolume());
-                    imHelper.publishMessage(sysUser.getId().toString(), roomId, deviceResourceMessage, 1);
+                    if (TencentCloudRTCPlugin.PLUGIN_NAME.equals(pluginService.pluginName())) {
+                        // 腾讯云推送
+                        pluginService.sendChatRoomMessage(message.objectName(RTCRoomMessage.CONTROL_DEVICE_NOTIFY_MESSAGE).content(notifyContent));
+                    } else {
+                        // 融云推送
+                        imHelper.publishMessage(sysUser.getId().toString(), roomId, deviceResourceMessage, 1);
+                    }
                     break;
                 default:
-                    //邀请打开指定设备权限
-                    ImNetworkControlDeviceNotifyMessage message = new ImNetworkControlDeviceNotifyMessage(ImNetworkActionEnum.INVITE.ordinal());
-                    message.setType(deviceControl.getDeviceType().ordinal());
-                    message.setOpUserId(sysUser.getId().toString());
-                    message.setOpUserName(sysUser.getUsername());
-                    imHelper.publishMessage(sysUser.getId().toString(), imUserId, roomId, message);
+                    // 发送消息
+                    if (TencentCloudRTCPlugin.PLUGIN_NAME.equals(pluginService.pluginName())) {
+                        // 腾讯消息
+                        pluginService.sendChatRoomMessage(message.objectName(RTCRoomMessage.CONTROL_DEVICE_NOTIFY_MESSAGE).content(notifyContent));
+                    } else {
+                        // 融云消息
+                        //邀请打开指定设备权限
+                        ImNetworkControlDeviceNotifyMessage message1 = new ImNetworkControlDeviceNotifyMessage(ImNetworkActionEnum.INVITE.ordinal());
+                        message1.setType(deviceControl.getDeviceType().ordinal());
+                        message1.setOpUserId(sysUser.getId().toString());
+                        message1.setOpUserName(sysUser.getUsername());
+                        imHelper.publishMessage(sysUser.getId().toString(), imUserId, roomId, message1);
+                    }
                     break;
             }
         }else {
@@ -543,13 +752,19 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
                     deviceControl.getDeviceType() != ImNetworkDeviceTypeEnum.ACCOMPANIMENT){
                 imNetworkRoomMemberService.getDao().updateById(roomMember);
             }
-            ImNetworkDeviceStateChangedMessage deviceResourceMessage = new ImNetworkDeviceStateChangedMessage(deviceControl.getDeviceType().ordinal(),deviceControl.getEnable());
-            deviceResourceMessage.setUserId(imUserId);
-            BasicUserInfo basicUserInfo = teacherDao.getBasicUserInfo(userId);
-            if (Objects.nonNull(basicUserInfo)) {
-                deviceResourceMessage.setUserName(basicUserInfo.getUsername());
+            if (TencentCloudRTCPlugin.PLUGIN_NAME.equals(pluginService.pluginName())) {
+                // 腾讯消息
+                pluginService.sendChatRoomMessage(message.objectName(RTCRoomMessage.CONTROL_DEVICE_NOTIFY_MESSAGE).content(notifyContent));
+            } else {
+                // 融云消息
+                ImNetworkDeviceStateChangedMessage deviceResourceMessage = new ImNetworkDeviceStateChangedMessage(deviceControl.getDeviceType().ordinal(),deviceControl.getEnable());
+                deviceResourceMessage.setUserId(imUserId);
+                BasicUserInfo basicUserInfo = teacherDao.getBasicUserInfo(userId);
+                if (Objects.nonNull(basicUserInfo)) {
+                    deviceResourceMessage.setUserName(basicUserInfo.getUsername());
+                }
+                imHelper.publishMessage(sysUser.getId().toString(), roomId, deviceResourceMessage, 1);
             }
-            imHelper.publishMessage(sysUser.getId().toString(), roomId, deviceResourceMessage, 1);
         }
     }
 
@@ -559,13 +774,11 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
         log.info("approveControlDevice: roomId:{} ,deviceType:{} ,enable:{}", deviceControl.getRoomId(),deviceControl.getDeviceType(),deviceControl.getEnable());
         SysUser sysUser = sysUserService.getUser();
 
-        String imUserId = String.valueOf(sysUser.getId());
         UserRoleEnum userRole = UserRoleEnum.TEACHER;
         if (ClientEnum.STUDENT == deviceControl.getClientType()) {
-            imUserId = MessageFormat.format("{0}:{1}", imUserId, ClientEnum.STUDENT.name());
-
             userRole = UserRoleEnum.STUDENT;
         }
+        String imUserId = imGroupService.getImUserId(sysUser.getId(),deviceControl.getClientType());
 
         ImNetworkRoomMember roomMember = Optional.ofNullable(imNetworkRoomMemberService.getDao().findByRidAndUid(deviceControl.getRoomId(), sysUser.getId(),
                         userRole.ordinal())).
@@ -588,33 +801,73 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
                 deviceControl.getDeviceType() == ImNetworkDeviceTypeEnum.MICROPHONE){
             imNetworkRoomMemberService.getDao().updateById(roomMember);
         }
-        ImNetworkControlDeviceNotifyMessage msg = new ImNetworkControlDeviceNotifyMessage(ImNetworkActionEnum.APPROVE.ordinal());
-        msg.setType(deviceControl.getDeviceType().ordinal());
-        msg.setOpUserId(sysUser.getId().toString());
-        msg.setOpUserName(sysUser.getUsername());
-        //获取老师编号
-        Long teacherId = Optional.ofNullable(courseScheduleService.getById(deviceControl.getRoomId())).
-                map(CourseSchedule::getTeacherId).
-                orElseThrow(()->new BizException("房间信息不存在"));
-        imHelper.publishMessage(imUserId, teacherId.toString(), deviceControl.getRoomId(), msg);
 
-        //发送设备状态变更消息
-        this.sendDeviceStateChangedMessage(deviceControl, imUserId);
+        CourseSchedule courseSchedule = courseScheduleService.getById(deviceControl.getRoomId());
+        if (TencentCloudRTCPlugin.PLUGIN_NAME.equals(courseSchedule.getServiceProvider())) {
+
+            // 获取RTC服务提供方
+            RTCRoomPluginService pluginService = rtcRoomPluginContext.getPluginService(TencentCloudRTCPlugin.PLUGIN_NAME);
+            // 腾讯云RTC服务
+            RTCRoomMessage.MessageContent.MessageContentBuilder action = RTCRoomMessage.MessageContent.builder()
+                    .type(ImNetworkActionEnum.APPROVE.ordinal())
+                    .enable(deviceControl.getEnable())
+                    .targetId(imUserId)
+                    .targetName(sysUser.getUsername())
+                    .sendUserInfo(getSendUser(sysUserService.findUserById(courseSchedule.getTeacherId()), RoleEnum.RoleTeacher));
+
+
+            RTCRoomMessage roomMessage = RTCRoomMessage.builder()
+                    .objectName(RTCRoomMessage.CONTROL_DEVICE_NOTIFY_MESSAGE)
+                    .content(action.build())
+                    .toChatRoomId(roomMember.getRoomId())
+                    .fromUserId(imUserId)
+                    .isIncludeSender(1)
+                    .isPersisted(1)
+                    .build();
+
+            pluginService.sendChatRoomMessage(roomMessage);
+
+            // 腾讯云RTC服务
+            action = RTCRoomMessage.MessageContent.builder()
+                    .type(ImNetworkActionEnum.APPROVE.ordinal())
+                    .enable(deviceControl.getEnable())
+                    .sendUserInfo(getSendUser(sysUser,RoleEnum.getEnumByValue(roomMember.getRole())));
+
+            // 腾讯云消息推送
+            RTCRoomMessage message = RTCRoomMessage.builder()
+                    .objectName(RTCRoomMessage.DEVICE_MESSAGE)
+                    .fromUserId(imUserId)
+                    .toChatRoomId(roomMember.getRoomId())
+                    .content(action.build())
+                    .isPersisted(1)
+                    .isIncludeSender(0)
+                    .build();
+
+            pluginService.sendChatRoomMessage(message);
+        }else {
+            ImNetworkControlDeviceNotifyMessage msg = new ImNetworkControlDeviceNotifyMessage(ImNetworkActionEnum.APPROVE.ordinal());
+            msg.setType(deviceControl.getDeviceType().ordinal());
+            msg.setOpUserId(sysUser.getId().toString());
+            msg.setOpUserName(sysUser.getUsername());
+            //获取老师编号
+            imHelper.publishMessage(imUserId, courseSchedule.getTeacherId().toString(), deviceControl.getRoomId(), msg);
+            //发送设备状态变更消息
+            this.sendDeviceStateChangedMessage(deviceControl, imUserId);
+        }
     }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void deviceStatusSync(ImNetworkDeviceControlDto deviceStatusSync) throws Exception {
         log.info("deviceStatusSync: enable:{} ,roomId:{} ,deviceType:{}", deviceStatusSync.getEnable(),deviceStatusSync.getRoomId(),deviceStatusSync.getDeviceType());
-        Long userId = sysUserService.getUserId();
+        SysUser user = sysUserService.getUser();
+        Long userId = user.getId();
 
         UserRoleEnum userRole = UserRoleEnum.TEACHER;
-        String imUserId = String.valueOf(userId);
         if (ClientEnum.STUDENT == deviceStatusSync.getClientType()) {
-            imUserId = MessageFormat.format("{0}:{1}", imUserId, ClientEnum.STUDENT.name());
-
             userRole = UserRoleEnum.STUDENT;
         }
+        String imUserId = imGroupService.getImUserId(userId,deviceStatusSync.getClientType());
 
         ImNetworkDeviceTypeEnum deviceType = deviceStatusSync.getDeviceType();
         ImNetworkRoomMember roomMember = Optional.ofNullable(imNetworkRoomMemberService.getDao().
@@ -646,8 +899,33 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
                 deviceType == ImNetworkDeviceTypeEnum.MUSIC_MODE){
             imNetworkRoomMemberService.getDao().updateById(roomMember);
         }
+        CourseSchedule courseSchedule = courseScheduleService.getById(deviceStatusSync.getRoomId());
         //发送设备状态同步消息
-        this.sendDeviceStateChangedMessage(deviceStatusSync, imUserId);
+        if (TencentCloudRTCPlugin.PLUGIN_NAME.matches(courseSchedule.getServiceProvider())) {
+            // RTC服务对象
+            RTCRoomPluginService pluginService = rtcRoomPluginContext.getPluginService(courseSchedule.getServiceProvider());
+            // 消息内容
+            RTCRoomMessage.MessageContent messageContent = RTCRoomMessage.MessageContent
+                    .builder()
+                    .type(deviceType.ordinal())
+                    .enable(deviceStatusSync.getEnable())
+                    .sendUserInfo(getSendUser(user, RoleEnum.RoleTeacher))
+                    .build();
+            // 腾讯云消息推送
+            RTCRoomMessage message = RTCRoomMessage.builder()
+                    .objectName(RTCRoomMessage.DEVICE_MESSAGE)
+                    .fromUserId(imUserId)
+                    .toChatRoomId(deviceStatusSync.getRoomId())
+                    .content(messageContent)
+                    .isPersisted(1)
+                    .isIncludeSender(0)
+                    .build();
+            // 发送消息
+            pluginService.sendChatRoomMessage(message);
+        } else {
+            // 融云消息推送
+            this.sendDeviceStateChangedMessage(deviceStatusSync, imUserId);
+        }
     }
 
     @Override
@@ -656,9 +934,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;
@@ -678,15 +954,42 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
     public void rejectControlDevice(ImNetworkDeviceControlDto deviceControl) throws Exception {
         log.info("rejectControlDevice: roomId:{} ,deviceType:{}", deviceControl.getRoomId(),deviceControl.getDeviceType());
         SysUser sysUser = sysUserService.getUser();
-        ImNetworkControlDeviceNotifyMessage msg = new ImNetworkControlDeviceNotifyMessage(ImNetworkActionEnum.REJECT.ordinal());
-        msg.setType(deviceControl.getDeviceType().ordinal());
-        msg.setOpUserId(sysUser.getId().toString());
-        msg.setOpUserName(sysUser.getUsername());
-        //获取老师编号
-        Long teacherId = Optional.ofNullable(courseScheduleService.getById(deviceControl.getRoomId())).
-                map(CourseSchedule::getTeacherId).
-                orElseThrow(()->new BizException("房间信息不存在"));
-        imHelper.publishMessage(sysUser.getId().toString(),teacherId.toString(), deviceControl.getRoomId(), msg);
+
+        CourseSchedule courseSchedule = courseScheduleService.getById(deviceControl.getRoomId());
+        if (TencentCloudRTCPlugin.PLUGIN_NAME.equals(courseSchedule.getServiceProvider())) {
+
+            // 获取RTC服务提供方
+            RTCRoomPluginService pluginService = rtcRoomPluginContext.getPluginService(TencentCloudRTCPlugin.PLUGIN_NAME);
+
+            RTCRoomMessage.MessageContent.MessageContentBuilder action = RTCRoomMessage.MessageContent.builder()
+                    .type(ImNetworkActionEnum.REJECT.ordinal())
+                    .enable(deviceControl.getEnable())
+                    .targetId(sysUser.getId().toString())
+                    .targetName(sysUser.getUsername())
+                    .sendUserInfo(getSendUser(sysUser, RoleEnum.RoleTeacher));
+
+
+            RTCRoomMessage roomMessage = RTCRoomMessage.builder()
+                    .objectName(RTCRoomMessage.CONTROL_DEVICE_NOTIFY_MESSAGE)
+                    .content(action.build())
+                    .toChatRoomId(deviceControl.getRoomId())
+                    .fromUserId(sysUser.getId().toString())
+                    .isIncludeSender(1)
+                    .isPersisted(1)
+                    .build();
+
+            pluginService.sendChatRoomMessage(roomMessage);
+        } else {
+            ImNetworkControlDeviceNotifyMessage msg = new ImNetworkControlDeviceNotifyMessage(ImNetworkActionEnum.REJECT.ordinal());
+            msg.setType(deviceControl.getDeviceType().ordinal());
+            msg.setOpUserId(sysUser.getId().toString());
+            msg.setOpUserName(sysUser.getUsername());
+            //获取老师编号
+            Long teacherId = Optional.ofNullable(courseScheduleService.getById(deviceControl.getRoomId())).
+                    map(CourseSchedule::getTeacherId).
+                    orElseThrow(()->new BizException("房间信息不存在"));
+            imHelper.publishMessage(sysUser.getId().toString(),teacherId.toString(), deviceControl.getRoomId(), msg);
+        }
     }
 
     @Override
@@ -712,10 +1015,7 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
         ImNetworkMusicSheetDownloadStatusMessage statusMessage = new ImNetworkMusicSheetDownloadStatusMessage(status,studentMusicSheetResults);
 
         // IM用户ID
-        String imUserId = String.valueOf(userId);
-        if (ClientEnum.STUDENT == musicSheetDto.getClientType()) {
-            imUserId = MessageFormat.format("{0}:{1}", imUserId, ClientEnum.STUDENT.name());
-        }
+        String imUserId = imGroupService.getImUserId(userId,musicSheetDto.getClientType());
 
         imHelper.publishMessage(imUserId, courseSchedule.getTeacherId().toString(), roomId.toString(), statusMessage);
     }
@@ -731,10 +1031,10 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
         return courseSchedule.getStartTime().compareTo(addMinutes) > 0;
     }
 
-    public ImNetworkRoom initRoom(String roomId,Long courseId,UserRoleEnum userRole,Long userId,Date now) throws Exception {
+    public ImNetworkRoom initRoom(String roomId,Long courseId,RoleEnum userRole,Long userId,Date now) throws Exception {
         ImNetworkRoom room = baseMapper.findByRoomId(roomId);
         String display = "";
-        if (userRole == UserRoleEnum.TEACHER) {
+        if (userRole == RoleEnum.RoleTeacher) {
             display = "display://type=0?userId=" + userId + "?uri=";
         }
         if(Objects.isNull(room)){
@@ -751,7 +1051,7 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
                 log.error("创建群聊失败 resultInfo:{}",resultInfo);
             }
         }else {
-            if(userRole == UserRoleEnum.TEACHER){
+            if(userRole == RoleEnum.RoleTeacher){
                 room.setDisplay(display);
                 baseMapper.updateById(room);
             }
@@ -762,9 +1062,36 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
     //修改节拍器
     public void updateDisplay(String imUserId,ImNetworkRoom room) throws Exception {
         baseMapper.updateById(room);
-
         // IM发送用户消息
-        this.sendDisplay(imUserId,room);
+        ImNetworkRoomMember roomMember = imNetworkRoomMemberService.lambdaQuery().eq(ImNetworkRoomMember::getUserId, imUserId)
+                .eq(ImNetworkRoomMember::getRoomId, room.getRoomId()).last("LIMIT 1").one();
+        CourseSchedule courseSchedule = courseScheduleService.getById(room.getRoomId());
+        if (TencentCloudRTCPlugin.PLUGIN_NAME.equals(courseSchedule.getServiceProvider())) {
+            sendDisplayMessage(room.getDisplay(), roomMember);
+        } else {
+            this.sendDisplay(imUserId,room);
+        }
+    }
+
+    private void sendDisplayMessage(String display, ImNetworkRoomMember roomMember) throws Exception {
+        // 获取RTC服务提供方
+        RTCRoomPluginService pluginService = rtcRoomPluginContext.getPluginService(TencentCloudRTCPlugin.PLUGIN_NAME);
+        // 腾讯云RTC服务
+        RTCRoomMessage.MessageContent.MessageContentBuilder action = RTCRoomMessage.MessageContent.builder()
+                .display(display)
+                .sendUserInfo(getSendUser(sysUserService.findUserById(roomMember.getUserId()),RoleEnum.getEnumByValue(roomMember.getRole())));
+
+
+        RTCRoomMessage roomMessage = RTCRoomMessage.builder()
+                .objectName(RTCRoomMessage.DISPLAY_MESSAGE)
+                .content(action.build())
+                .toChatRoomId(roomMember.getRoomId())
+                .fromUserId(roomMember.getUserId().toString())
+                .isIncludeSender(1)
+                .isPersisted(1)
+                .build();
+
+        pluginService.sendChatRoomMessage(roomMessage);
     }
 
     public void sendDisplay(String imUserId,ImNetworkRoom room) throws Exception {
@@ -783,4 +1110,88 @@ public class ImNetworkRoomServiceImpl extends ServiceImpl<ImNetworkRoomDao, ImNe
         }
         return result;
     }
+
+    private void joinImGroup(String roomId, Integer actualTeacherId, CourseSchedule courseSchedule) throws Exception {
+
+        String joinImGroupKey = "joinImGroup:" + roomId;
+        // VIP课或网络课,IM群聊已创建标识
+        Boolean exists = redisTemplate.opsForValue().setIfAbsent(joinImGroupKey, roomId, 1L, TimeUnit.DAYS);
+
+        RTCRoomPluginService pluginService = rtcRoomPluginContext.getPluginService(courseSchedule.getServiceProvider());
+        if (Optional.ofNullable(exists).orElse(false)) {
+
+            try {
+                // 创建群组
+                log.info("createImGroup: roomId = {}, userId = {}", roomId, actualTeacherId);
+                if (TencentCloudRTCPlugin.PLUGIN_NAME.equals(pluginService.pluginName())) {
+
+                    // 群组帐号注册
+                    TeacherVo teacherVo = teacherDao.detail(courseSchedule.getTeacherId());
+                    if (Objects.nonNull(teacherVo)) {
+                        try {
+                            pluginService.register(courseSchedule.getTeacherId().toString(), teacherVo.getRealName(), teacherVo.getAvatar());
+                        } catch (Exception e) {
+                            log.error("直播房间群主注册失败: userId={}", courseSchedule.getTeacherId(), e);
+                        }
+                    }
+
+                    // 生成群组
+                    pluginService.chatRoomCreate(roomId, roomId, courseSchedule.getTeacherId().toString());
+
+                    // 群组老师信息
+                    List<ImGroupMemberWrapper.ImGroupMember> groupMembers = Lists.newArrayList(ImGroupMemberWrapper.ImGroupMember
+                            .builder()
+                            .userId(Long.valueOf(actualTeacherId))
+                            .imUserIdFormat(false)
+                            .build());
+
+                    List<CourseScheduleStudentPayment> payments = courseScheduleStudentPaymentService.getByCourseId(Long.parseLong(roomId.substring(1)));
+                    // 群组学生信息
+                    if (org.apache.commons.collections.CollectionUtils.isNotEmpty(payments)) {
+                        for (CourseScheduleStudentPayment item : payments) {
+                            groupMembers.add(ImGroupMemberWrapper.ImGroupMember
+                                    .builder()
+                                    .userId(Long.valueOf(item.getUserId()))
+                                    .imUserIdFormat(false)
+                                    .build());
+                        }
+                    }
+
+                    // 加入群组成员
+                    log.info("joinImGroup: roomId = {}, serviceProvider={}, userIds = {}", roomId, courseSchedule.getServiceProvider(), groupMembers);
+                    pluginService.chatRoomGroupJoin(roomId, roomId, groupMembers);
+                } else {
+
+                    List<CourseScheduleStudentPayment> payments = courseScheduleStudentPaymentService.getByCourseId(Long.parseLong(roomId.substring(1)));
+                    List<String> collect = payments.stream().map(e -> e.getUserId().toString()).collect(Collectors.toList());
+                    collect.add(actualTeacherId.toString());
+                    String[] integers = collect.toArray(new String[]{});
+                    imHelper.joinGroup(integers, roomId, roomId);
+                }
+
+            } catch (Exception e) {
+
+                redisTemplate.delete(joinImGroupKey);
+                // 异常抛出,删除缓存标识
+                throw e;
+            }
+
+        }
+    }
+
+    private void dismissImGroup(String userId,String roomId, String serviceProvider) throws Exception {
+        log.info("dismissImGroup: roomId = {}, userId = {}", roomId, userId);
+        String joinImGroupKey = "joinImGroup:" + roomId;
+        redisTemplate.delete(joinImGroupKey);
+
+        RTCRoomPluginService pluginService = rtcRoomPluginContext.getPluginService(serviceProvider);
+        if (TencentCloudRTCPlugin.PLUGIN_NAME.equals(pluginService.pluginName())) {
+            // 腾讯云群销毁
+            pluginService.chatRoomDestroy(roomId);
+        } else {
+            // 融云群销毁
+            // 销毁群组
+            imHelper.dismiss(userId, roomId);
+        }
+    }
 }

+ 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);

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

@@ -42,6 +42,7 @@ public class MusicTagServiceImpl extends ServiceImpl<MusicTagDao, MusicTag> impl
      @Override
      public IPage<MusicTagVo> selectPage(IPage<MusicTagVo> page, MusicTagSearch query){
          IPage<MusicTagVo> musicTagVoIPage = page.setRecords(baseMapper.selectPage(page, query));
+
          // 将父类的id 拿出来,集体查出子集
          List<Long> longList = musicTagVoIPage
                  .getRecords()

+ 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());

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

@@ -1,5 +1,6 @@
 package com.yonge.cooleshow.biz.dal.service.impl;
 
+
 import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -46,9 +47,12 @@ import com.yonge.cooleshow.biz.dal.dao.TeacherDao;
 import com.yonge.cooleshow.biz.dal.dao.VideoLessonAuthRecordDao;
 import com.yonge.cooleshow.biz.dal.dao.VideoLessonGroupDao;
 import com.yonge.cooleshow.biz.dal.dao.VideoLessonGroupDetailDao;
+import com.yonge.cooleshow.biz.dal.dto.CourseMusicAlbumRelationDto;
+import com.yonge.cooleshow.biz.dal.dto.VideoLessonGroupDetailDto;
 import com.yonge.cooleshow.biz.dal.dto.search.VideoGroupSearch;
 import com.yonge.cooleshow.biz.dal.dto.search.VideoLessonGroupSearch;
 import com.yonge.cooleshow.biz.dal.dto.search.VideoLessonSearch;
+import com.yonge.cooleshow.biz.dal.entity.CourseRelationMusicAlbum;
 import com.yonge.cooleshow.biz.dal.entity.Subject;
 import com.yonge.cooleshow.biz.dal.entity.Teacher;
 import com.yonge.cooleshow.biz.dal.entity.VideoLessonAuthRecord;
@@ -59,11 +63,16 @@ import com.yonge.cooleshow.biz.dal.enums.AuthStatusEnum;
 import com.yonge.cooleshow.biz.dal.enums.ClientEnum;
 import com.yonge.cooleshow.biz.dal.enums.MessageTypeEnum;
 import com.yonge.cooleshow.biz.dal.enums.TeacherTagEnum;
+import com.yonge.cooleshow.biz.dal.enums.course.CourseTypeEnum;
+import com.yonge.cooleshow.biz.dal.service.CourseRelationMusicAlbumService;
+import com.yonge.cooleshow.biz.dal.service.MusicSheetPurchaseRecordService;
 import com.yonge.cooleshow.biz.dal.service.SysConfigService;
 import com.yonge.cooleshow.biz.dal.service.SysMessageService;
 import com.yonge.cooleshow.biz.dal.service.TeacherService;
+import com.yonge.cooleshow.biz.dal.service.VideoLessonGroupDetailService;
 import com.yonge.cooleshow.biz.dal.service.VideoLessonGroupLogService;
 import com.yonge.cooleshow.biz.dal.service.VideoLessonGroupService;
+import com.yonge.cooleshow.biz.dal.service.VideoLessonPurchaseRecordService;
 import com.yonge.cooleshow.biz.dal.vo.CountVideoGroupVo;
 import com.yonge.cooleshow.biz.dal.vo.LessonGroupVo;
 import com.yonge.cooleshow.biz.dal.vo.ShareProfitVo;
@@ -77,12 +86,27 @@ import com.yonge.cooleshow.biz.dal.vo.VideoLessonStudentDetailVo;
 import com.yonge.cooleshow.biz.dal.vo.VideoLessonStudentVo;
 import com.yonge.cooleshow.biz.dal.vo.VideoLessonTeacherVo;
 import com.yonge.cooleshow.biz.dal.vo.VideoLessonVo;
+import com.yonge.cooleshow.biz.dal.wrapper.course.CourseRelationWrapper;
 import com.yonge.cooleshow.common.constant.SysConfigConstant;
 import com.yonge.cooleshow.common.enums.YesOrNoEnum;
 import com.yonge.toolset.base.exception.BizException;
 import com.yonge.toolset.base.string.MessageFormatter;
 import com.yonge.toolset.thirdparty.message.MessageSenderPluginContext;
 import com.yonge.toolset.utils.json.JsonUtil;
+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;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
 
 /**
  * VideoLessonGroupService服务实现类
@@ -634,6 +658,7 @@ public class VideoLessonGroupServiceImpl extends ServiceImpl<VideoLessonGroupDao
         }
 
 
+
         // 草稿状态不审核
         if (lessonGroup.getDraftFlag() == null) {
             lessonGroup.setDraftFlag(YesOrNoEnum.NO);

+ 1 - 0
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/vo/TeacherHomeVo.java

@@ -95,6 +95,7 @@ public class TeacherHomeVo extends Teacher implements Serializable {
         this.tenantName = tenantName;
     }
 
+
     public String getDefaultSubjectName() {
         return defaultSubjectName;
     }

+ 51 - 48
cooleshow-user/user-biz/src/main/java/com/yonge/cooleshow/biz/dal/wrapper/StudentWrapper.java

@@ -4,6 +4,7 @@ import com.alibaba.excel.annotation.ExcelProperty;
 import com.yonge.cooleshow.biz.dal.entity.SysUser;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
+import java.io.Serializable;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
@@ -23,73 +24,75 @@ import java.util.regex.Pattern;
  */
 public class StudentWrapper {
 
-    @Data
-    @Builder
-    @NoArgsConstructor
-    @AllArgsConstructor
-    @ApiModel("学生帐号关联")
-    public static class UnionStudent implements Serializable {
+  @Data
+  @Builder
+  @NoArgsConstructor
+  @AllArgsConstructor
+  @ApiModel("学生帐号关联")
+  public static class UnionStudent implements Serializable {
 
-        @ApiModelProperty("帐号关联Id")
-        private Long unionId;
+    @ApiModelProperty("帐号关联Id")
+    private Long unionId;
 
-        @ApiModelProperty("授权Token")
-        private String token;
+    @ApiModelProperty("授权Token")
+    private String token;
 
-        @ApiModelProperty("联系方式")
-        private String mobile;
+    @ApiModelProperty("联系方式")
+    private String mobile;
 
-        @ApiModelProperty("声部名称")
-        private String subjectName;
+    @ApiModelProperty("声部名称")
+    private String subjectName;
 
-        @ApiModelProperty("用户名")
-        private String username;
+    @ApiModelProperty("用户名")
+    private String username;
 
-        @ApiModelProperty("性别(0,女  1,男)")
-        private Integer gender;
+    @ApiModelProperty("性别(0,女  1,男)")
+    private Integer gender;
 
-        @ApiModelProperty("是否校验token")
-        private Boolean tokenCheck;
+    @ApiModelProperty("是否校验token")
+    private Boolean tokenCheck;
 
-        @ApiModelProperty("0-正常,1-锁定")
-        private Integer lockFlag;
-    }
+    @ApiModelProperty("0-正常,1-锁定")
+    private Integer lockFlag;
 
-    @Data
-    @Builder
-    @NoArgsConstructor
-    @AllArgsConstructor
-    @ApiModel("帐号关联响应")
-    public static class UnionStudentResp implements Serializable {
+  }
 
-        @ApiModelProperty("帐号关联Id")
-        private Long unionId;
+  @Data
+  @Builder
+  @NoArgsConstructor
+  @AllArgsConstructor
+  @ApiModel("帐号关联响应")
+  public static class UnionStudentResp implements Serializable {
 
-        @ApiModelProperty("联系方式")
-        private String mobile;
+    @ApiModelProperty("帐号关联Id")
+    private Long unionId;
 
-        @ApiModelProperty("用户Id")
-        private Long userId;
+    @ApiModelProperty("联系方式")
+    private String mobile;
 
-        @ApiModelProperty("用户名")
-        private String username;
+    @ApiModelProperty("用户Id")
+    private Long userId;
 
-        @ApiModelProperty("更新标识")
-        private Boolean updateFlag;
+    @ApiModelProperty("用户名")
+    private String username;
 
-        private SysUser sysUser;
+    @ApiModelProperty("更新标识")
+    private Boolean updateFlag;
 
-        public UnionStudentResp userId(Long userId) {
-            this.userId = userId;
-            return this;
-        }
+    private SysUser sysUser;
 
-        public UnionStudentResp username(String username) {
-            this.username = username;
-            return this;
-        }
+    public UnionStudentResp userId(Long userId) {
+      this.userId = userId;
+      return this;
     }
 
+    public UnionStudentResp username(String username) {
+      this.username = username;
+      return this;
+    }
+  }
+
+
     @Data
     @ApiModel("学生基础信息")
     public static class Student {

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

@@ -3,9 +3,13 @@ package com.yonge.cooleshow.biz.dal.wrapper.im;
 import com.yonge.cooleshow.biz.dal.enums.ImGroupType;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
 import lombok.Data;
+import lombok.NoArgsConstructor;
 
 import javax.validation.constraints.NotBlank;
+import java.io.Serializable;
 import java.util.Date;
 import java.util.HashSet;
 import java.util.Set;
@@ -14,6 +18,25 @@ import java.util.Set;
 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;
+        }
+    }
+
+    @Data
     @ApiModel("ImGroup")
     public static class ImGroup {
         @NotBlank(message = "群编号不可为空")

+ 7 - 1
cooleshow-user/user-biz/src/main/resources/config/mybatis/CourseScheduleMapper.xml

@@ -18,11 +18,16 @@
         <result column="created_time_" jdbcType="TIMESTAMP" property="createdTime"/>
         <result column="updated_by_" jdbcType="INTEGER" property="updatedBy"/>
         <result column="updated_time_" jdbcType="TIMESTAMP" property="updatedTime"/>
+        <result column="service_provider_"  property="serviceProvider"/>
+        <result column="room_id_"  property="roomId"/>
+        <result column="mute_all_"  property="muteAll"/>
     </resultMap>
 
     <sql id="Base_Column_List">
         id_
-        , course_group_id_, type_, status_,class_num_, teacher_id_, class_date_, start_time_, end_time_, lock_, lock_time_, ex_student_num_, created_by_, created_time_, updated_by_, updated_time_
+        , course_group_id_, type_, status_,class_num_, teacher_id_, class_date_,
+            start_time_, end_time_, lock_, lock_time_, ex_student_num_,
+            created_by_, created_time_, updated_by_, updated_time_,service_provider_,room_id_,mute_all_
     </sql>
 
     <insert id="insertBatch" keyColumn="id_" keyProperty="id" useGeneratedKeys="true"
@@ -158,6 +163,7 @@
         from course_schedule as a
         left join course_group as b on a.course_group_id_ = b.id_
         left join subject as s on b.subject_id_ = s.id_
+
         left join sys_user su on su.id_ = a.teacher_id_
 
         where b.teacher_id_ = #{param.teacherId}

+ 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" >
+                   (#{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">

+ 1 - 0
cooleshow-user/user-biz/src/main/resources/config/mybatis/SubjectMapper.xml

@@ -60,6 +60,7 @@
                     (select a.subject_id_ as subject_id_ from course_group a
                     where a.type_ = 'PRACTICE' and a.status_ in ('ING','COMPLETE','APPLY')
                     GROUP BY a.subject_id_)
+
             </if>
             <if test="type == null or type =='LIVE'">
                 union all

+ 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")

+ 17 - 0
cooleshow-user/user-student/src/main/java/com/yonge/cooleshow/student/controller/open/OpenClient.java

@@ -42,6 +42,23 @@ import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.bind.annotation.RestController;
 
+import com.ksyun.ks3.dto.PostObjectFormFields;
+import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
+import com.yonge.cooleshow.auth.api.entity.SysUser;
+import com.yonge.cooleshow.biz.dal.dto.MusicImgDto;
+import com.yonge.cooleshow.biz.dal.service.*;
+import com.yonge.cooleshow.biz.dal.service.ActivityPlanService;
+import com.yonge.cooleshow.biz.dal.service.MusicSheetService;
+import com.yonge.cooleshow.biz.dal.service.StudentService;
+import com.yonge.cooleshow.biz.dal.service.TeacherService;
+import com.yonge.cooleshow.biz.dal.service.UploadFileService;
+import com.yonge.cooleshow.biz.dal.vo.MusicActivityVo;
+import com.yonge.cooleshow.biz.dal.vo.TeacherVo;
+import com.yonge.cooleshow.common.controller.BaseController;
+import com.yonge.cooleshow.common.entity.HttpResponseResult;
+import com.yonge.toolset.base.util.StringUtil;
+import com.yonge.toolset.thirdparty.entity.UploadSign;
+import com.yonge.toolset.utils.string.ValueUtil;
 import javax.validation.Valid;
 import java.util.HashMap;
 import java.util.List;

+ 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) {

+ 0 - 1
cooleshow-user/user-teacher/src/main/java/com/yonge/cooleshow/teacher/controller/open/OpenShareController.java

@@ -188,7 +188,6 @@ public class OpenShareController extends BaseController {
         // 响应数据
         return succeed(Objects.nonNull(teacher));
     }
-
     @PostMapping("/appVersion")
     @ApiOperation(value = "查询app是否为审核版本")
     public HttpResponseResult<CheckVo> appVersion(@RequestBody Map<String,String> obj) {

+ 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();

+ 7 - 13
cooleshow-user/user-website/src/main/java/com/yonge/cooleshow/website/controller/open/OpenClient.java

@@ -1,25 +1,19 @@
 package com.yonge.cooleshow.website.controller.open;
 
 import com.ksyun.ks3.dto.PostObjectFormFields;
-import com.yonge.cooleshow.auth.api.client.SysUserFeignService;
-import com.yonge.cooleshow.auth.api.entity.SysUser;
-import com.yonge.cooleshow.biz.dal.dto.MusicImgDto;
-import com.yonge.cooleshow.biz.dal.service.*;
-import com.yonge.cooleshow.biz.dal.vo.MusicActivityVo;
-import com.yonge.cooleshow.biz.dal.vo.TeacherVo;
+import com.yonge.cooleshow.biz.dal.service.UploadFileService;
 import com.yonge.cooleshow.common.controller.BaseController;
 import com.yonge.cooleshow.common.entity.HttpResponseResult;
-import com.yonge.toolset.base.util.StringUtil;
 import com.yonge.toolset.thirdparty.entity.UploadSign;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiImplicitParam;
-import com.yonge.toolset.utils.string.ValueUtil;
-import io.swagger.annotations.*;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.*;
-
-import javax.validation.Valid;
-import java.util.Map;
+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;
 
 
 @RestController

+ 1 - 0
pom.xml

@@ -22,6 +22,7 @@
 		<zipkin.version>2.12.2</zipkin.version>
 		<google.zxing.version>3.4.0</google.zxing.version>
 		<redisson.version>3.16.4</redisson.version>
+		<maven.test.skip>true</maven.test.skip>
 	</properties>
 
 

+ 3 - 1
service.md

@@ -1,8 +1,10 @@
 - [ ] gateway-server
 - [ ] auth-server
+- [x] admin-server
+- [ ] auth-server
 - [ ] admin-server
 - [ ] teacher-server
-- [x] student-server
+- [ ] student-server
 - [ ] website-server
 - [ ] task-server
 - [ ] websocket-server