Explorar o código

Merge branch 'saas' of http://git.dayaedu.com/yonge/mec into zx_saas_0303

 Conflicts:
	mec-biz/src/main/java/com/ym/mec/biz/service/impl/ClassGroupServiceImpl.java
zouxuan %!s(int64=3) %!d(string=hai) anos
pai
achega
7ffa58da1b
Modificáronse 100 ficheiros con 5110 adicións e 872 borrados
  1. 4 0
      audio-analysis/src/main/java/com/yonge/netty/dto/UserChannelContext.java
  2. 1 1
      audio-analysis/src/main/resources/logback-spring.xml
  3. 22 21
      mec-auth/mec-auth-server/src/main/java/com/ym/mec/auth/config/ResourceServerConfig.java
  4. 111 92
      mec-auth/mec-auth-server/src/main/java/com/ym/mec/auth/dal/dao/SysUserDao.java
  5. 110 90
      mec-auth/mec-auth-server/src/main/java/com/ym/mec/auth/service/SysUserService.java
  6. 176 142
      mec-auth/mec-auth-server/src/main/java/com/ym/mec/auth/service/impl/SysUserServiceImpl.java
  7. 12 0
      mec-auth/mec-auth-server/src/main/java/com/ym/mec/auth/web/controller/UserController.java
  8. 13 0
      mec-auth/mec-auth-server/src/main/resources/config/mybatis/SysUserMapper.xml
  9. 28 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/dao/ImLiveBroadcastRoomDao.java
  10. 23 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/dao/ImLiveBroadcastRoomDataDao.java
  11. 26 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/dao/ImLiveBroadcastRoomMemberDao.java
  12. 20 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/dao/ImLiveRoomVideoDao.java
  13. 1 1
      mec-biz/src/main/java/com/ym/mec/biz/dal/dao/IndexErrDataRecordDao.java
  14. 165 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/dto/ImLiveBroadcastRoomDto.java
  15. 12 1
      mec-biz/src/main/java/com/ym/mec/biz/dal/dto/StudentManageListDto.java
  16. 217 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/entity/ImLiveBroadcastRoom.java
  17. 119 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/entity/ImLiveBroadcastRoomData.java
  18. 97 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/entity/ImLiveBroadcastRoomMember.java
  19. 121 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/entity/ImLiveRoomVideo.java
  20. 26 2
      mec-biz/src/main/java/com/ym/mec/biz/dal/entity/Student.java
  21. 1 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/enums/MessageTypeEnum.java
  22. 101 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/vo/ImLiveBroadcastRoomDetailVo.java
  23. 102 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/vo/ImLiveBroadcastRoomMemberVo.java
  24. 249 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/vo/ImLiveBroadcastRoomVo.java
  25. 100 0
      mec-biz/src/main/java/com/ym/mec/biz/dal/vo/RoomUserInfoVo.java
  26. 18 0
      mec-biz/src/main/java/com/ym/mec/biz/service/ImLiveBroadcastRoomDataService.java
  27. 33 0
      mec-biz/src/main/java/com/ym/mec/biz/service/ImLiveBroadcastRoomMemberService.java
  28. 67 0
      mec-biz/src/main/java/com/ym/mec/biz/service/ImLiveBroadcastRoomService.java
  29. 21 0
      mec-biz/src/main/java/com/ym/mec/biz/service/ImLiveRoomVideoService.java
  30. 3 3
      mec-biz/src/main/java/com/ym/mec/biz/service/impl/ClassGroupServiceImpl.java
  31. 3 3
      mec-biz/src/main/java/com/ym/mec/biz/service/impl/EmployeeServiceImpl.java
  32. 1 1
      mec-biz/src/main/java/com/ym/mec/biz/service/impl/ExportServiceImpl.java
  33. 28 0
      mec-biz/src/main/java/com/ym/mec/biz/service/impl/ImLiveBroadcastRoomDataServiceImpl.java
  34. 62 0
      mec-biz/src/main/java/com/ym/mec/biz/service/impl/ImLiveBroadcastRoomMemberServiceImpl.java
  35. 980 0
      mec-biz/src/main/java/com/ym/mec/biz/service/impl/ImLiveBroadcastRoomServiceImpl.java
  36. 44 0
      mec-biz/src/main/java/com/ym/mec/biz/service/impl/ImLiveRoomVideoServiceImpl.java
  37. 1 3
      mec-biz/src/main/java/com/ym/mec/biz/service/impl/IndexErrDataRecordServiceImpl.java
  38. 16 4
      mec-biz/src/main/java/com/ym/mec/biz/service/impl/MusicGroupServiceImpl.java
  39. 3 1
      mec-biz/src/main/java/com/ym/mec/biz/service/impl/PayServiceImpl.java
  40. 45 0
      mec-biz/src/main/resources/config/mybatis/ImLiveBroadcastRoomDataMapper.xml
  41. 92 0
      mec-biz/src/main/resources/config/mybatis/ImLiveBroadcastRoomMapper.xml
  42. 50 0
      mec-biz/src/main/resources/config/mybatis/ImLiveBroadcastRoomMemberMapper.xml
  43. 31 0
      mec-biz/src/main/resources/config/mybatis/ImLiveRoomVideoMapper.xml
  44. 1 1
      mec-biz/src/main/resources/config/mybatis/IndexErrDataRecordMapper.xml
  45. 1 1
      mec-biz/src/main/resources/config/mybatis/MusicGroupPaymentCalenderDetailMapper.xml
  46. 2 1
      mec-biz/src/main/resources/config/mybatis/StudentManageDao.xml
  47. 9 2
      mec-biz/src/main/resources/config/mybatis/StudentMapper.xml
  48. 2 2
      mec-biz/src/main/resources/config/mybatis/StudentPaymentRouteOrderMapper.xml
  49. 160 109
      mec-client-api/src/main/java/com/ym/mec/im/ImFeignService.java
  50. 26 5
      mec-client-api/src/main/java/com/ym/mec/im/fallback/ImFeignServiceFallback.java
  51. 15 4
      mec-client-api/src/main/java/com/ym/mec/task/TaskRemoteService.java
  52. 10 0
      mec-client-api/src/main/java/com/ym/mec/task/fallback/TaskRemoteServiceFallback.java
  53. 1 1
      mec-common/common-core/src/main/java/com/ym/mec/common/entity/BaseMessage.java
  54. 67 0
      mec-common/common-core/src/main/java/com/ym/mec/common/entity/ImRoomMessage.java
  55. 70 0
      mec-common/common-core/src/main/java/com/ym/mec/common/entity/ImUserState.java
  56. 117 115
      mec-im/pom.xml
  57. 1 0
      mec-im/src/main/java/com/ym/config/IMProperties.java
  58. 6 4
      mec-im/src/main/java/com/ym/config/ResourceServerConfig.java
  59. 1 1
      mec-im/src/main/java/com/ym/config/WebMvcConfig.java
  60. 65 0
      mec-im/src/main/java/com/ym/controller/LiveRoomController.java
  61. 26 6
      mec-im/src/main/java/com/ym/controller/UserController.java
  62. 1 1
      mec-im/src/main/java/com/ym/enums/ActionEnum.java
  63. 1 1
      mec-im/src/main/java/com/ym/enums/DeviceTypeEnum.java
  64. 1 1
      mec-im/src/main/java/com/ym/enums/RoleEnum.java
  65. 93 33
      mec-im/src/main/java/com/ym/http/HttpHelper.java
  66. 131 17
      mec-im/src/main/java/com/ym/mec/im/IMHelper.java
  67. 1 1
      mec-im/src/main/java/com/ym/mec/im/message/ApplyForSpeechMessage.java
  68. 1 1
      mec-im/src/main/java/com/ym/mec/im/message/AssistantTransferMessage.java
  69. 1 1
      mec-im/src/main/java/com/ym/mec/im/message/ControlDeviceNotifyMessage.java
  70. 1 1
      mec-im/src/main/java/com/ym/mec/im/message/DeviceStateChangedMessage.java
  71. 1 1
      mec-im/src/main/java/com/ym/mec/im/message/DisplayMessage.java
  72. 1 1
      mec-im/src/main/java/com/ym/mec/im/message/ExamSongDownloadMessageMessage.java
  73. 1 1
      mec-im/src/main/java/com/ym/mec/im/message/ExamSongDownloadStatusMessage.java
  74. 1 1
      mec-im/src/main/java/com/ym/mec/im/message/MemberChangedMessage.java
  75. 1 1
      mec-im/src/main/java/com/ym/mec/im/message/MetronomeMessageMessage.java
  76. 1 1
      mec-im/src/main/java/com/ym/mec/im/message/MusicScoreDownloadMessageMessage.java
  77. 1 1
      mec-im/src/main/java/com/ym/mec/im/message/MusicScoreDownloadStatusMessage.java
  78. 1 1
      mec-im/src/main/java/com/ym/mec/im/message/RoleChangedMessage.java
  79. 1 1
      mec-im/src/main/java/com/ym/mec/im/message/SpeechResultMessage.java
  80. 1 1
      mec-im/src/main/java/com/ym/mec/im/message/TicketExpiredMessage.java
  81. 1 1
      mec-im/src/main/java/com/ym/mec/im/message/TurnPageMessage.java
  82. 1 1
      mec-im/src/main/java/com/ym/mec/im/message/UpgradeRoleMessage.java
  83. 1 1
      mec-im/src/main/java/com/ym/mec/im/message/WhiteboardMessage.java
  84. 1 0
      mec-im/src/main/java/com/ym/pojo/ControlDeviceTaskInfo.java
  85. 16 0
      mec-im/src/main/java/com/ym/pojo/RecordConfig.java
  86. 50 0
      mec-im/src/main/java/com/ym/pojo/RecordNotify.java
  87. 32 0
      mec-im/src/main/java/com/ym/pojo/RecordNotifyConfig.java
  88. 57 0
      mec-im/src/main/java/com/ym/pojo/RecordNotifyOutput.java
  89. 1 0
      mec-im/src/main/java/com/ym/pojo/UpgradeRoleTaskInfo.java
  90. 211 0
      mec-im/src/main/java/com/ym/service/Impl/LiveRoomServiceImpl.java
  91. 187 184
      mec-im/src/main/java/com/ym/service/Impl/RoomServiceImpl.java
  92. 46 0
      mec-im/src/main/java/com/ym/service/LiveRoomService.java
  93. 6 0
      mec-student/src/main/java/com/ym/mec/student/controller/StudentManageController.java
  94. 24 0
      mec-task/src/main/java/com/ym/mec/task/jobs/CreateLiveRoomTask.java
  95. 24 0
      mec-task/src/main/java/com/ym/mec/task/jobs/DestroyExpiredLiveRoomTask.java
  96. 2 0
      mec-util/src/main/java/com/ym/mec/util/date/DateUtil.java
  97. 1 1
      mec-web/src/main/java/com/ym/mec/web/config/ResourceServerConfig.java
  98. 185 0
      mec-web/src/main/java/com/ym/mec/web/controller/ImLiveBroadcastRoomController.java
  99. 29 0
      mec-web/src/main/java/com/ym/mec/web/controller/ImLiveBroadcastRoomDataController.java
  100. 59 0
      mec-web/src/main/java/com/ym/mec/web/controller/ImLiveBroadcastRoomMemberController.java

+ 4 - 0
audio-analysis/src/main/java/com/yonge/netty/dto/UserChannelContext.java

@@ -764,6 +764,10 @@ public class UserChannelContext {
 			totalTimes = totalTimes + entry.getValue();
 		}
 		
+		if(totalTimes == 0){
+			totalTimes = chunkList.size();
+		}
+		
 		if (maxTimes / totalTimes < hardLevel.getIntegrityRange()) {
 			tempo = false;
 			LOGGER.debug("节奏错误原因:不是同一个音");

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

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

+ 22 - 21
mec-auth/mec-auth-server/src/main/java/com/ym/mec/auth/config/ResourceServerConfig.java

@@ -1,5 +1,7 @@
 package com.ym.mec.auth.config;
 
+import com.ym.mec.common.security.BaseAccessDeniedHandler;
+import com.ym.mec.common.security.BaseAuthenticationEntryPoint;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@@ -7,9 +9,6 @@ import org.springframework.security.oauth2.config.annotation.web.configuration.E
 import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
 import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
 
-import com.ym.mec.common.security.BaseAccessDeniedHandler;
-import com.ym.mec.common.security.BaseAuthenticationEntryPoint;
-
 /**
  * 资源服务器配置
  */
@@ -17,23 +16,25 @@ import com.ym.mec.common.security.BaseAuthenticationEntryPoint;
 @EnableResourceServer
 public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
 
-	@Autowired
-	private BaseAccessDeniedHandler baseAccessDeniedHandler;
-
-	@Autowired
-	private BaseAuthenticationEntryPoint baseAuthenticationEntryPoint;
-
-	@Override
-	public void configure(HttpSecurity http) throws Exception {
-		http.csrf().disable().exceptionHandling().accessDeniedHandler(baseAccessDeniedHandler).authenticationEntryPoint(baseAuthenticationEntryPoint).and()
-				.authorizeRequests().antMatchers("/task/**", "/user/updatePassword", "/user/noAuth/queryUserByPhone",
-				"/user/queryUserByPhone", "/user/add", "/user/queryUserById/*","/queryUserInfo").hasIpAddress("0.0.0.0/0")
-				.anyRequest().authenticated().and().httpBasic();
-	}
-
-	@Override
-	public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
-		resources.authenticationEntryPoint(baseAuthenticationEntryPoint).accessDeniedHandler(baseAccessDeniedHandler);
-	}
+    @Autowired
+    private BaseAccessDeniedHandler baseAccessDeniedHandler;
+
+    @Autowired
+    private BaseAuthenticationEntryPoint baseAuthenticationEntryPoint;
+
+    @Override
+    public void configure(HttpSecurity http) throws Exception {
+        http.csrf().disable().exceptionHandling().accessDeniedHandler(baseAccessDeniedHandler).authenticationEntryPoint(baseAuthenticationEntryPoint).and()
+                .authorizeRequests().antMatchers(
+                        "/task/**", "/user/updatePassword", "/user/noAuth/queryUserByPhone",
+                        "/user/queryUserByPhone", "/user/queryClient", "/user/add", "/user/queryUserById/*",
+                        "/queryUserInfo").hasIpAddress("0.0.0.0/0")
+                .anyRequest().authenticated().and().httpBasic();
+    }
+
+    @Override
+    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
+        resources.authenticationEntryPoint(baseAuthenticationEntryPoint).accessDeniedHandler(baseAccessDeniedHandler);
+    }
 
 }

+ 111 - 92
mec-auth/mec-auth-server/src/main/java/com/ym/mec/auth/dal/dao/SysUserDao.java

@@ -1,104 +1,123 @@
 package com.ym.mec.auth.dal.dao;
 
-import java.util.List;
-
-import org.apache.ibatis.annotations.Param;
-
 import com.ym.mec.auth.api.entity.SysUser;
 import com.ym.mec.auth.api.enums.UserLockFlag;
 import com.ym.mec.common.dal.BaseDAO;
 import com.ym.mec.common.entity.ImUserModel;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
 
 public interface SysUserDao extends BaseDAO<Integer, SysUser> {
 
-	/**
-	 * 根据用户名查询对象
-	 * @param username
-	 * @return
-	 */
-	SysUser queryByUsername(String username);
-
-	/**
-	 * 根据手机号查询对象
-	 * @param phone
-	 * @return
-	 */
-	SysUser queryByPhone(String phone);
-
-	/**
-	 * 根据手机号查询对象
-	 * @param phone
-	 * @return
-	 */
-	SysUser queryLockByPhone(String phone);
-
-	/**
-	 * 根据手机号查询对象
-	 * @param phone
-	 * @return
-	 */
-	SysUser queryByPhoneAndClient(@Param("phone") String phone, @Param("client") String client);
-
-	/**
-	 * 修改密码
-	 * @param mobile
-	 * @param password
-	 */
-	void updatePassword(@Param("mobile") String mobile, @Param("password") String password);
-
-	/**
-	 * 获取用户基本信息
-	 * @param userId
-	 * @return
-	 */
-	ImUserModel getBasic(Integer userId);
-
-	/**
-	 * 根据状态查询
-	 * @param status
-	 * @return
-	 */
-	List<SysUser> queryByStatus(UserLockFlag status);
-
-	/**
-	 * 获取随机盐不为null的一条数据
-	 * @param userType
-	 * @return
-	 */
-	SysUser findUserBySalt(String userType);
-
-	/**
-	 * 创建teacher表
-	 * @param userId
-	 * @param lesseeOrganId
-	 */
-	void insertTeacher(@Param("userId") Integer userId,
-					   @Param("lesseeOrganId") Integer lesseeOrganId,
-					   @Param("tenantId") Integer tenantId);
-
-	/**
-	 * 获取教师分部编号
-	 * @param userId
-	 * @return
-	 */
-	Integer getTeacherOrganId(Integer userId);
-
-	/**
-	 * 创建租户账户表
-	 * @param userId
-	 */
+    /**
+     * 根据用户名查询对象
+     *
+     * @param username
+     * @return
+     */
+    SysUser queryByUsername(String username);
+
+    /**
+     * 根据手机号查询对象
+     *
+     * @param phone
+     * @return
+     */
+    SysUser queryByPhone(String phone);
+
+    /**
+     * 根据手机号查询对象
+     *
+     * @param phone
+     * @return
+     */
+    SysUser queryLockByPhone(String phone);
+
+    /**
+     * 根据手机号查询对象
+     *
+     * @param phone
+     * @return
+     */
+    SysUser queryByPhoneAndClient(@Param("phone") String phone, @Param("client") String client);
+
+    /**
+     * 修改密码
+     *
+     * @param mobile
+     * @param password
+     */
+    void updatePassword(@Param("mobile") String mobile, @Param("password") String password);
+
+    /**
+     * 获取用户基本信息
+     *
+     * @param userId
+     * @return
+     */
+    ImUserModel getBasic(Integer userId);
+
+    /**
+     * 根据状态查询
+     *
+     * @param status
+     * @return
+     */
+    List<SysUser> queryByStatus(UserLockFlag status);
+
+    /**
+     * 获取随机盐不为null的一条数据
+     *
+     * @param userType
+     * @return
+     */
+    SysUser findUserBySalt(String userType);
+
+    /**
+     * 创建teacher表
+     *
+     * @param userId
+     * @param lesseeOrganId
+     */
+    void insertTeacher(@Param("userId") Integer userId,
+                       @Param("lesseeOrganId") Integer lesseeOrganId,
+                       @Param("tenantId") Integer tenantId);
+
+    /**
+     * 获取教师分部编号
+     *
+     * @param userId
+     * @return
+     */
+    Integer getTeacherOrganId(Integer userId);
+
+    /**
+     * 创建租户账户表
+     *
+     * @param userId
+     */
     void insertSysTenantAccount(Integer userId);
 
-	/**
-	 * 刷新用户token
-	 * @param userId
-	 * @param imToken
-	 */
-	void refreshImToken(@Param("userId") Integer userId, @Param("imToken") String imToken);
-
-	/**
-	 * 保存student
-	 * @param userId
-	 */
-    void saveStudent(@Param("userId") Integer userId,@Param("tenantId") Integer tenantId);
+    /**
+     * 刷新用户token
+     *
+     * @param userId
+     * @param imToken
+     */
+    void refreshImToken(@Param("userId") Integer userId, @Param("imToken") String imToken);
+
+    /**
+     * 保存student
+     *
+     * @param userId
+     */
+    void saveStudent(@Param("userId") Integer userId, @Param("tenantId") Integer tenantId);
+
+    /**
+     * 通过手机号及姓名进行模糊查询
+     *
+     * @param search 关键字
+     */
+    List<SysUser> queryLikeByPhoneOrName(@Param("search") String search,@Param("tenantId") Integer tenantId);
 }

+ 110 - 90
mec-auth/mec-auth-server/src/main/java/com/ym/mec/auth/service/SysUserService.java

@@ -5,102 +5,122 @@ import com.ym.mec.auth.api.entity.SysUser;
 import com.ym.mec.common.entity.ImUserModel;
 import com.ym.mec.common.service.BaseService;
 
+import java.util.List;
+
 public interface SysUserService extends BaseService<Integer, SysUser> {
 
-	/**
-	 * 根据用户名查询对象
-	 * @param username
-	 * @return
-	 */
-	SysUser queryByUsername(String username);
-
-	/**
-	 * 根据手机号查询对象
-	 * @param phone
-	 * @return
-	 */
-	SysUser queryByPhone(String phone);
-
-	/**
-	 * 根据手机号查询对象
-	 * @param phone
-	 * @return
-	 */
-	SysUser queryLockByPhone(String phone);
-
-	/**
-	 * 根据手机号查询对象
-	 * @param phone
-	 * @return
-	 */
-	SysUser queryByPhoneAndClient(String phone,String client);
-
-	/**
-	 * 根据用户名获取系统用户详细信息
-	 * @param username
-	 * @return
-	 */
-	SysUserInfo queryUserInfoByUsername(String username);
-
-	/**
-	 * 根据手机号获取系统用户详细信息
-	 * @param phone
-	 * @return
-	 */
-	SysUserInfo queryUserInfoByPhone(String phone);
-
-	/**
-	 * 新增用户
-	 * @param sysUser
-	 * @return
-	 */
+    /**
+     * 根据用户名查询对象
+     *
+     * @param username
+     * @return
+     */
+    SysUser queryByUsername(String username);
+
+    /**
+     * 根据手机号查询对象
+     *
+     * @param phone
+     * @return
+     */
+    SysUser queryByPhone(String phone);
+
+    /**
+     * 根据手机号查询对象
+     *
+     * @param phone
+     * @return
+     */
+    SysUser queryLockByPhone(String phone);
+
+    /**
+     * 根据手机号查询对象
+     *
+     * @param phone
+     * @return
+     */
+    SysUser queryByPhoneAndClient(String phone, String client);
+
+    /**
+     * 根据用户名获取系统用户详细信息
+     *
+     * @param username
+     * @return
+     */
+    SysUserInfo queryUserInfoByUsername(String username);
+
+    /**
+     * 根据手机号获取系统用户详细信息
+     *
+     * @param phone
+     * @return
+     */
+    SysUserInfo queryUserInfoByPhone(String phone);
+
+    /**
+     * 新增用户
+     *
+     * @param sysUser
+     * @return
+     */
     Integer add(SysUser sysUser);
 
-	/**
-	 * 获取用户基本信息
-	 * @param userId
-	 * @return
-	 */
-	ImUserModel getBasic(Integer userId);
-
-	/**
-	 * 修改用户基本信息
-	 * @param sysUser
-	 */
+    /**
+     * 获取用户基本信息
+     *
+     * @param userId
+     * @return
+     */
+    ImUserModel getBasic(Integer userId);
+
+    /**
+     * 修改用户基本信息
+     *
+     * @param sysUser
+     */
     void updateBaseInfo(SysUser sysUser);
 
-	/**
-	 * 获取用户基本信息
-	 * @param userId
-	 * @return
-	 */
-	SysUser queryUserInfo(Integer userId);
-
-	/**
-	 * 上线时初始化用户数据
-	 * @param phone
-	 * @param clientId
-	 * @return
-	 */
-	SysUserInfo initUser(Integer tenantId, String organId, String phone,String clientId);
-
-	/**
-	 * 刷新token
-	 * @param sysUser
-	 */
+    /**
+     * 获取用户基本信息
+     *
+     * @param userId
+     * @return
+     */
+    SysUser queryUserInfo(Integer userId);
+
+    /**
+     * 上线时初始化用户数据
+     *
+     * @param phone
+     * @param clientId
+     * @return
+     */
+    SysUserInfo initUser(Integer tenantId, String organId, String phone, String clientId);
+
+    /**
+     * 刷新token
+     *
+     * @param sysUser
+     */
     void refreshImToken(SysUser sysUser);
 
-	/**
-	 * 保存student数据
-	 * @param userId
-	 */
-	void saveStudent(Integer userId,Integer tenantId);
-	
-	/**
-	 * 保存teacher数据
-	 * @param userId
-	 */
-	void saveTeacher(Integer userId,Integer tenantId);
-	
-	Integer getLesseeOrganId();
+    /**
+     * 保存student数据
+     *
+     * @param userId
+     */
+    void saveStudent(Integer userId, Integer tenantId);
+
+    /**
+     * 保存teacher数据
+     *
+     * @param userId
+     */
+    void saveTeacher(Integer userId, Integer tenantId);
+
+    Integer getLesseeOrganId();
+
+    List<SysUser> queryLikeByPhoneOrName(String search);
+
+    String queryClientByPhone(String phone);
 }

+ 176 - 142
mec-auth/mec-auth-server/src/main/java/com/ym/mec/auth/service/impl/SysUserServiceImpl.java

@@ -9,160 +9,194 @@ import com.ym.mec.common.dal.BaseDAO;
 import com.ym.mec.common.entity.ImResult;
 import com.ym.mec.common.entity.ImUserModel;
 import com.ym.mec.common.service.impl.BaseServiceImpl;
+import com.ym.mec.common.tenant.TenantContextHolder;
 import com.ym.mec.im.ImFeignService;
 import com.ym.mec.web.WebFeignService;
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
 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 java.util.ArrayList;
 import java.util.List;
+import java.util.stream.Collectors;
 
 @Service
 public class SysUserServiceImpl extends BaseServiceImpl<Integer, SysUser> implements SysUserService {
 
-	@Autowired
-	private SysUserDao sysUserDao;
-	@Autowired
-	private SysRoleMenuService sysRoleMenuService;
-	@Autowired
-	private ImFeignService imFeignService;
-	@Autowired
-	private WebFeignService userFeignService;
-	@Autowired
-	private SysUserService sysUserService;
-
-	@Value("${auth.sysconfig.tenantId}")
-	private Integer lesseeOrganId;
-
-	@Override
-	public BaseDAO<Integer, SysUser> getDAO() {
-		return sysUserDao;
-	}
-
-	@Override
-	public SysUser queryByUsername(String username) {
-		return sysUserDao.queryByUsername(username);
-	}
-
-	@Override
-	public SysUser queryByPhone(String phone) {
-		return sysUserDao.queryByPhone(phone);
-	}
-	@Override
-	public SysUser queryLockByPhone(String phone) {
-		return sysUserDao.queryLockByPhone(phone);
-	}
-
-	@Override
-	public SysUser queryByPhoneAndClient(String phone,String client) {
-		return sysUserDao.queryByPhoneAndClient(phone,client);
-	}
-
-	@Override
-	public SysUserInfo queryUserInfoByUsername(String username) {
-		return getSysUserInfo(queryByUsername(username));
-	}
-
-	@Override
-	public SysUserInfo queryUserInfoByPhone(String phone) {
-		return getSysUserInfo(queryLockByPhone(phone));
-	}
-
-	private SysUserInfo getSysUserInfo(SysUser sysUser) {
-		if (sysUser == null) {
-			return null;
-		}
-		SysUserInfo userInfo = new SysUserInfo();
-		userInfo.setSysUser(sysUser);
-		List<String> permissionList = sysRoleMenuService.queryPermissionsByRoleIdList(sysUser.getId());
-		String[] strArray = permissionList.toArray(new String[permissionList.size()]);
-		userInfo.setPermissions(strArray);
-		return userInfo;
-	}
-
-	@Override
-	public Integer add(SysUser sysUser) {
-		sysUserDao.insert(sysUser);
-		ImResult imResult = imFeignService.register(new ImUserModel(sysUser.getId().toString(), sysUser.getUsername(), sysUser.getAvatar()));
-		sysUser.setImToken(imResult.getToken());
-		sysUserDao.update(sysUser);
-		return sysUser.getId();
-	}
-
-	@Override
-	public ImUserModel getBasic(Integer userId) {
-		return sysUserDao.getBasic(userId);
-	}
-
-	@Override
-	public void updateBaseInfo(SysUser sysUser) {
-		sysUserDao.update(sysUser);
-		imFeignService.update(new ImUserModel(sysUser.getId().toString(),sysUser.getRealName()==null?sysUser.getUsername():sysUser.getRealName(),sysUser.getAvatar()));
-	}
-
-	@Override
-	public SysUser queryUserInfo(Integer userId) {
-		return sysUserDao.get(userId);
-	}
-
-	@Override
-	@Transactional(rollbackFor = Exception.class)
-	public SysUserInfo initUser(Integer tenantId, String organId, String phone,String clientId) {
-		if(StringUtils.equalsIgnoreCase(clientId,"TEACHER")){
-			SysUser sysUser = new SysUser();
-			sysUser.setPhone(phone);
-			sysUser.setUserType("TEACHER");
-			sysUser.setTenantId(tenantId);
-			sysUserDao.insert(sysUser);
-			//添加用户现金账户
-			imFeignService.register(new ImUserModel(sysUser.getId().toString(),phone,null));
-			userFeignService.createCashAccount(sysUser.getId(),tenantId);
-			//sysTenantAccount
-			sysUserDao.insertSysTenantAccount(sysUser.getId());
-			//创建teacher表
-			sysUserDao.insertTeacher(sysUser.getId(),lesseeOrganId,tenantId);
-			return queryUserInfoByPhone(phone);
-		}else if(StringUtils.equalsIgnoreCase(clientId,"STUDENT")){
-			SysUser sysUser = new SysUser();
-			sysUser.setPhone(phone);
-			sysUser.setUserType("STUDENT");
-			if(StringUtils.isBlank(organId)){
-				sysUser.setOrganId(lesseeOrganId);
-			}else{
-				sysUser.setOrganId(Integer.parseInt(organId));
-			}
-			sysUser.setTenantId(tenantId);
-			sysUserDao.insert(sysUser);
-			sysUserService.saveStudent(sysUser.getId(),tenantId);
-			//添加用户现金账户
-			imFeignService.register(new ImUserModel(sysUser.getId().toString(),phone,null));
-			userFeignService.createCashAccount(sysUser.getId(),tenantId);
-			return queryUserInfoByPhone(phone);
-		}
-		return null;
-	}
-
-	@Override
-	@Transactional(rollbackFor = Exception.class)
-	public void refreshImToken(SysUser sysUser) {
-		sysUserDao.refreshImToken(sysUser.getId(),sysUser.getImToken());
-	}
-
-	@Override
-	public void saveStudent(Integer userId,Integer tenantId) {
-		sysUserDao.saveStudent(userId,tenantId);
-	}
-
-	@Override
-	public void saveTeacher(Integer userId,Integer tenantId) {
-		sysUserDao.insertTeacher(userId, lesseeOrganId,tenantId);
-	}
-
-	@Override
-	public Integer getLesseeOrganId() {
-		return lesseeOrganId;
-	}
+    @Autowired
+    private SysUserDao sysUserDao;
+    @Autowired
+    private SysRoleMenuService sysRoleMenuService;
+    @Autowired
+    private ImFeignService imFeignService;
+    @Autowired
+    private WebFeignService userFeignService;
+    @Autowired
+    private SysUserService sysUserService;
+
+    @Value("${auth.sysconfig.tenantId}")
+    private Integer lesseeOrganId;
+
+    @Override
+    public BaseDAO<Integer, SysUser> getDAO() {
+        return sysUserDao;
+    }
+
+    @Override
+    public SysUser queryByUsername(String username) {
+        return sysUserDao.queryByUsername(username);
+    }
+
+    @Override
+    public SysUser queryByPhone(String phone) {
+        return sysUserDao.queryByPhone(phone);
+    }
+
+    @Override
+    public SysUser queryLockByPhone(String phone) {
+        return sysUserDao.queryLockByPhone(phone);
+    }
+
+    @Override
+    public SysUser queryByPhoneAndClient(String phone, String client) {
+        return sysUserDao.queryByPhoneAndClient(phone, client);
+    }
+
+    @Override
+    public SysUserInfo queryUserInfoByUsername(String username) {
+        return getSysUserInfo(queryByUsername(username));
+    }
+
+    @Override
+    public SysUserInfo queryUserInfoByPhone(String phone) {
+        return getSysUserInfo(queryLockByPhone(phone));
+    }
+
+    private SysUserInfo getSysUserInfo(SysUser sysUser) {
+        if (sysUser == null) {
+            return null;
+        }
+        SysUserInfo userInfo = new SysUserInfo();
+        userInfo.setSysUser(sysUser);
+        List<String> permissionList = sysRoleMenuService.queryPermissionsByRoleIdList(sysUser.getId());
+        String[] strArray = permissionList.toArray(new String[permissionList.size()]);
+        userInfo.setPermissions(strArray);
+        return userInfo;
+    }
+
+    @Override
+    public Integer add(SysUser sysUser) {
+        sysUserDao.insert(sysUser);
+        ImResult imResult = imFeignService.register(new ImUserModel(sysUser.getId().toString(), sysUser.getUsername(), sysUser.getAvatar()));
+        sysUser.setImToken(imResult.getToken());
+        sysUserDao.update(sysUser);
+        return sysUser.getId();
+    }
+
+    @Override
+    public ImUserModel getBasic(Integer userId) {
+        return sysUserDao.getBasic(userId);
+    }
+
+    @Override
+    public void updateBaseInfo(SysUser sysUser) {
+        sysUserDao.update(sysUser);
+        imFeignService.update(new ImUserModel(sysUser.getId().toString(), sysUser.getRealName() == null ? sysUser.getUsername() : sysUser.getRealName(), sysUser.getAvatar()));
+    }
+
+    @Override
+    public SysUser queryUserInfo(Integer userId) {
+        return sysUserDao.get(userId);
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public SysUserInfo initUser(Integer tenantId, String organId, String phone, String clientId) {
+        if (StringUtils.equalsIgnoreCase(clientId, "TEACHER")) {
+            SysUser sysUser = new SysUser();
+            sysUser.setPhone(phone);
+            sysUser.setUserType("TEACHER");
+            sysUser.setTenantId(tenantId);
+            sysUserDao.insert(sysUser);
+            //添加用户现金账户
+            imFeignService.register(new ImUserModel(sysUser.getId().toString(), phone, null));
+            userFeignService.createCashAccount(sysUser.getId(), tenantId);
+            //sysTenantAccount
+            sysUserDao.insertSysTenantAccount(sysUser.getId());
+            //创建teacher表
+            sysUserDao.insertTeacher(sysUser.getId(), lesseeOrganId, tenantId);
+            return queryUserInfoByPhone(phone);
+        } else if (StringUtils.equalsIgnoreCase(clientId, "STUDENT")) {
+            SysUser sysUser = new SysUser();
+            sysUser.setPhone(phone);
+            sysUser.setUserType("STUDENT");
+            if (StringUtils.isBlank(organId)) {
+                sysUser.setOrganId(lesseeOrganId);
+            } else {
+                sysUser.setOrganId(Integer.parseInt(organId));
+            }
+            sysUser.setTenantId(tenantId);
+            sysUserDao.insert(sysUser);
+            sysUserService.saveStudent(sysUser.getId(), tenantId);
+            //添加用户现金账户
+            imFeignService.register(new ImUserModel(sysUser.getId().toString(), phone, null));
+            userFeignService.createCashAccount(sysUser.getId(), tenantId);
+            return queryUserInfoByPhone(phone);
+        }
+        return null;
+    }
+
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void refreshImToken(SysUser sysUser) {
+        sysUserDao.refreshImToken(sysUser.getId(), sysUser.getImToken());
+    }
+
+    @Override
+    public void saveStudent(Integer userId, Integer tenantId) {
+        sysUserDao.saveStudent(userId, tenantId);
+    }
+
+    @Override
+    public void saveTeacher(Integer userId, Integer tenantId) {
+        sysUserDao.insertTeacher(userId, lesseeOrganId, tenantId);
+    }
+
+    @Override
+    public Integer getLesseeOrganId() {
+        return lesseeOrganId;
+    }
+
+    /**
+     * 通过手机号及姓名进行模糊查询
+     *
+     * @param search 关键字
+     */
+    @Override
+    public List<SysUser> queryLikeByPhoneOrName(String search) {
+        if (StringUtils.isBlank(search.trim())) {
+            return new ArrayList<>();
+        }
+        List<SysUser> sysUsers = sysUserDao.queryLikeByPhoneOrName(search, TenantContextHolder.getTenantId());
+        if (CollectionUtils.isEmpty(sysUsers)) {
+            return new ArrayList<>();
+        }
+        //只查询管理及老师
+        return sysUsers.stream()
+                .filter(u -> u.getUserType().contains("SYSTEM") || u.getUserType().contains("TEACHER"))
+                .collect(Collectors.toList());
+    }
+
+    @Override
+    public String queryClientByPhone(String phone) {
+        SysUser sysUser = queryByPhone(phone);
+        if (sysUser == null) {
+            return null;
+        }
+        return sysUser.getUserType();
+    }
 
 }

+ 12 - 0
mec-auth/mec-auth-server/src/main/java/com/ym/mec/auth/web/controller/UserController.java

@@ -357,4 +357,16 @@ public class UserController extends BaseController {
 		sysUserRoleService.batchDel(Integer.parseInt(userId), roleIds);
 		return succeed();
 	}
+
+    @ApiOperation(value = "通过手机号及姓名进行模糊查询")
+    @GetMapping(value = "/queryLike")
+    public Object queryLikeByPhoneOrName(String search) {
+        return succeed(sysUserService.queryLikeByPhoneOrName(search));
+    }
+
+    @ApiOperation(value = "通过手机号查询client")
+    @GetMapping(value = "/queryClient")
+    public Object queryClientByPhone(String phone) {
+        return succeed(sysUserService.queryClientByPhone(phone));
+    }
 }

+ 13 - 0
mec-auth/mec-auth-server/src/main/resources/config/mybatis/SysUserMapper.xml

@@ -225,4 +225,17 @@
     <select id="queryByPhoneAndClient" resultMap="SysUser">
         select * from sys_user where (phone_ = #{phone} OR username_ = #{phone}) AND user_type_ LIKE CONCAT('%',#{client},'%') LIMIT 1 FOR UPDATE
     </select>
+
+    <select id="queryLikeByPhoneOrName" resultMap="SysUser">
+        select *
+        from sys_user
+        where tenant_id_ = #{tenantId}
+          and lock_flag_ = 0
+          and del_flag_ = 0
+          and (
+                    phone_ like CONCAT('%', #{search}, '%')
+                or real_name_ like CONCAT('%', #{search}, '%')
+            )
+    </select>
+
 </mapper>

+ 28 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/dao/ImLiveBroadcastRoomDao.java

@@ -0,0 +1,28 @@
+package com.ym.mec.biz.dal.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ym.mec.biz.dal.entity.ImLiveBroadcastRoom;
+import com.ym.mec.biz.dal.vo.ImLiveBroadcastRoomVo;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 直播房间管理表(ImLiveBroadcastRoom)表数据库访问层
+ *
+ * @author hgw
+ * @since 2022-02-17 20:52:05
+ */
+public interface ImLiveBroadcastRoomDao extends BaseMapper<ImLiveBroadcastRoom> {
+
+    IPage<ImLiveBroadcastRoomVo> queryPage(Page<ImLiveBroadcastRoomVo> page, @Param("param") Map<String, Object> param);
+
+    List<ImLiveBroadcastRoomVo> queryPage(@Param("param") Map<String, Object> param);
+
+    int insertBatch(@Param("entities") List<ImLiveBroadcastRoom> entities);
+
+}
+

+ 23 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/dao/ImLiveBroadcastRoomDataDao.java

@@ -0,0 +1,23 @@
+package com.ym.mec.biz.dal.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ym.mec.biz.dal.entity.ImLiveBroadcastRoomData;
+import com.ym.mec.biz.dal.vo.ImLiveBroadcastRoomDetailVo;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 直播房间数据表(ImLiveBroadcastRoomData)表数据库访问层
+ *
+ * @author hgw
+ * @since 2022-02-21 14:26:58
+ */
+public interface ImLiveBroadcastRoomDataDao extends BaseMapper<ImLiveBroadcastRoomData> {
+
+    int insertBatch(@Param("entities") List<ImLiveBroadcastRoomData> entities);
+
+    ImLiveBroadcastRoomDetailVo queryByRoomUid(@Param("roomUid") String roomUid);
+
+}
+

+ 26 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/dao/ImLiveBroadcastRoomMemberDao.java

@@ -0,0 +1,26 @@
+package com.ym.mec.biz.dal.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ym.mec.biz.dal.entity.ImLiveBroadcastRoomMember;
+import com.ym.mec.biz.dal.vo.ImLiveBroadcastRoomMemberVo;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 直播间人员关系表(ImLiveBroadcastRoomMember)表数据库访问层
+ *
+ * @author hgw
+ * @since 2022-02-21 14:26:58
+ */
+public interface ImLiveBroadcastRoomMemberDao extends BaseMapper<ImLiveBroadcastRoomMember> {
+
+    int insertBatch(@Param("entities") List<ImLiveBroadcastRoomMember> entities);
+
+    IPage<ImLiveBroadcastRoomMemberVo> queryMemberPage(Page<ImLiveBroadcastRoomMemberVo> page, @Param("param") Map<String, Object> param);
+
+}
+

+ 20 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/dao/ImLiveRoomVideoDao.java

@@ -0,0 +1,20 @@
+package com.ym.mec.biz.dal.dao;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ym.mec.biz.dal.entity.ImLiveRoomVideo;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * 直播视频记录(ImLiveRoomVideo)表数据库访问层
+ *
+ * @author hgw
+ * @since 2022-03-07 19:06:54
+ */
+public interface ImLiveRoomVideoDao extends BaseMapper<ImLiveRoomVideo> {
+
+    int insertBatch(@Param("entities") List<ImLiveRoomVideo> entities);
+
+}
+

+ 1 - 1
mec-biz/src/main/java/com/ym/mec/biz/dal/dao/IndexErrDataRecordDao.java

@@ -29,7 +29,7 @@ public interface IndexErrDataRecordDao extends BaseDAO<Long, IndexErrDataRecord>
      * @param generateTime:
      * @return int
      */
-    int countWithGenerateTime(@Param("generateTime") String generateTime, @Param("tenantId") Integer tenantId);
+    int countWithGenerateTime(@Param("generateTime") String generateTime);
 
     /**
      * @describe 获取所有异常相关教务老师编号

+ 165 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/dto/ImLiveBroadcastRoomDto.java

@@ -0,0 +1,165 @@
+package com.ym.mec.biz.dal.dto;
+
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 直播房间管理表(ImLiveBroadcastRoom)表实体类
+ *
+ * @author hgw
+ * @since 2022-02-17 20:52:05
+ */
+@ApiModel(value = "直播房间管理")
+public class ImLiveBroadcastRoomDto implements Serializable {
+    @ApiModelProperty(value = "主键")
+    private Integer id;
+
+    @ApiModelProperty(value = "机构id")
+    private Integer tenantId;
+
+    @NotNull(message = "主讲人不能为空!")
+    @ApiModelProperty(value = "主讲人id/老师id")
+    private Integer speakerId;
+
+    @Size(max = 12, message = "房间标题最多12个字!")
+    @NotBlank(message = "房间标题不能为空")
+    @ApiModelProperty(value = "房间标题/最多12个字")
+    private String roomTitle;
+
+    @NotNull(message = "直播开始时间不能为空!")
+    @ApiModelProperty(value = "直播开始时间")
+    private Date liveStartTime;
+
+    @Size(max = 200, message = "直播内容最多200个字!")
+    @NotBlank(message = "直播内容不能为空")
+    @ApiModelProperty(value = "直播内容/最多200个字")
+    private String liveRemark;
+
+    @NotBlank(message = "预热模版不能为空")
+    @ApiModelProperty(value = "预热模版")
+    private String preTemplate;
+
+    @ApiModelProperty(value = "房间配置")
+    private RoomConfig roomConfig;
+
+    @ApiModel(value = "房间配置")
+    public static class RoomConfig implements Serializable {
+
+        @ApiModelProperty(value = "是否允许点赞 0允许 1不允许")
+        private Integer whether_like = 0;
+
+        @ApiModelProperty(value = "是否允许聊天互动  0允许 1不允许")
+        private Integer whether_chat = 0;
+
+        @ApiModelProperty(value = "是否允许保存直播回放 0允许 1不允许")
+        private Integer whether_video = 1;
+
+        @ApiModelProperty(value = "是否允许连麦 0允许 1不允许")
+        private Integer whether_mic = 0;
+
+        public Integer getWhether_like() {
+            return whether_like;
+        }
+
+        public void setWhether_like(Integer whether_like) {
+            this.whether_like = whether_like;
+        }
+
+        public Integer getWhether_chat() {
+            return whether_chat;
+        }
+
+        public void setWhether_chat(Integer whether_chat) {
+            this.whether_chat = whether_chat;
+        }
+
+        public Integer getWhether_video() {
+            return whether_video;
+        }
+
+        public void setWhether_video(Integer whether_video) {
+            this.whether_video = whether_video;
+        }
+
+        public Integer getWhether_mic() {
+            return whether_mic;
+        }
+
+        public void setWhether_mic(Integer whether_mic) {
+            this.whether_mic = whether_mic;
+        }
+    }
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Integer getTenantId() {
+        return tenantId;
+    }
+
+    public void setTenantId(Integer tenantId) {
+        this.tenantId = tenantId;
+    }
+
+    public Integer getSpeakerId() {
+        return speakerId;
+    }
+
+    public void setSpeakerId(Integer speakerId) {
+        this.speakerId = speakerId;
+    }
+
+    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 String getLiveRemark() {
+        return liveRemark;
+    }
+
+    public void setLiveRemark(String liveRemark) {
+        this.liveRemark = liveRemark;
+    }
+
+    public String getPreTemplate() {
+        return preTemplate;
+    }
+
+    public void setPreTemplate(String preTemplate) {
+        this.preTemplate = preTemplate;
+    }
+
+    public RoomConfig getRoomConfig() {
+        return roomConfig;
+    }
+
+    public void setRoomConfig(RoomConfig roomConfig) {
+        this.roomConfig = roomConfig;
+    }
+
+}
+

+ 12 - 1
mec-biz/src/main/java/com/ym/mec/biz/dal/dto/StudentManageListDto.java

@@ -3,6 +3,7 @@ package com.ym.mec.biz.dal.dto;
 import com.ym.mec.biz.dal.enums.GradeTypeEnum;
 import com.ym.mec.biz.dal.enums.YesOrNoEnum;
 import com.ym.mec.common.enums.UserGenderEnum;
+
 import io.swagger.annotations.ApiModelProperty;
 
 import java.math.BigDecimal;
@@ -118,7 +119,9 @@ public class StudentManageListDto {
     private Boolean hasNoStartCloudTeacher = false;
 
     private Integer recordUserId;
-
+	
+	private String extSubjectIds;
+	
     public Integer getRecordUserId() {
         return recordUserId;
     }
@@ -492,4 +495,12 @@ public class StudentManageListDto {
 	public void setMembershipEndTime(Date membershipEndTime) {
 		this.membershipEndTime = membershipEndTime;
 	}
+
+	public String getExtSubjectIds() {
+		return extSubjectIds;
+	}
+
+	public void setExtSubjectIds(String extSubjectIds) {
+		this.extSubjectIds = extSubjectIds;
+	}
 }

+ 217 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/entity/ImLiveBroadcastRoom.java

@@ -0,0 +1,217 @@
+package com.ym.mec.biz.dal.entity;
+
+
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import com.baomidou.mybatisplus.annotation.TableId;
+
+import java.io.Serializable;
+
+/**
+ * 直播房间管理表(ImLiveBroadcastRoom)表实体类
+ *
+ * @author hgw
+ * @since 2022-02-17 20:52:05
+ */
+@ApiModel(value = "im_live_broadcast_room-直播房间管理表")
+public class ImLiveBroadcastRoom implements Serializable {
+    @TableId(value = "id_", type = IdType.AUTO)
+    @ApiModelProperty(value = "主键")
+    private Integer id;
+
+    @TableField("tenant_id_")
+    @ApiModelProperty(value = "机构id")
+    private Integer tenantId;
+
+    @TableField("speaker_id_")
+    @ApiModelProperty(value = "主讲人id/老师id")
+    private Integer speakerId;
+
+    @TableField("room_uid_")
+    @ApiModelProperty(value = "房间编号")
+    private String roomUid;
+
+    @TableField("room_title_")
+    @ApiModelProperty(value = "房间标题/最多12个字")
+    private String roomTitle;
+
+    @TableField("live_start_time_")
+    @ApiModelProperty(value = "直播开始时间")
+    private Date liveStartTime;
+
+    @TableField("live_end_time_")
+    @ApiModelProperty(value = "直播结束时间")
+    private Date liveEndTime;
+
+    @TableField("live_remark_")
+    @ApiModelProperty(value = "直播内容/最多200个字")
+    private String liveRemark;
+
+    @TableField("pre_template_")
+    @ApiModelProperty(value = "预热模版")
+    private String preTemplate;
+
+    @TableField("room_config_")
+    @ApiModelProperty(value = "房间配置json格式-是否允许点赞-是否允许聊天互动-是否允许保存直播回放")
+    private String roomConfig;
+
+    @TableField("live_state_")
+    @ApiModelProperty(value = "直播状态 0未开始 1开始 2已结束")
+    private Integer liveState;
+
+    @TableField("room_state_")
+    @ApiModelProperty(value = "房间状态 0正常 1已删除 2销毁")
+    private Integer roomState;
+
+    @TableField("created_by_")
+    @ApiModelProperty(value = "创建人")
+    private Integer createdBy;
+
+    @TableField("created_time_")
+    @ApiModelProperty(value = "创建时间")
+    private Date createdTime;
+
+    @TableField("updated_by_")
+    @ApiModelProperty(value = "更新人")
+    private Integer updatedBy;
+
+    @TableField("updated_time_")
+    @ApiModelProperty(value = "更新时间")
+    private Date updatedTime;
+
+    private static final long serialVersionUID = 1L;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Integer getTenantId() {
+        return tenantId;
+    }
+
+    public void setTenantId(Integer tenantId) {
+        this.tenantId = tenantId;
+    }
+
+    public Integer getSpeakerId() {
+        return speakerId;
+    }
+
+    public void setSpeakerId(Integer 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 String getPreTemplate() {
+        return preTemplate;
+    }
+
+    public void setPreTemplate(String preTemplate) {
+        this.preTemplate = preTemplate;
+    }
+
+    public String getRoomConfig() {
+        return roomConfig;
+    }
+
+    public void setRoomConfig(String roomConfig) {
+        this.roomConfig = roomConfig;
+    }
+
+    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 Integer getCreatedBy() {
+        return createdBy;
+    }
+
+    public void setCreatedBy(Integer createdBy) {
+        this.createdBy = createdBy;
+    }
+
+    public Date getCreatedTime() {
+        return createdTime;
+    }
+
+    public void setCreatedTime(Date createdTime) {
+        this.createdTime = createdTime;
+    }
+
+    public Integer getUpdatedBy() {
+        return updatedBy;
+    }
+
+    public void setUpdatedBy(Integer updatedBy) {
+        this.updatedBy = updatedBy;
+    }
+
+    public Date getUpdatedTime() {
+        return updatedTime;
+    }
+
+    public void setUpdatedTime(Date updatedTime) {
+        this.updatedTime = updatedTime;
+    }
+
+}
+

+ 119 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/entity/ImLiveBroadcastRoomData.java

@@ -0,0 +1,119 @@
+package com.ym.mec.biz.dal.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 直播房间数据表(ImLiveBroadcastRoomData)表实体类
+ *
+ * @author hgw
+ * @since 2022-02-21 14:26:58
+ */
+@ApiModel(value = "im_live_broadcast_room_data-直播房间数据表")
+public class ImLiveBroadcastRoomData implements Serializable {
+    @TableId(value = "id_", type = IdType.AUTO)
+    @ApiModelProperty(value = "主键")
+    private Integer id;
+
+    @TableField("tenant_id_")
+    @ApiModelProperty(value = "机构id")
+    private Integer tenantId;
+
+    @TableField("room_uid_")
+    @ApiModelProperty(value = "房间uid")
+    private String roomUid;
+
+    @TableField("like_num_")
+    @ApiModelProperty(value = "累计点赞数")
+    private Integer likeNum;
+
+    @TableField("look_user_num_")
+    @ApiModelProperty(value = "即时观看人员数")
+    private Integer lookUserNum;
+
+    @TableField("total_user_num_")
+    @ApiModelProperty(value = "累计观看总人员数")
+    private Integer totalUserNum;
+
+    @TableField("updated_time_")
+    @ApiModelProperty(value = "更新时间")
+    private Date updatedTime;
+
+    @TableField("live_time_")
+    @ApiModelProperty(value = "直播时长")
+    private Integer liveTime;
+
+    private static final long serialVersionUID = 1L;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Integer getTenantId() {
+        return tenantId;
+    }
+
+    public void setTenantId(Integer tenantId) {
+        this.tenantId = tenantId;
+    }
+
+    public String getRoomUid() {
+        return roomUid;
+    }
+
+    public void setRoomUid(String roomUid) {
+        this.roomUid = roomUid;
+    }
+
+    public Integer getLikeNum() {
+        return likeNum;
+    }
+
+    public void setLikeNum(Integer likeNum) {
+        this.likeNum = likeNum;
+    }
+
+    public Integer getLookUserNum() {
+        return lookUserNum;
+    }
+
+    public void setLookUserNum(Integer lookUserNum) {
+        this.lookUserNum = lookUserNum;
+    }
+
+    public Integer getTotalUserNum() {
+        return totalUserNum;
+    }
+
+    public void setTotalUserNum(Integer totalUserNum) {
+        this.totalUserNum = totalUserNum;
+    }
+
+    public Date getUpdatedTime() {
+        return updatedTime;
+    }
+
+    public void setUpdatedTime(Date updatedTime) {
+        this.updatedTime = updatedTime;
+    }
+
+    public Integer getLiveTime() {
+        return liveTime;
+    }
+
+    public void setLiveTime(Integer liveTime) {
+        this.liveTime = liveTime;
+    }
+
+}
+

+ 97 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/entity/ImLiveBroadcastRoomMember.java

@@ -0,0 +1,97 @@
+package com.ym.mec.biz.dal.entity;
+
+
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import com.baomidou.mybatisplus.annotation.TableId;
+
+import java.io.Serializable;
+
+/**
+ * 直播间人员关系表(ImLiveBroadcastRoomMember)表实体类
+ *
+ * @author hgw
+ * @since 2022-02-21 14:26:58
+ */
+@ApiModel(value = "im_live_broadcast_room_member-直播间人员关系表")
+public class ImLiveBroadcastRoomMember implements Serializable {
+    @TableId(value = "id_", type = IdType.AUTO)
+    @ApiModelProperty(value = "主键")
+    private Integer id;
+
+    @TableField("tenant_id_")
+    @ApiModelProperty(value = "机构id")
+    private Integer tenantId;
+
+    @TableField("room_uid_")
+    @ApiModelProperty(value = "房间编号")
+    private String roomUid;
+
+    @TableField("user_id_")
+    @ApiModelProperty(value = "进入房间的人员id/学生id")
+    private Integer userId;
+
+    @TableField("join_time_")
+    @ApiModelProperty(value = "进入房间时间")
+    private Date joinTime;
+
+    @TableField("total_time_")
+    @ApiModelProperty(value = "累计观看时间/以分钟为单位")
+    private Integer totalTime;
+
+    private static final long serialVersionUID = 1L;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Integer getTenantId() {
+        return tenantId;
+    }
+
+    public void setTenantId(Integer tenantId) {
+        this.tenantId = tenantId;
+    }
+
+    public String getRoomUid() {
+        return roomUid;
+    }
+
+    public void setRoomUid(String roomUid) {
+        this.roomUid = roomUid;
+    }
+
+    public Integer getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Integer userId) {
+        this.userId = userId;
+    }
+
+    public Date getJoinTime() {
+        return joinTime;
+    }
+
+    public void setJoinTime(Date joinTime) {
+        this.joinTime = joinTime;
+    }
+
+    public Integer getTotalTime() {
+        return totalTime;
+    }
+
+    public void setTotalTime(Integer totalTime) {
+        this.totalTime = totalTime;
+    }
+
+}
+

+ 121 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/entity/ImLiveRoomVideo.java

@@ -0,0 +1,121 @@
+package com.ym.mec.biz.dal.entity;
+
+
+import java.util.Date;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import com.baomidou.mybatisplus.annotation.TableId;
+
+import java.io.Serializable;
+
+/**
+ * 直播视频记录(ImLiveRoomVideo)表实体类
+ *
+ * @author hgw
+ * @since 2022-03-07 19:06:54
+ */
+@ApiModel(value = "im_live_room_video-直播视频记录")
+public class ImLiveRoomVideo implements Serializable {
+    @TableId(value = "id_", type = IdType.AUTO)
+    @ApiModelProperty(value = "主键")
+    private Integer id;
+
+    @TableField("room_uid_")
+    @ApiModelProperty(value = "房间编号")
+    private String roomUid;
+
+    @TableField("record_id_")
+    @ApiModelProperty(value = "融云直播视频id")
+    private String recordId;
+
+    @TableField("url_")
+    @ApiModelProperty(value = "直播视频地址")
+    private String url;
+
+    @TableField("start_time_")
+    @ApiModelProperty(value = "视频开始时间")
+    private Date startTime;
+
+    @TableField("end_time_")
+    @ApiModelProperty(value = "视频结束时间")
+    private Date endTime;
+
+    @TableField("type")
+    @ApiModelProperty(value = "录制状态 0开始录制  1录制结束 2融云回调完成")
+    private Integer type;
+
+    @TableField("created_time_")
+    @ApiModelProperty(value = "创建时间")
+    private Date createdTime;
+
+    private static final long serialVersionUID = 1L;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public String getRoomUid() {
+        return roomUid;
+    }
+
+    public void setRoomUid(String roomUid) {
+        this.roomUid = roomUid;
+    }
+
+    public String getRecordId() {
+        return recordId;
+    }
+
+    public void setRecordId(String recordId) {
+        this.recordId = recordId;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public Date getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(Date startTime) {
+        this.startTime = startTime;
+    }
+
+    public Date getEndTime() {
+        return endTime;
+    }
+
+    public void setEndTime(Date endTime) {
+        this.endTime = endTime;
+    }
+
+    public Integer getType() {
+        return type;
+    }
+
+    public void setType(Integer type) {
+        this.type = type;
+    }
+
+    public Date getCreatedTime() {
+        return createdTime;
+    }
+
+    public void setCreatedTime(Date createdTime) {
+        this.createdTime = createdTime;
+    }
+
+}
+

+ 26 - 2
mec-biz/src/main/java/com/ym/mec/biz/dal/entity/Student.java

@@ -1,10 +1,14 @@
 package com.ym.mec.biz.dal.entity;
 
-import com.ym.mec.auth.api.entity.SysUser;
 import io.swagger.annotations.ApiModelProperty;
-import org.apache.commons.lang3.builder.ToStringBuilder;
 
 import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+
+import com.ym.mec.auth.api.entity.SysUser;
 
 /**
  * 对应数据库表(student):
@@ -76,6 +80,10 @@ public class Student extends SysUser {
 	private String activityCourseDetail;
 
 	private Integer countFlag;
+	
+	private String extSubjectIds;
+	
+	private Map<Integer,String> extSjectNamesMap = new HashMap<Integer, String>();
 
 	@ApiModelProperty(value = "家长姓名")
 	private String parentName;
@@ -316,4 +324,20 @@ public class Student extends SysUser {
 	public void setParentName(String parentName) {
 		this.parentName = parentName;
 	}
+
+	public String getExtSubjectIds() {
+		return extSubjectIds;
+	}
+
+	public void setExtSubjectIds(String extSubjectIds) {
+		this.extSubjectIds = extSubjectIds;
+	}
+
+	public Map<Integer, String> getExtSjectNamesMap() {
+		return extSjectNamesMap;
+	}
+
+	public void setExtSjectNamesMap(Map<Integer, String> extSjectNamesMap) {
+		this.extSjectNamesMap = extSjectNamesMap;
+	}
 }

+ 1 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/enums/MessageTypeEnum.java

@@ -217,6 +217,7 @@ public enum MessageTypeEnum implements BaseEnum<String, MessageTypeEnum> {
 
     STUDENT_SMS_CLOUD_PAYMENT("STUDENT_SMS_CLOUD_PAYMENT",  "待缴费订单"),
     STUDENT_PUSH_CLOUD_PAYMENT("STUDENT_PUSH_CLOUD_PAYMENT",  "待缴费订单"),
+    IM_SHARE_LIVE_URL("IM_SHARE_LIVE_URL",  "直播链接分享")
     ;
 
     MessageTypeEnum(String code, String msg) {

+ 101 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/vo/ImLiveBroadcastRoomDetailVo.java

@@ -0,0 +1,101 @@
+package com.ym.mec.biz.dal.vo;
+
+import com.ym.mec.biz.dal.entity.ImLiveRoomVideo;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * @author hgw
+ * Created by 2022-02-25
+ */
+public class ImLiveBroadcastRoomDetailVo implements Serializable {
+
+    @ApiModelProperty(value = "房间编号")
+    private String roomUid;
+
+    @ApiModelProperty(value = "房间标题/最多12个字")
+    private String roomTitle;
+
+    @ApiModelProperty(value = "主讲人名称")
+    private String speakerName;
+
+    @ApiModelProperty(value = "直播内容/最多200个字")
+    private String liveRemark;
+
+    @ApiModelProperty(value = "参与人员数量")
+    private Integer totalLookNum;
+
+    @ApiModelProperty(value = "点赞数")
+    private Integer totalLikeNum;
+
+    @ApiModelProperty(value = "直播时长")
+    private Integer totalLiveTime;
+
+    private List<ImLiveRoomVideo> videoList;
+
+    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 String getSpeakerName() {
+        return speakerName;
+    }
+
+    public void setSpeakerName(String speakerName) {
+        this.speakerName = speakerName;
+    }
+
+    public String getLiveRemark() {
+        return liveRemark;
+    }
+
+    public void setLiveRemark(String liveRemark) {
+        this.liveRemark = liveRemark;
+    }
+
+    public Integer getTotalLookNum() {
+        return totalLookNum;
+    }
+
+    public void setTotalLookNum(Integer totalLookNum) {
+        this.totalLookNum = totalLookNum;
+    }
+
+    public Integer getTotalLikeNum() {
+        return totalLikeNum;
+    }
+
+    public void setTotalLikeNum(Integer totalLikeNum) {
+        this.totalLikeNum = totalLikeNum;
+    }
+
+    public Integer getTotalLiveTime() {
+        return totalLiveTime;
+    }
+
+    public void setTotalLiveTime(Integer totalLiveTime) {
+        this.totalLiveTime = totalLiveTime;
+    }
+
+    public List<ImLiveRoomVideo> getVideoList() {
+        return videoList;
+    }
+
+    public void setVideoList(List<ImLiveRoomVideo> videoList) {
+        this.videoList = videoList;
+    }
+}

+ 102 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/vo/ImLiveBroadcastRoomMemberVo.java

@@ -0,0 +1,102 @@
+package com.ym.mec.biz.dal.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.util.Date;
+
+/**
+ * @author hgw
+ * Created by 2022-02-25
+ */
+public class ImLiveBroadcastRoomMemberVo implements java.io.Serializable {
+
+    @ApiModelProperty(value = "房间编号")
+    private String roomUid;
+
+    @ApiModelProperty(value = "房间标题")
+    private String roomTitle;
+
+    @ApiModelProperty(value = "学生编号")
+    private Integer studentId;
+
+    @ApiModelProperty(value = "学生姓名")
+    private String studentName;
+
+    @ApiModelProperty(value = "手机号")
+    private String phone;
+
+    @ApiModelProperty(value = "声部名称")
+    private String subName;
+
+    @ApiModelProperty(value = "进入房间时间")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date joinTime;
+
+    @ApiModelProperty(value = "累计观看时长")
+    private Integer totalViewTime;
+
+    public Integer getStudentId() {
+        return studentId;
+    }
+
+    public void setStudentId(Integer studentId) {
+        this.studentId = studentId;
+    }
+
+    public String getStudentName() {
+        return studentName;
+    }
+
+    public void setStudentName(String studentName) {
+        this.studentName = studentName;
+    }
+
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+
+    public String getSubName() {
+        return subName;
+    }
+
+    public void setSubName(String subName) {
+        this.subName = subName;
+    }
+
+    public Date getJoinTime() {
+        return joinTime;
+    }
+
+    public void setJoinTime(Date joinTime) {
+        this.joinTime = joinTime;
+    }
+
+    public Integer getTotalViewTime() {
+        return totalViewTime;
+    }
+
+    public void setTotalViewTime(Integer totalViewTime) {
+        this.totalViewTime = totalViewTime;
+    }
+
+    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;
+    }
+}

+ 249 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/vo/ImLiveBroadcastRoomVo.java

@@ -0,0 +1,249 @@
+package com.ym.mec.biz.dal.vo;
+
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 直播房间管理表(ImLiveBroadcastRoom)表实体类
+ *
+ * @author hgw
+ * @since 2022-02-17 20:52:05
+ */
+@ApiModel(value = "im_live_broadcast_room-直播房间管理表")
+public class ImLiveBroadcastRoomVo implements Serializable {
+    @ApiModelProperty(value = "主键")
+    private Integer id;
+
+    @ApiModelProperty(value = "机构id")
+    private Integer tenantId;
+
+    @ApiModelProperty(value = "机构名称")
+    private String tenantName;
+
+    @ApiModelProperty(value = "机构logo")
+    private String tenantLogo;
+
+    @ApiModelProperty(value = "房间编号")
+    private String roomUid;
+
+    @ApiModelProperty(value = "房间标题/最多12个字")
+    private String roomTitle;
+
+    @ApiModelProperty(value = "直播内容/最多200个字")
+    private String liveRemark;
+
+    @ApiModelProperty(value = "主讲人id/老师id")
+    private Integer speakerId;
+
+    @ApiModelProperty(value = "主讲人名称")
+    private String speakerName;
+
+    @ApiModelProperty(value = "主讲人头像")
+    private String speakerPic;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    @ApiModelProperty(value = "直播开始时间")
+    private Date liveStartTime;
+
+    @ApiModelProperty(value = "直播状态 0未开始 1开始 2结束")
+    private Integer liveState;
+
+    @ApiModelProperty(value = "房间状态 0正常 1已删除")
+    private Integer roomState;
+
+    @ApiModelProperty(value = "创建人")
+    private String createdByName;
+
+    @ApiModelProperty(value = "预热模版")
+    private String preTemplate;
+
+    @ApiModelProperty(value = "房间配置json格式-是否允许点赞-是否允许聊天互动-是否允许保存直播回放")
+    private String roomConfig;
+
+    @ApiModelProperty(value = "imToken")
+    private String imToken;
+
+    @ApiModelProperty(value = "播出端")
+    private String os = "pc";
+
+    @ApiModelProperty(value = "点赞数")
+    private Integer likeNum;
+    @ApiModelProperty(value = "当前观看人数")
+    private Integer lookNum;
+    @ApiModelProperty(value = "累计观看人数")
+    private Integer totalLookNum;
+
+    public Integer getId() {
+        return id;
+    }
+
+    public void setId(Integer id) {
+        this.id = id;
+    }
+
+    public Integer getTenantId() {
+        return tenantId;
+    }
+
+    public void setTenantId(Integer tenantId) {
+        this.tenantId = tenantId;
+    }
+
+    public String getTenantName() {
+        return tenantName;
+    }
+
+    public void setTenantName(String tenantName) {
+        this.tenantName = tenantName;
+    }
+
+    public String getTenantLogo() {
+        return tenantLogo;
+    }
+
+    public void setTenantLogo(String tenantLogo) {
+        this.tenantLogo = tenantLogo;
+    }
+
+    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 String getLiveRemark() {
+        return liveRemark;
+    }
+
+    public void setLiveRemark(String liveRemark) {
+        this.liveRemark = liveRemark;
+    }
+
+    public Integer getSpeakerId() {
+        return speakerId;
+    }
+
+    public void setSpeakerId(Integer speakerId) {
+        this.speakerId = speakerId;
+    }
+
+    public String getSpeakerName() {
+        return speakerName;
+    }
+
+    public void setSpeakerName(String speakerName) {
+        this.speakerName = speakerName;
+    }
+
+    public String getSpeakerPic() {
+        return speakerPic;
+    }
+
+    public void setSpeakerPic(String speakerPic) {
+        this.speakerPic = speakerPic;
+    }
+
+    public Date getLiveStartTime() {
+        return liveStartTime;
+    }
+
+    public void setLiveStartTime(Date liveStartTime) {
+        this.liveStartTime = liveStartTime;
+    }
+
+    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 getCreatedByName() {
+        return createdByName;
+    }
+
+    public void setCreatedByName(String createdByName) {
+        this.createdByName = createdByName;
+    }
+
+    public String getPreTemplate() {
+        return preTemplate;
+    }
+
+    public void setPreTemplate(String preTemplate) {
+        this.preTemplate = preTemplate;
+    }
+
+    public String getRoomConfig() {
+        return roomConfig;
+    }
+
+    public void setRoomConfig(String roomConfig) {
+        this.roomConfig = roomConfig;
+    }
+
+    public String getImToken() {
+        return imToken;
+    }
+
+    public void setImToken(String imToken) {
+        this.imToken = imToken;
+    }
+
+    public String getOs() {
+        return os;
+    }
+
+    public void setOs(String os) {
+        this.os = os;
+    }
+
+    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 Integer getTotalLookNum() {
+        return totalLookNum;
+    }
+
+    public void setTotalLookNum(Integer totalLookNum) {
+        this.totalLookNum = totalLookNum;
+    }
+}
+

+ 100 - 0
mec-biz/src/main/java/com/ym/mec/biz/dal/vo/RoomUserInfoVo.java

@@ -0,0 +1,100 @@
+package com.ym.mec.biz.dal.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * @author hgw
+ * Created by 2022-02-24
+ */
+public class RoomUserInfoVo implements Serializable {
+    private Integer userId;
+
+    private String userName;
+
+    private Integer tenantId;
+
+    //累计观看时长
+    private Integer totalViewTime;
+
+    //首次进入时间
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date firstJoinTime;
+
+    //动态进入房间时间  多次进入房间每次进入更新
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date dynamicJoinTime;
+
+    //最后一次退出时间 该字段是为了控制连续退出的 如果2次退出时间间隔不足1分钟则不处理
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date lastOutTime;
+
+    //0:在房间 1:不在房间
+    private Integer state;
+
+    public Integer getUserId() {
+        return userId;
+    }
+
+    public void setUserId(Integer userId) {
+        this.userId = userId;
+    }
+
+    public String getUserName() {
+        return userName;
+    }
+
+    public void setUserName(String userName) {
+        this.userName = userName;
+    }
+
+    public Integer getTotalViewTime() {
+        return totalViewTime;
+    }
+
+    public void setTotalViewTime(Integer totalViewTime) {
+        this.totalViewTime = totalViewTime;
+    }
+
+    public Date getFirstJoinTime() {
+        return firstJoinTime;
+    }
+
+    public void setFirstJoinTime(Date firstJoinTime) {
+        this.firstJoinTime = firstJoinTime;
+    }
+
+    public Date getDynamicJoinTime() {
+        return dynamicJoinTime;
+    }
+
+    public void setDynamicJoinTime(Date dynamicJoinTime) {
+        this.dynamicJoinTime = dynamicJoinTime;
+    }
+
+    public Date getLastOutTime() {
+        return lastOutTime;
+    }
+
+    public void setLastOutTime(Date lastOutTime) {
+        this.lastOutTime = lastOutTime;
+    }
+
+    public Integer getTenantId() {
+        return tenantId;
+    }
+
+    public void setTenantId(Integer tenantId) {
+        this.tenantId = tenantId;
+    }
+
+    public Integer getState() {
+        return state;
+    }
+
+    public void setState(Integer state) {
+        this.state = state;
+    }
+}

+ 18 - 0
mec-biz/src/main/java/com/ym/mec/biz/service/ImLiveBroadcastRoomDataService.java

@@ -0,0 +1,18 @@
+package com.ym.mec.biz.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ym.mec.biz.dal.dao.ImLiveBroadcastRoomDataDao;
+import com.ym.mec.biz.dal.entity.ImLiveBroadcastRoomData;
+
+/**
+ * 直播房间数据表(ImLiveBroadcastRoomData)表服务接口
+ *
+ * @author hgw
+ * @since 2022-02-21 14:26:58
+ */
+public interface ImLiveBroadcastRoomDataService extends IService<ImLiveBroadcastRoomData> {
+
+    ImLiveBroadcastRoomDataDao getDao();
+
+}
+

+ 33 - 0
mec-biz/src/main/java/com/ym/mec/biz/service/ImLiveBroadcastRoomMemberService.java

@@ -0,0 +1,33 @@
+package com.ym.mec.biz.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ym.mec.biz.dal.dao.ImLiveBroadcastRoomMemberDao;
+import com.ym.mec.biz.dal.entity.ImLiveBroadcastRoomMember;
+import com.ym.mec.biz.dal.vo.ImLiveBroadcastRoomDetailVo;
+import com.ym.mec.biz.dal.vo.ImLiveBroadcastRoomMemberVo;
+import com.ym.mec.common.page.PageInfo;
+
+import java.util.Map;
+
+/**
+ * 直播间人员关系表(ImLiveBroadcastRoomMember)表服务接口
+ *
+ * @author hgw
+ * @since 2022-02-21 14:26:59
+ */
+public interface ImLiveBroadcastRoomMemberService extends IService<ImLiveBroadcastRoomMember> {
+
+    ImLiveBroadcastRoomMemberDao getDao();
+
+    /**
+     * 查询房间详情
+     */
+    ImLiveBroadcastRoomDetailVo queryRoomDetail(Map<String, Object> param);
+
+    /**
+     * 查询房间观看人员详情
+     */
+    PageInfo<ImLiveBroadcastRoomMemberVo> queryRoomMember(Map<String, Object> param);
+
+}
+

+ 67 - 0
mec-biz/src/main/java/com/ym/mec/biz/service/ImLiveBroadcastRoomService.java

@@ -0,0 +1,67 @@
+package com.ym.mec.biz.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ym.mec.biz.dal.dto.ImLiveBroadcastRoomDto;
+import com.ym.mec.biz.dal.entity.ImLiveBroadcastRoom;
+import com.ym.mec.biz.dal.vo.ImLiveBroadcastRoomVo;
+import com.ym.mec.biz.dal.vo.RoomUserInfoVo;
+import com.ym.mec.common.entity.ImUserState;
+import com.ym.mec.common.page.PageInfo;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 直播房间管理表(ImLiveBroadcastRoom)表服务接口
+ *
+ * @author hgw
+ * @since 2022-02-17 20:52:05
+ */
+public interface ImLiveBroadcastRoomService extends IService<ImLiveBroadcastRoom> {
+
+    PageInfo<ImLiveBroadcastRoomVo> queryPage(Map<String, Object> param);
+
+    ImLiveBroadcastRoomVo queryRoomAndCheck(String roomUid, Integer userId);
+
+    ImLiveBroadcastRoomVo queryRoomInfo(String roomUid);
+
+    void add(ImLiveBroadcastRoomDto dto);
+
+    void update(ImLiveBroadcastRoomDto dto);
+
+    void whetherChat(Integer id, Integer whetherChat);
+
+    void roomDestroy(Integer id);
+
+    void delete(Integer id);
+
+    void destroyExpiredLiveRoom();
+
+    void syncLike(String roomUid, Integer likeNum);
+
+    void opsRoom(List<ImUserState> userState);
+
+    ImLiveBroadcastRoomVo speakerJoinRoom(String roomUid);
+
+    void joinRoom(String roomUid, Integer userId);
+
+    void startLive(String roomUid,Integer userId);
+
+    void closeLive(String roomUid,Integer userId);
+
+    void createLiveRoom();
+
+    Map<String, Object> test(String roomUid, Integer userId);
+
+    /**
+    * @description: 分享直播链接
+     * @param roomUid
+    * @return void
+    * @author zx
+    * @date 2022/2/23 16:17
+    */
+    void shareGroup(String roomUid,String groupIds);
+
+    List<RoomUserInfoVo> queryRoomUserInfo(String roomUid);
+}
+

+ 21 - 0
mec-biz/src/main/java/com/ym/mec/biz/service/ImLiveRoomVideoService.java

@@ -0,0 +1,21 @@
+package com.ym.mec.biz.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.ym.mec.biz.dal.dao.ImLiveRoomVideoDao;
+import com.ym.mec.biz.dal.entity.ImLiveRoomVideo;
+
+import java.util.List;
+
+/**
+ * 直播视频记录(ImLiveRoomVideo)表服务接口
+ *
+ * @author hgw
+ * @since 2022-03-07 19:06:55
+ */
+public interface ImLiveRoomVideoService extends IService<ImLiveRoomVideo> {
+
+    ImLiveRoomVideoDao getDao();
+
+    List<ImLiveRoomVideo> queryList(String roomUid);
+}
+

+ 3 - 3
mec-biz/src/main/java/com/ym/mec/biz/service/impl/ClassGroupServiceImpl.java

@@ -4439,9 +4439,9 @@ public class ClassGroupServiceImpl extends BaseServiceImpl<Integer, ClassGroup>
         //获取分布默认的课程类型单价
         MusicGroup musicGroup = musicGroupDao.findByClassGroupId(masterClassGroupId);
         //主班乐团不能是系统收费团
-//        if(musicGroup.getCourseViewType() == CourseViewTypeEnum.MEMBER_FEE){
-//            throw new BizException("操作失败:主班乐团不能是系统收费团");
-//        }
+        /*if(musicGroup.getCourseViewType() == CourseViewTypeEnum.MEMBER_FEE){
+            throw new BizException("操作失败:主班乐团不能是系统收费团");
+        }*/
         Map<String, BigDecimal> unitPriceMap = MapUtil.convertIntegerMap(organizationCourseUnitPriceSettingsDao.queryMapByOrganIdAndChargeTypeId(musicGroup.getChargeTypeId(), musicGroup.getOrganId()));
         Set<String> masterKeySet = masterMap.keySet();
         //计算主班课程类型剩余价值

+ 3 - 3
mec-biz/src/main/java/com/ym/mec/biz/service/impl/EmployeeServiceImpl.java

@@ -156,10 +156,10 @@ public class EmployeeServiceImpl extends BaseServiceImpl<Integer, Employee> impl
      * @param userId       用户id
      */
     private void addUserTenant(List<Integer> tenantIds, Integer userTenantId, Integer userId) {
+        if (CollectionUtils.isEmpty(tenantIds)) {
+            throw new BizException("平台账号必须指定一个机构");
+        }
         if (Objects.nonNull(userTenantId) && userTenantId == -1) {
-            if (CollectionUtils.isEmpty(tenantIds)) {
-                throw new BizException("平台账号必须指定一个机构!");
-            }
             Date now = new Date();
             tenantIds.forEach(t -> {
                 SysUserTenant userTenant = new SysUserTenant();

+ 1 - 1
mec-biz/src/main/java/com/ym/mec/biz/service/impl/ExportServiceImpl.java

@@ -1370,7 +1370,7 @@ public class ExportServiceImpl implements ExportService {
         if (sysUser == null || sysUser.getId() == null) {
             return BaseController.failed(HttpStatus.FORBIDDEN, "请登录");
         }
-        queryInfo.setOrganId(organizationService.getEmployeeOrgan(sysUser.getId(),queryInfo.getOrganId(),sysUser.getIsSuperAdmin()));
+        queryInfo.setRoutingOrganId(organizationService.getEmployeeOrgan(sysUser.getId(),queryInfo.getRoutingOrganId(),sysUser.getIsSuperAdmin()));
         if (StringUtils.isNotBlank(queryInfo.getSearch())) {
             List<BasicUserDto> users = studentPaymentOrderDao.getUsers(queryInfo.getSearch());
             List<Integer> userIds = users.stream().map(BasicUserDto::getUserId).collect(Collectors.toList());

+ 28 - 0
mec-biz/src/main/java/com/ym/mec/biz/service/impl/ImLiveBroadcastRoomDataServiceImpl.java

@@ -0,0 +1,28 @@
+package com.ym.mec.biz.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ym.mec.biz.dal.dao.ImLiveBroadcastRoomDataDao;
+import com.ym.mec.biz.dal.entity.ImLiveBroadcastRoomData;
+import com.ym.mec.biz.service.ImLiveBroadcastRoomDataService;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+/**
+ * 直播房间数据表(ImLiveBroadcastRoomData)表服务实现类
+ *
+ * @author hgw
+ * @since 2022-02-21 14:26:58
+ */
+@Service("imLiveBroadcastRoomDataService")
+public class ImLiveBroadcastRoomDataServiceImpl extends ServiceImpl<ImLiveBroadcastRoomDataDao, ImLiveBroadcastRoomData> implements ImLiveBroadcastRoomDataService {
+
+    private final static Logger log = LoggerFactory.getLogger(ImLiveBroadcastRoomDataServiceImpl.class);
+
+    @Override
+    public ImLiveBroadcastRoomDataDao getDao() {
+        return this.baseMapper;
+    }
+
+}
+

+ 62 - 0
mec-biz/src/main/java/com/ym/mec/biz/service/impl/ImLiveBroadcastRoomMemberServiceImpl.java

@@ -0,0 +1,62 @@
+package com.ym.mec.biz.service.impl;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ym.mec.biz.dal.dao.ImLiveBroadcastRoomMemberDao;
+import com.ym.mec.biz.dal.entity.ImLiveBroadcastRoomMember;
+import com.ym.mec.biz.dal.vo.ImLiveBroadcastRoomDetailVo;
+import com.ym.mec.biz.dal.vo.ImLiveBroadcastRoomMemberVo;
+import com.ym.mec.biz.service.ImLiveBroadcastRoomDataService;
+import com.ym.mec.biz.service.ImLiveBroadcastRoomMemberService;
+import com.ym.mec.biz.service.ImLiveRoomVideoService;
+import com.ym.mec.common.page.PageInfo;
+import com.ym.mec.common.page.PageUtil;
+import com.ym.mec.common.page.WrapperUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.util.Map;
+
+/**
+ * 直播间人员关系表(ImLiveBroadcastRoomMember)表服务实现类
+ *
+ * @author hgw
+ * @since 2022-02-21 14:26:59
+ */
+@Service("imLiveBroadcastRoomMemberService")
+public class ImLiveBroadcastRoomMemberServiceImpl extends ServiceImpl<ImLiveBroadcastRoomMemberDao, ImLiveBroadcastRoomMember> implements ImLiveBroadcastRoomMemberService {
+
+    private final static Logger log = LoggerFactory.getLogger(ImLiveBroadcastRoomMemberServiceImpl.class);
+
+    @Autowired
+    private ImLiveBroadcastRoomDataService imLiveBroadcastRoomDataService;
+@Autowired
+private ImLiveRoomVideoService imLiveRoomVideoService;
+
+
+    @Override
+    public ImLiveBroadcastRoomMemberDao getDao() {
+        return this.baseMapper;
+    }
+
+    /**
+     * 查询直播详情
+     */
+    @Override
+    public ImLiveBroadcastRoomDetailVo queryRoomDetail(Map<String, Object> param) {
+        String roomUid = WrapperUtil.toStr(param, "roomUid", "请传入房间编号");
+        ImLiveBroadcastRoomDetailVo vo = imLiveBroadcastRoomDataService.getDao().queryByRoomUid(roomUid);
+        vo.setVideoList(imLiveRoomVideoService.queryList(roomUid));
+        return vo;
+    }
+
+    @Override
+    public PageInfo<ImLiveBroadcastRoomMemberVo> queryRoomMember(Map<String, Object> param) {
+        Page<ImLiveBroadcastRoomMemberVo> pageInfo = PageUtil.getPageInfo(param);
+        return PageUtil.pageInfo(baseMapper.queryMemberPage(pageInfo, param));
+    }
+
+}
+

+ 980 - 0
mec-biz/src/main/java/com/ym/mec/biz/service/impl/ImLiveBroadcastRoomServiceImpl.java

@@ -0,0 +1,980 @@
+package com.ym.mec.biz.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.ym.mec.auth.api.client.SysUserFeignService;
+import com.ym.mec.auth.api.entity.SysUser;
+import com.ym.mec.biz.dal.dao.ImLiveBroadcastRoomDao;
+import com.ym.mec.biz.dal.dao.SysConfigDao;
+import com.ym.mec.biz.dal.dto.ImLiveBroadcastRoomDto;
+import com.ym.mec.biz.dal.entity.ImLiveBroadcastRoom;
+import com.ym.mec.biz.dal.entity.ImLiveBroadcastRoomData;
+import com.ym.mec.biz.dal.entity.ImLiveBroadcastRoomMember;
+import com.ym.mec.biz.dal.enums.MessageTypeEnum;
+import com.ym.mec.biz.dal.vo.ImLiveBroadcastRoomVo;
+import com.ym.mec.biz.dal.vo.RoomUserInfoVo;
+import com.ym.mec.biz.service.*;
+import com.ym.mec.common.entity.ImRoomMessage;
+import com.ym.mec.common.entity.ImUserState;
+import com.ym.mec.common.exception.BizException;
+import com.ym.mec.common.page.PageInfo;
+import com.ym.mec.common.page.PageUtil;
+import com.ym.mec.common.page.WrapperUtil;
+import com.ym.mec.common.tenant.TenantContextHolder;
+import com.ym.mec.im.ImFeignService;
+import com.ym.mec.util.date.DateUtil;
+import com.ym.mec.util.http.HttpUtil;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.redisson.api.RBucket;
+import org.redisson.api.RMap;
+import org.redisson.api.RedissonClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.io.Serializable;
+import java.util.*;
+import java.util.function.BiConsumer;
+import java.util.stream.Collectors;
+
+/**
+ * 直播房间管理表(ImLiveBroadcastRoom)表服务实现类
+ *
+ * @author hgw
+ * @since 2022-02-17 20:52:05
+ */
+@Service("imLiveBroadcastRoomService")
+public class ImLiveBroadcastRoomServiceImpl extends ServiceImpl<ImLiveBroadcastRoomDao, ImLiveBroadcastRoom> implements ImLiveBroadcastRoomService {
+
+    private final static Logger log = LoggerFactory.getLogger(ImLiveBroadcastRoomServiceImpl.class);
+
+    @Autowired
+    private SysUserFeignService sysUserFeignService;
+    @Autowired
+    private ImFeignService imFeignService;
+    @Autowired
+    private RedissonClient redissonClient;
+    @Autowired
+    private SysMessageService sysMessageService;
+    @Autowired
+    private SysConfigDao sysConfigDao;
+    @Autowired
+    private ImLiveBroadcastRoomMemberService liveBroadcastRoomMemberService;
+    @Autowired
+    private ImLiveBroadcastRoomDataService liveBroadcastRoomDataService;
+
+    public static final String USER_ID = "${userId}";
+    public static final String ROOM_UID = "${roomUid}";
+
+    //直播间累计用户信息-指只要进入到该房间的用户都要记录
+    public static final String LIVE_ROOM_TOTAL_USER_LIST = "IM:LIVE_ROOM_TOTAL_USER_LIST:" + ROOM_UID;
+    //主讲人信息
+    public static final String LIVE_SPEAKER_INFO = "IM:LIVE_SPEAKER_INFO:" + USER_ID;
+    //用户对应的直播间Uid
+    public static final String LIVE_USER_ROOM = "IM:LIVE_ROOM_USER:" + USER_ID;
+    //房间点赞数
+    public static final String LIVE_ROOM_LIKE = "IM:LIVE_ROOM_LIKE:" + ROOM_UID;
+
+    /**
+     * 进入直播间检查数据
+     *
+     * @param roomUid 房间uid
+     * @param userId  用户id
+     */
+    @Override
+    public ImLiveBroadcastRoomVo queryRoomAndCheck(String roomUid, Integer userId) {
+        SysUser sysUser;
+        if (Objects.isNull(userId)) {
+            sysUser = getSysUser();
+        } else {
+            sysUser = getSysUser(userId);
+        }
+        return queryRoomAndCheck(roomUid, sysUser);
+    }
+
+    public ImLiveBroadcastRoomVo queryRoomAndCheck(String roomUid, SysUser sysUser) {
+        //直播间信息校验
+        Optional<ImLiveBroadcastRoomVo> optional = Optional.ofNullable(roomUid)
+                .map(this::queryRoomInfo);
+        optional.orElseThrow(() -> new BizException("直播间不存在"));
+        optional.filter(r -> r.getTenantId().equals(sysUser.getTenantId()))
+                .orElseThrow(() -> new BizException("您不是该直播机构人员,不可观看!"));
+        optional.filter(r -> r.getRoomState() != 1).orElseThrow(() -> new BizException("直播间不存在"));
+        ImLiveBroadcastRoomVo room = optional.get();
+        if (room.getLiveState() == 0) {
+            throw new BizException("直播未开始,直播开启的时间是 "
+                    + DateUtil.format(room.getLiveStartTime(), DateUtil.EXPANDED_DATE_TIME_FORMAT));
+        }
+        if (room.getLiveState() == 2) {
+            throw new BizException("直播已结束!");
+        }
+        return room;
+    }
+
+
+    /**
+     * 查询直播间信息
+     *
+     * @param roomUid 直播间uid
+     */
+    @Override
+    public ImLiveBroadcastRoomVo queryRoomInfo(String roomUid) {
+        List<ImLiveBroadcastRoomVo> list = baseMapper.queryPage(new HashMap<String, Object>() {{
+            put("roomUid", roomUid);
+        }});
+        if (CollectionUtils.isEmpty(list)) {
+            return null;
+        }
+        ImLiveBroadcastRoomVo roomVo = list.get(0);
+        getRoomData(roomVo);
+        return roomVo;
+    }
+
+    /**
+     * 分页查询直播间列表
+     */
+    @Override
+    public PageInfo<ImLiveBroadcastRoomVo> queryPage(Map<String, Object> param) {
+        Page<ImLiveBroadcastRoomVo> pageInfo = PageUtil.getPageInfo(param);
+        pageInfo.setDesc("a.created_time_");
+        param.put("tenantId", TenantContextHolder.getTenantId());
+        BiConsumer<String, String> addTimeCons = (key, time) -> Optional.ofNullable(param.get(key)).map(String::valueOf)
+                .ifPresent(date -> param.put(key, date + " " + time));
+        addTimeCons.accept("startTime", "00:00:00");
+        addTimeCons.accept("endTime", "23:59:59");
+        IPage<ImLiveBroadcastRoomVo> page = baseMapper.queryPage(pageInfo, param);
+        return PageUtil.pageInfo(page);
+    }
+
+    /**
+     * 预创建直播间
+     *
+     * @param dto 直播间信息
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void add(ImLiveBroadcastRoomDto dto) {
+        SysUser sysUser = getSysUser(dto.getSpeakerId());
+        ImLiveBroadcastRoom obj = new ImLiveBroadcastRoom();
+        BeanUtils.copyProperties(dto, obj);
+        Date now = new Date();
+        String roomUid = "LIVE-" + sysUser.getId() + "-" + now.getTime();
+        obj.setTenantId(TenantContextHolder.getTenantId());
+        obj.setRoomUid(roomUid);
+        obj.setRoomConfig(JSONObject.toJSONString(dto.getRoomConfig()));
+        obj.setLiveState(0);
+        obj.setRoomState(0);
+        obj.setCreatedBy(getSysUser().getId());
+        obj.setCreatedTime(now);
+        this.save(obj);
+    }
+
+    /**
+     * 修改直播间信息
+     *
+     * @param dto
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void update(ImLiveBroadcastRoomDto dto) {
+        ImLiveBroadcastRoom obj = this.getById(dto.getId());
+        BeanUtils.copyProperties(dto, obj);
+        obj.setRoomConfig(JSONObject.toJSONString(dto.getRoomConfig()));
+        obj.setUpdatedBy(getSysUser().getId());
+        obj.setUpdatedTime(new Date());
+        log.info("update room  >>>  :{}", JSONObject.toJSONString(obj));
+        this.updateById(obj);
+    }
+
+    /**
+     * 是否禁言
+     *
+     * @param id          房间id
+     * @param whetherChat 是否允许聊天互动  0允许 1不允许
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void whetherChat(Integer id, Integer whetherChat) {
+        ImLiveBroadcastRoom obj = this.getById(id);
+        ImLiveBroadcastRoomDto.RoomConfig roomConfig = getRoomConfig(obj.getRoomConfig()).orElse(null);
+        if (Objects.isNull(roomConfig)) {
+            return;
+        }
+        roomConfig.setWhether_chat(whetherChat);
+        obj.setRoomConfig(JSONObject.toJSONString(roomConfig));
+        this.updateById(obj);
+    }
+
+    /**
+     * 删除直播间
+     *
+     * @param id 直播间id
+     */
+    @Override
+    @Transactional(rollbackFor = Exception.class)
+    public void delete(Integer id) {
+        ImLiveBroadcastRoom obj = this.getById(id);
+        if (obj.getLiveState() == 1) {
+            throw new BizException("直播已经开始,无法删除");
+        }
+        obj.setId(id);
+        obj.setRoomState(1);
+        obj.setUpdatedBy(getSysUser().getId());
+        obj.setUpdatedTime(new Date());
+        this.updateById(obj);
+    }
+
+    /**
+     * 定时任务,每分钟执行
+     * 销毁主讲人退出超过30分钟的直播间
+     */
+    public void destroyExpiredLiveRoom() {
+        //查询已经开始并且没有删除及销毁的直播间
+        List<ImLiveBroadcastRoom> list = this.list(new WrapperUtil<ImLiveBroadcastRoom>()
+                .hasEq("live_state_", 1)
+                .hasEq("room_state_", 0)
+                .queryWrapper());
+        if (CollectionUtils.isEmpty(list)) {
+            return;
+        }
+        //查询房间过期时间
+        String expiredMinuteStr = sysConfigDao.findConfigValue("destroy_expired_live_room_minute");
+        if (StringUtils.isEmpty(expiredMinuteStr)) {
+            log.info("roomDestroy>>>> 未查询到配置:destroy_expired_live_room_minute");
+            return;
+        }
+        int expiredMinute = Integer.parseInt(expiredMinuteStr);
+        Date now = new Date();
+        list.forEach(room -> {
+            try {
+                destroyExpiredLiveRoom(now, room, expiredMinute);
+            } catch (Exception e) {
+                log.error("roomDestroy>>>> failed roomId:{} msg:{}", room.getId(), e.getMessage());
+            }
+        });
+    }
+
+    private void destroyExpiredLiveRoom(Date now, ImLiveBroadcastRoom room, int expiredMinute) {
+        log.error("roomDestroy destroyExpiredLiveRoom >>>> now {} roomInfo : {} expiredMinute:{}", now, JSONObject.toJSONString(room), expiredMinute);
+        //获取直播间主讲人信息
+        RBucket<RoomSpeakerInfo> speakerCache = redissonClient.getBucket(LIVE_SPEAKER_INFO.replace(USER_ID, room.getSpeakerId().toString()));
+        if (speakerCache.isExists()) {
+            RoomSpeakerInfo speakerInfo = speakerCache.get();
+            //过期时间= 房间正式开始时间+expiredMinute 分钟
+            Date expiredTime = DateUtil.addMinutes(room.getCreatedTime(), expiredMinute);
+            //先决条件 当前时间 大于(创建房间时填入的开播时间 + 设置的过期分钟数)
+            if (now.getTime() >= expiredTime.getTime()) {
+
+                //1.主播没有进入房间,则直接销毁房间
+                if (Objects.isNull(speakerInfo.getJoinRoomTime())) {
+                    log.info("roomDestroy not joinRoom >>>> cache : {}", JSONObject.toJSONString(test(room.getRoomUid(), room.getSpeakerId())));
+                    roomDestroy(room);
+                    return;
+                }
+
+                //如果主播已经进入了房间 就判断是否退出过房间
+                if (Objects.nonNull(speakerInfo.getExitRoomTime())) {
+                    //如果退出过房间 判断退出时间是否大于进入时间
+                    if (speakerInfo.getExitRoomTime().getTime() > speakerInfo.getJoinRoomTime().getTime()) {
+                        //如果退出时间大于进入时间,就将退出时间+expiredMinute分钟
+                        Date exitExpiredTime = DateUtil.addMinutes(speakerInfo.getExitRoomTime(), expiredMinute);
+                        if (now.getTime() >= exitExpiredTime.getTime()) {
+                            log.info("roomDestroy exitExpiredTime >>>> cache : {}", JSONObject.toJSONString(test(room.getRoomUid(), room.getSpeakerId())));
+                            roomDestroy(room);
+                        }
+                    }
+                }
+
+            }
+        }
+    }
+
+    /**
+     * 关闭房间-融云
+     * 获取所有直播间缓存数据并写入数据库后并清理缓存
+     *
+     * @param id 直播房间表id
+     */
+    @Override
+    public void roomDestroy(Integer id) {
+        ImLiveBroadcastRoom room = this.getById(id);
+        if (Objects.isNull(room)) {
+            return;
+        }
+        if (room.getLiveState() == 0) {
+            throw new BizException("直播未开始");
+        }
+        roomDestroy(room);
+    }
+
+    public void roomDestroy(ImLiveBroadcastRoom room) {
+        log.error("roomDestroy>>>> room : {}", JSONObject.toJSONString(room));
+        String roomUid = room.getRoomUid();
+        Integer speakerId = room.getSpeakerId();
+
+        try {
+            //向聊天室发自定义消息踢出所有人
+            ImRoomMessage message = new ImRoomMessage();
+            message.setFromUserId(speakerId.toString());
+            message.setToChatroomId(roomUid);
+            message.setObjectName(ImRoomMessage.FORCED_OFFLINE);
+            imFeignService.publishRoomMsg(message);
+            log.info("roomDestroy>>>> FORCED_OFFLINE {}", JSONObject.toJSONString(message));
+            //销毁直播间
+            imFeignService.destroyLiveRoom(roomUid);
+            log.info("roomDestroy>>>> destroyLiveRoom {}", JSONObject.toJSONString(message));
+        } catch (Exception e) {
+            log.error("roomDestroy>>>> errorMsg{}", e.getMessage(), e.getCause());
+        }
+
+        //获取所有直播间缓存数据并写入数据库后并清理缓存
+        insertAndCleanLiveData(roomUid, speakerId);
+        log.info("roomDestroy>>>> insertAndCleanLiveData {}", JSONObject.toJSONString(room));
+
+        //将房间状态改为已销毁
+        Date date = new Date();
+        room.setRoomState(2);
+        room.setLiveState(2);
+        Integer userId = -1;
+        try {
+            userId = getSysUser().getId();
+        } catch (Exception ignored) {
+        }
+        room.setUpdatedBy(userId);
+        room.setUpdatedTime(date);
+        room.setLiveEndTime(date);
+        this.updateById(room);
+    }
+
+    //获取该直播间所有数据写入数据库-并清理缓存
+    private void insertAndCleanLiveData(String roomUid, Integer speakerId) {
+        //总观看人数
+        int totalLookNum = 0;
+        //获取直播间所有人数据写入 im_live_broadcast_room_member
+        RMap<Integer, RoomUserInfoVo> roomTotalUserCache = redissonClient.getMap(LIVE_ROOM_TOTAL_USER_LIST.replace(ROOM_UID, roomUid));
+        if (roomTotalUserCache.isExists()) {
+            List<RoomUserInfoVo> roomTotalUser = new ArrayList<>(roomTotalUserCache.values());
+            List<ImLiveBroadcastRoomMember> memberList = new ArrayList<>();
+            roomTotalUser.forEach(v -> {
+                ImLiveBroadcastRoomMember member = new ImLiveBroadcastRoomMember();
+                member.setTenantId(v.getTenantId());
+                member.setRoomUid(roomUid);
+                member.setUserId(v.getUserId());
+                member.setJoinTime(v.getFirstJoinTime());
+                member.setTotalTime(v.getTotalViewTime());
+                memberList.add(member);
+            });
+            if (CollectionUtils.isNotEmpty(memberList)) {
+                liveBroadcastRoomMemberService.getDao().insertBatch(memberList);
+                totalLookNum = roomTotalUser.size();
+            }
+            //删除用户对应的直播间关系缓存
+            roomTotalUser.stream()
+                    .map(RoomUserInfoVo::getUserId)
+                    .filter(Objects::nonNull)
+                    .forEach(id -> redissonClient.getBucket(LIVE_USER_ROOM.replace(USER_ID, id.toString())).delete());
+            //删除直播间所有人数据
+            roomTotalUserCache.clear();
+        }
+
+        //获取直播点赞
+        int like = 0;
+        RBucket<Object> likeCache = redissonClient.getBucket(LIVE_ROOM_LIKE.replace(ROOM_UID, roomUid));
+        if (likeCache.isExists()) {
+            like = (int) likeCache.get();
+            //删除房间点赞数据
+            likeCache.delete();
+        }
+
+        //获取直播间主讲人信息 写入im_live_broadcast_room_data
+        RBucket<RoomSpeakerInfo> speakerCache = redissonClient.getBucket(LIVE_SPEAKER_INFO.replace(USER_ID, speakerId.toString()));
+        if (speakerCache.isExists()) {
+            ImLiveBroadcastRoomData liveData = new ImLiveBroadcastRoomData();
+            RoomSpeakerInfo speakerInfo = speakerCache.get();
+            liveData.setTenantId(speakerInfo.getTenantId());
+            liveData.setRoomUid(roomUid);
+            liveData.setLikeNum(like);
+            liveData.setTotalUserNum(totalLookNum);
+            liveData.setUpdatedTime(new Date());
+            liveData.setLiveTime(speakerInfo.getTotalLiveTime());
+            liveBroadcastRoomDataService.save(liveData);
+            //删除房间主讲人数据
+            speakerCache.delete();
+        }
+
+    }
+
+    /**
+     * 同步点赞数量
+     *
+     * @param roomUid 房间uid
+     * @param likeNum 点赞数
+     */
+    @Override
+    public void syncLike(String roomUid, Integer likeNum) {
+        redissonClient.getBucket(LIVE_ROOM_LIKE.replace(ROOM_UID, roomUid)).set(likeNum);
+    }
+
+    /**
+     * <p>主讲人处理进入和退出房间数据
+     * <p>观看者只处理退出房间数据
+     *
+     * @param userState 用户状态数据
+     */
+    @Override
+    public void opsRoom(List<ImUserState> userState) {
+        if (CollectionUtils.isEmpty(userState)) {
+            return;
+        }
+        userState.forEach(user -> {
+            log.info("opsRoom>>>> {}", JSONObject.toJSONString(user));
+            if (StringUtils.isBlank(user.getStatus())) {
+                return;
+            }
+            Date now = new Date();
+            //查询主讲人userId,若是主讲人
+            RBucket<RoomSpeakerInfo> speakerCache = redissonClient.getBucket(LIVE_SPEAKER_INFO.replace(USER_ID, user.getUserid()));
+            if (speakerCache.isExists()) {
+                RoomSpeakerInfo speakerInfo = speakerCache.get();
+                //主讲人进入房间
+                if (user.getStatus().equals("0")) {
+                    speakerInfo.setJoinRoomTime(now);
+                    log.info("opsRoom>>>> join speakerCache {}", JSONObject.toJSONString(speakerInfo));
+                    speakerCache.set(speakerInfo);
+                    return;
+                }
+                //主讲人退出房间关闭录像
+                closeLive(speakerInfo);
+                speakerInfo.setExitRoomTime(now);
+                log.info("opsRoom>>>> exit speakerCache {}", JSONObject.toJSONString(speakerInfo));
+                speakerCache.set(speakerInfo);
+                return;
+            }
+            //未查询到主讲人信息,观看者只接受退出消息 status=0 是进入房间
+            if (user.getStatus().equals("0")) {
+                return;
+            }
+            //获取当前用户所在房间的uid
+            RBucket<String> userRoom = redissonClient.getBucket(LIVE_USER_ROOM.replace(USER_ID, user.getUserid()));
+            if (!userRoom.isExists()) {
+                return;
+            }
+            String roomUid = userRoom.get();
+            Integer userId = Integer.valueOf(user.getUserid());
+
+            //从房间累计用户信息中查询该用户的信息
+            RMap<Integer, RoomUserInfoVo> roomTotalUser = redissonClient.getMap(LIVE_ROOM_TOTAL_USER_LIST.replace(ROOM_UID, roomUid));
+            //该房间未查询到用户数据则不处理
+            if (!roomTotalUser.isExists() && !roomTotalUser.containsKey(userId)) {
+                return;
+            }
+            //查询到用户数据
+            RoomUserInfoVo userInfo = roomTotalUser.get(userId);
+            //用户是在房间的状态 并且 突然离线 - 那么融云会发送用户离线消息-此刻就发送退出房间消息给主讲人
+            if (userInfo.getState() == 0 && user.getStatus().equals("1")) {
+                ImRoomMessage message = new ImRoomMessage();
+                message.setFromUserId(userId.toString());
+                message.setToChatroomId(roomUid);
+                message.setObjectName(ImRoomMessage.LOOKER_LOGIN_OUT);
+                try {
+                    imFeignService.publishRoomMsg(message);
+                } catch (Exception e) {
+                    log.error("opsRoom>>>>  looker error {}", e.getMessage());
+                    log.error("opsRoom>>>>  looker error sendMessage {} : LOOKER_LOGIN_OUT : {}", message, JSONObject.toJSONString(userInfo));
+                }
+                log.info("opsRoom>>>> looker LOOKER_LOGIN_OUT : {}", JSONObject.toJSONString(userInfo));
+            }
+            //每次退出房间计算当前用户观看时长
+            int minutesBetween = getMinutesBetween(userInfo.getDynamicJoinTime(), now);
+            userInfo.setTotalViewTime(userInfo.getTotalViewTime() + minutesBetween);
+            //记录退出时间 并写入缓存
+            userInfo.setLastOutTime(now);
+            userInfo.setState(1);
+            roomTotalUser.fastPut(userId, userInfo);
+            log.info("opsRoom>>>> looker userInfo: {}", JSONObject.toJSONString(userInfo));
+        });
+
+    }
+
+    /**
+     * 主讲人登录专用
+     *
+     * @param roomUid
+     */
+    public ImLiveBroadcastRoomVo speakerJoinRoom(String roomUid) {
+        SysUser sysUser = getSysUser();
+        ImLiveBroadcastRoomVo roomVo = queryRoomAndCheck(roomUid, sysUser);
+        Integer userId = sysUser.getId();
+        if (!userId.equals(roomVo.getSpeakerId())) {
+            throw new BizException("您不是该直播间的主讲人");
+        }
+        Date now = new Date();
+        //获取直播间信息
+        RBucket<RoomSpeakerInfo> speakerCache = redissonClient.getBucket(LIVE_SPEAKER_INFO.replace(USER_ID, userId.toString()));
+        if (!speakerCache.isExists()) {
+            //没有主讲人信息则生成一个
+            createSpeakerInfo(this.getById(roomVo.getId()), sysUser);
+            speakerCache = redissonClient.getBucket(LIVE_SPEAKER_INFO.replace(USER_ID, userId.toString()));
+        }
+        RoomSpeakerInfo speakerInfo = speakerCache.get();
+        speakerInfo.setJoinRoomTime(now);
+        speakerCache.set(speakerInfo);
+        return roomVo;
+    }
+
+    /**
+     * 进入房间-修改用户对应的房间号信息
+     *
+     * @param roomUid 房间uid
+     * @param userId  用户id
+     */
+    public void joinRoom(String roomUid, Integer userId) {
+        //记录用户当前房间uid
+        redissonClient.getBucket(LIVE_USER_ROOM.replace(USER_ID, userId.toString())).set(roomUid);
+        //房间累计用户信息-指只要进入到该房间的用户都要记录
+        RMap<Integer, RoomUserInfoVo> roomTotalUser = redissonClient.getMap(LIVE_ROOM_TOTAL_USER_LIST.replace(ROOM_UID, roomUid));
+        //判断是否第一次进房间
+        RoomUserInfoVo userInfo;
+        Date now = new Date();
+        if (roomTotalUser.containsKey(userId)) {
+            //多次进入更新动态进入时间
+            userInfo = roomTotalUser.get(userId);
+        } else {
+            //第一次进该房间 写入用户首次进入时间
+            userInfo = getUserInfo(userId);
+            userInfo.setFirstJoinTime(now);
+            userInfo.setTotalViewTime(0);
+        }
+        userInfo.setState(0);//0 进入/在房间
+        userInfo.setDynamicJoinTime(now);
+        roomTotalUser.fastPut(userId, userInfo);
+        log.info("joinRoom>>>> userInfo: {}", JSONObject.toJSONString(userInfo));
+    }
+
+    /**
+     * 开始直播-录像
+     *
+     * @param roomUid 房间uid
+     */
+    public void startLive(String roomUid, Integer userId) {
+        //查询房间信息是否允许录像
+        RBucket<RoomSpeakerInfo> speakerCache = redissonClient.getBucket(LIVE_SPEAKER_INFO.replace(USER_ID, userId.toString()));
+        RoomSpeakerInfo roomSpeakerInfo = speakerCache.get();
+        if (Objects.nonNull(roomSpeakerInfo.getWhetherVideo()) && roomSpeakerInfo.getWhetherVideo() == 0) {
+            //开始录制视频
+            try {
+                imFeignService.startRecord(roomUid);
+            } catch (Exception e) {
+                log.error("startRecord error: {}", e.getMessage());
+            }
+        }
+        //开始直播
+        roomSpeakerInfo.setState(0);
+        roomSpeakerInfo.setStartLiveTime(new Date());
+        speakerCache.set(roomSpeakerInfo);
+        log.info("startLive>>>> roomSpeakerInfo: {}", JSONObject.toJSONString(roomSpeakerInfo));
+    }
+
+    /**
+     * 关闭直播-录像
+     *
+     * @param roomUid 房间uid
+     */
+    public void closeLive(String roomUid, Integer userId) {
+        //查询房间主播信息
+        RBucket<RoomSpeakerInfo> speakerCache = redissonClient.getBucket(LIVE_SPEAKER_INFO.replace(USER_ID, userId.toString()));
+        RoomSpeakerInfo roomSpeakerInfo = speakerCache.get();
+        //关闭直播
+        closeLive(roomSpeakerInfo);
+        speakerCache.set(roomSpeakerInfo);
+    }
+
+    private void closeLive(RoomSpeakerInfo roomSpeakerInfo) {
+        boolean stateFlag = Objects.nonNull(roomSpeakerInfo.getState()) && roomSpeakerInfo.getState() == 0;
+        if (Objects.nonNull(roomSpeakerInfo.getWhetherVideo()) && roomSpeakerInfo.getWhetherVideo() == 0
+                && stateFlag) {
+            //停止录制视频
+            try {
+                imFeignService.stopRecord(roomSpeakerInfo.getRoomUid());
+            } catch (Exception e) {
+                log.error("stopRecord error: {}", e.getMessage());
+            }
+        }
+        if (stateFlag) {
+            Date now = new Date();
+            roomSpeakerInfo.setEndLiveTime(now);
+            roomSpeakerInfo.setState(1);
+            //计算时长
+            int minutesBetween = getMinutesBetween(roomSpeakerInfo.getStartLiveTime(), now);
+            int i = Objects.isNull(roomSpeakerInfo.getTotalLiveTime()) ? 0 : roomSpeakerInfo.getTotalLiveTime();
+            roomSpeakerInfo.setTotalLiveTime(i + minutesBetween);
+        }
+        log.info("closeLive>>>> roomSpeakerInfo: {}", JSONObject.toJSONString(roomSpeakerInfo));
+    }
+
+    /**
+     * 定时任务,每分钟执行
+     * 提前30分钟主动去融云注册并创建房间
+     */
+    public void createLiveRoom() {
+        Date now = new Date();
+        Date endTime = DateUtil.addMinutes(now, 30);
+        List<ImLiveBroadcastRoom> list = this.list(new WrapperUtil<ImLiveBroadcastRoom>()
+                .hasEq("live_state_", 0)
+                .hasEq("room_state_", 0)
+                .hasGe("live_start_time_", now)
+                .hasLe("live_start_time_", endTime)
+                .queryWrapper());
+        if (CollectionUtils.isEmpty(list)) {
+            return;
+        }
+        list.forEach(this::createLiveRoom);
+    }
+
+    /**
+     * 去融云创建房间
+     */
+    private void createLiveRoom(ImLiveBroadcastRoom room) {
+        log.info("createLiveRoom>>>>>>roomUid:{}", room.getRoomUid());
+        try {
+            //生成主讲人信息到缓存
+            SysUser sysUser = getSysUser(room.getSpeakerId());
+            createSpeakerInfo(room, sysUser);
+            //去融云创建房间
+            imFeignService.createLiveRoom(room.getRoomUid(), room.getRoomTitle());
+        } catch (Exception e) {
+            log.error(">>>>>>>>>> createLiveRoom error roomUid:{} msg:{}", room.getRoomUid(), e.getMessage());
+        }
+    }
+
+    //生成主讲人信息
+    private void createSpeakerInfo(ImLiveBroadcastRoom room, SysUser sysUser) {
+        Date now = new Date();
+        RoomSpeakerInfo speakerInfo = new RoomSpeakerInfo();
+        speakerInfo.setSpeakerId(sysUser.getId());
+        speakerInfo.setSpeakerName(sysUser.getRealName());
+        speakerInfo.setCreateRoomTime(now);
+        speakerInfo.setRoomUid(room.getRoomUid());
+        speakerInfo.setTenantId(sysUser.getTenantId());
+        speakerInfo.setTotalLiveTime(0);
+
+        //查询房间信息是否允许录像
+        ImLiveBroadcastRoom one = this.getOne(new QueryWrapper<ImLiveBroadcastRoom>().eq("room_uid_", room.getRoomUid()));
+        boolean video = getRoomConfig(one.getRoomConfig())
+                .filter(c -> Objects.nonNull(c.getWhether_video()))
+                .map(c -> c.getWhether_video() == 0)
+                .orElse(false);
+        if (video) {
+            //可以录制视频
+            speakerInfo.setWhetherVideo(0);
+        } else {
+            //不可以录制视频
+            speakerInfo.setWhetherVideo(1);
+        }
+        //写入主讲人信息
+        redissonClient.getBucket(LIVE_SPEAKER_INFO.replace(USER_ID, room.getSpeakerId().toString())).set(speakerInfo);
+
+        //生成0点赞
+        redissonClient.getBucket(LIVE_ROOM_LIKE.replace(ROOM_UID, room.getRoomUid())).set(0);
+
+        //修改房间状态为开始
+        room.setLiveState(1);
+        room.setUpdatedTime(now);
+        room.setUpdatedBy(-2);
+        baseMapper.updateById(room);
+    }
+
+    private Optional<ImLiveBroadcastRoomDto.RoomConfig> getRoomConfig(String roomConfig) {
+        return Optional.ofNullable(roomConfig)
+                .map(c -> JSON.parseObject(c, ImLiveBroadcastRoomDto.RoomConfig.class));
+    }
+
+    private void getRoomData(ImLiveBroadcastRoomVo roomVo) {
+        //点赞数
+        Object like = redissonClient.getBucket(LIVE_ROOM_LIKE.replace(ROOM_UID, roomVo.getRoomUid())).get();
+        if (Objects.isNull(like)) {
+            like = 0;
+        }
+        roomVo.setLikeNum((int) like);
+        //累计总用户数量
+        List<RoomUserInfoVo> roomUserInfoVos = queryTotalRoomUserInfo(roomVo.getRoomUid());
+        if (CollectionUtils.isNotEmpty(roomUserInfoVos)) {
+            roomVo.setTotalLookNum(roomUserInfoVos.size());
+            //在房间观看用户数量
+            roomVo.setLookNum(queryRoomUserInfo(roomUserInfoVos).size());
+        } else {
+            roomVo.setTotalLookNum(0);
+            roomVo.setLookNum(0);
+        }
+
+    }
+
+    private SysUser getSysUser(Integer userId) {
+        return Optional.ofNullable(userId)
+                .map(sysUserFeignService::queryUserById)
+                .orElseThrow(() -> new BizException("用户不存在"));
+    }
+
+    private SysUser getSysUser() {
+        //修改机构基础信息
+        return Optional.ofNullable(sysUserFeignService.queryUserInfo())
+                .orElseThrow(() -> new BizException("用户不存在"));
+    }
+
+    /**
+     * 测试
+     */
+    public Map<String, Object> test(String roomUid, Integer userId) {
+        //test
+        Map<String, Object> result = new HashMap<>();
+        //点赞数
+        Object like = redissonClient.getBucket(LIVE_ROOM_LIKE.replace(ROOM_UID, roomUid)).get();
+        if (Objects.isNull(like)) {
+            like = 0;
+        }
+        result.put("点赞数", like);
+
+        int totalLook = 0;
+        int look = 0;
+        List<RoomUserInfoVo> inRoomUserInfo;
+
+        //累计总观看的用户数量
+        List<RoomUserInfoVo> totalUserInfo = queryTotalRoomUserInfo(roomUid);
+        if (CollectionUtils.isNotEmpty(totalUserInfo)) {
+            //正在房间观看的用户数据
+            inRoomUserInfo = queryRoomUserInfo(totalUserInfo);
+            if (CollectionUtils.isNotEmpty(inRoomUserInfo)) {
+                look = inRoomUserInfo.size();
+                result.put("正在观看的人员信息", inRoomUserInfo);
+            } else {
+                result.put("正在观看的人员信息", "没有正在观看的人员数据");
+            }
+            totalLook = totalUserInfo.size();
+            result.put("总人员数据", totalUserInfo);
+        } else {
+            result.put("总人员数据", "没有人员数据");
+        }
+
+        //获取主讲人信息
+        RBucket<RoomSpeakerInfo> speakerCache = redissonClient.getBucket(LIVE_SPEAKER_INFO.replace(USER_ID, userId.toString()));
+        if (speakerCache.isExists()) {
+            result.put("主讲人信息", speakerCache.get());
+        } else {
+            result.put("主讲人信息", "主讲人信息不存在");
+        }
+        result.put("总观看人数", totalLook);
+        result.put("实时观看数", look);
+        return result;
+    }
+
+    @Override
+    public void shareGroup(String roomUid, String groupIds) {
+        ImLiveBroadcastRoomVo imLiveBroadcastRoomVo = queryRoomInfo(roomUid);
+        if (imLiveBroadcastRoomVo == null) {
+            throw new BizException("直播间信息异常!");
+        }
+        SysUser sysUser = sysUserFeignService.queryUserInfo();
+        String baseApiUrl = sysConfigDao.findConfigValue(SysConfigService.BASE_API_URL);
+        StringBuffer pushUrl = new StringBuffer(baseApiUrl).append("/#/liveClassTransfer?roomUid=").append(roomUid);
+        sysMessageService.batchSendImGroupMessage(MessageTypeEnum.IM_SHARE_LIVE_URL, sysUser.getId().toString(), null, groupIds.split(","), null,
+                imLiveBroadcastRoomVo.getTenantName(), imLiveBroadcastRoomVo.getRoomTitle(), imLiveBroadcastRoomVo.getSpeakerName(),
+                DateUtil.format(imLiveBroadcastRoomVo.getLiveStartTime(), DateUtil.CHINESE_DATA_FORMAT_1),
+                imLiveBroadcastRoomVo.getLiveRemark(), HttpUtil.getSortUrl(pushUrl.toString()));
+    }
+
+    /**
+     * 查询在观看直播的用户信息
+     *
+     * @param roomUid 直播间uid
+     */
+    @Override
+    public List<RoomUserInfoVo> queryRoomUserInfo(String roomUid) {
+        List<RoomUserInfoVo> roomUserInfoVos = queryTotalRoomUserInfo(roomUid);
+        return queryRoomUserInfo(roomUserInfoVos);
+    }
+
+    public List<RoomUserInfoVo> queryRoomUserInfo(List<RoomUserInfoVo> totalUserInfo) {
+        return totalUserInfo.stream()
+                .filter(o -> Objects.nonNull(o.getState()) && o.getState() == 0)
+                .collect(Collectors.toList());
+    }
+
+    public List<RoomUserInfoVo> queryTotalRoomUserInfo(String roomUid) {
+        RMap<Integer, RoomUserInfoVo> roomTotalUser = redissonClient.getMap(LIVE_ROOM_TOTAL_USER_LIST.replace(ROOM_UID, roomUid));
+        return roomTotalUser.values().stream()
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+    }
+
+    private RoomUserInfoVo getUserInfo(Integer userId) {
+        RoomUserInfoVo userInfo = new RoomUserInfoVo();
+        userInfo.setUserId(userId);
+        SysUser sysUser = sysUserFeignService.queryUserById(userId);
+        String name = userId.toString();
+        if (Objects.nonNull(sysUser)) {
+            name = sysUser.getPhone();
+            if (sysUser.getUserType().contains("STUDENT")) {
+                name = sysUser.getUsername();
+            } else {
+                if (StringUtils.isNotBlank(sysUser.getRealName())) {
+                    name = sysUser.getRealName();
+                }
+            }
+        }
+        userInfo.setUserName(name);
+        userInfo.setTenantId(sysUser.getTenantId());
+        return userInfo;
+    }
+
+    //计算时间差-分钟数不满一分钟为0
+    private int getMinutesBetween(Date startDT, Date endDT) {
+        if (Objects.isNull(startDT) || Objects.isNull(endDT)) {
+            return 0;
+        }
+        //课程结束时间-课程开始时间
+        long durationTime = endDT.getTime() - startDT.getTime();
+        //相差多少分钟
+        return new Long(durationTime / 1000 / 60).intValue();
+    }
+
+    /**
+     * 主讲人信息
+     */
+    public static class RoomSpeakerInfo implements Serializable {
+        //主讲人id
+        private Integer speakerId;
+        //主讲人名称
+        private String speakerName;
+        //直播状态 0 直播中 1关闭直播
+        private Integer state;
+        //房间uid
+        private String roomUid;
+        //房间创建时间
+        private Date createRoomTime;
+        //进入房间时间
+        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+        private Date joinRoomTime;
+        //开始直播时间
+        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+        private Date startLiveTime;
+        //开始直播时间
+        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+        private Date endLiveTime;
+        //退出房间时间
+        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+        private Date exitRoomTime;
+        //总直播时间
+        private Integer totalLiveTime = 0;
+        //是否录像 0允许 1不允许
+        private Integer whetherVideo;
+        //机构
+        private Integer tenantId;
+
+        public Integer getSpeakerId() {
+            return speakerId;
+        }
+
+        public void setSpeakerId(Integer speakerId) {
+            this.speakerId = speakerId;
+        }
+
+        public String getSpeakerName() {
+            return speakerName;
+        }
+
+        public void setSpeakerName(String speakerName) {
+            this.speakerName = speakerName;
+        }
+
+        public Integer getState() {
+            return state;
+        }
+
+        public void setState(Integer state) {
+            this.state = state;
+        }
+
+        public String getRoomUid() {
+            return roomUid;
+        }
+
+        public void setRoomUid(String roomUid) {
+            this.roomUid = roomUid;
+        }
+
+        public Date getJoinRoomTime() {
+            return joinRoomTime;
+        }
+
+        public void setJoinRoomTime(Date joinRoomTime) {
+            this.joinRoomTime = joinRoomTime;
+        }
+
+        public Date getStartLiveTime() {
+            return startLiveTime;
+        }
+
+        public void setStartLiveTime(Date startLiveTime) {
+            this.startLiveTime = startLiveTime;
+        }
+
+        public Integer getTotalLiveTime() {
+            return totalLiveTime;
+        }
+
+        public void setTotalLiveTime(Integer totalLiveTime) {
+            this.totalLiveTime = totalLiveTime;
+        }
+
+        public Date getCreateRoomTime() {
+            return createRoomTime;
+        }
+
+        public void setCreateRoomTime(Date createRoomTime) {
+            this.createRoomTime = createRoomTime;
+        }
+
+        public Date getExitRoomTime() {
+            return exitRoomTime;
+        }
+
+        public void setExitRoomTime(Date exitRoomTime) {
+            this.exitRoomTime = exitRoomTime;
+        }
+
+        public Integer getWhetherVideo() {
+            return whetherVideo;
+        }
+
+        public void setWhetherVideo(Integer whetherVideo) {
+            this.whetherVideo = whetherVideo;
+        }
+
+        public Integer getTenantId() {
+            return tenantId;
+        }
+
+        public void setTenantId(Integer tenantId) {
+            this.tenantId = tenantId;
+        }
+
+        public Date getEndLiveTime() {
+            return endLiveTime;
+        }
+
+        public void setEndLiveTime(Date endLiveTime) {
+            this.endLiveTime = endLiveTime;
+        }
+    }
+
+}
+

+ 44 - 0
mec-biz/src/main/java/com/ym/mec/biz/service/impl/ImLiveRoomVideoServiceImpl.java

@@ -0,0 +1,44 @@
+package com.ym.mec.biz.service.impl;
+
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ym.mec.biz.dal.dao.ImLiveRoomVideoDao;
+import com.ym.mec.biz.dal.entity.ImLiveRoomVideo;
+import com.ym.mec.biz.service.ImLiveRoomVideoService;
+import com.ym.mec.common.page.PageInfo;
+import com.ym.mec.common.page.PageUtil;
+import com.ym.mec.common.page.WrapperUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 直播视频记录(ImLiveRoomVideo)表服务实现类
+ *
+ * @author hgw
+ * @since 2022-03-07 19:06:55
+ */
+@Service("imLiveRoomVideoService")
+public class ImLiveRoomVideoServiceImpl extends ServiceImpl<ImLiveRoomVideoDao, ImLiveRoomVideo> implements ImLiveRoomVideoService {
+
+    private final static Logger log = LoggerFactory.getLogger(ImLiveRoomVideoServiceImpl.class);
+
+    @Override
+    public ImLiveRoomVideoDao getDao() {
+        return this.baseMapper;
+    }
+
+    public List<ImLiveRoomVideo> queryList(String roomUid) {
+        return baseMapper.selectList(new WrapperUtil<ImLiveRoomVideo>()
+                .hasEq("room_uid_",roomUid)
+                .hasEq("type",2).queryWrapper()
+        );
+    }
+
+
+}
+

+ 1 - 3
mec-biz/src/main/java/com/ym/mec/biz/service/impl/IndexErrDataRecordServiceImpl.java

@@ -83,9 +83,7 @@ public class IndexErrDataRecordServiceImpl extends BaseServiceImpl<Long, IndexEr
 		}
 		Date dt = DateUtil.stringToDate(date, "yyyy-MM-dd");
 		
-		Integer tenantId = TenantContextHolder.getTenantId();
-
-		int errDateNum = indexErrDataRecordDao.countWithGenerateTime(date, tenantId);
+		int errDateNum = indexErrDataRecordDao.countWithGenerateTime(date);
 
 		if(errDateNum<=0){
 			//乐团巡查事项异常

+ 16 - 4
mec-biz/src/main/java/com/ym/mec/biz/service/impl/MusicGroupServiceImpl.java

@@ -50,6 +50,7 @@ import java.util.*;
 import java.util.stream.Collectors;
 
 import static com.ym.mec.biz.dal.entity.ApprovalStatus.PROCESSING;
+import static com.ym.mec.biz.dal.enums.DealStatusEnum.ING;
 import static com.ym.mec.biz.dal.enums.DealStatusEnum.SUCCESS;
 import static com.ym.mec.biz.dal.enums.OrderDetailTypeEnum.*;
 import static com.ym.mec.biz.dal.enums.PaymentStatusEnum.YES;
@@ -567,9 +568,11 @@ public class MusicGroupServiceImpl extends BaseServiceImpl<String, MusicGroup> i
         //关闭之前的订单
         if (calenderDetail.getPaymentOrderId() != null) {
             StudentPaymentOrder oldStudentPaymentOrder = studentPaymentOrderDao.get(calenderDetail.getPaymentOrderId());
-            HttpResponseResult result = studentPaymentOrderService.checkRepeatPay(oldStudentPaymentOrder, renewParamDto.getRepeatPay());
-            if (result.getCode() != 200) {
-                return result;
+            if(oldStudentPaymentOrder.getStatus() == ING){
+                HttpResponseResult result = studentPaymentOrderService.checkRepeatPay(oldStudentPaymentOrder, renewParamDto.getRepeatPay());
+                if (result.getCode() != 200) {
+                    return result;
+                }
             }
         }
 
@@ -3711,7 +3714,16 @@ public class MusicGroupServiceImpl extends BaseServiceImpl<String, MusicGroup> i
             }
             //退用户余额
             if (userReBack.compareTo(BigDecimal.ZERO) > 0) {
-                sysUserCashAccountService.updateBalance(musicGroupReg.getUserId(), userReBack, PlatformCashAccountDetailTypeEnum.REFUNDS, "关闭乐团");
+                SysUserCashAccountLog sysUserCashAccountLog = new SysUserCashAccountLog();
+                sysUserCashAccountLog.setGroupType(GroupType.MUSIC);
+                sysUserCashAccountLog.setUserId(musicGroupReg.getUserId());
+                sysUserCashAccountLog.setOrganId(musicGroup.getOrganId());
+                sysUserCashAccountLog.setGroupId(musicGroup.getId());
+                sysUserCashAccountLog.setAmount(userReBack);
+                sysUserCashAccountLog.setReturnFeeType(ReturnFeeEnum.MUSIC);
+                sysUserCashAccountLog.setComment("关闭乐团");
+                sysUserCashAccountLogDao.insert(sysUserCashAccountLog);
+//                sysUserCashAccountService.updateBalance(musicGroupReg.getUserId(), userReBack, PlatformCashAccountDetailTypeEnum.REFUNDS, "关闭乐团");
             }
             //清除剩余课程费用
             if (musicGroupReg.getSurplusCourseFee().compareTo(BigDecimal.ZERO) > 0) {

+ 3 - 1
mec-biz/src/main/java/com/ym/mec/biz/service/impl/PayServiceImpl.java

@@ -126,9 +126,11 @@ public class PayServiceImpl implements PayService {
 	        		throw new BizException("平台收款账户查询失败");
 	        	}
 	        	
+	        	Integer routeOrganId = 68;
+	        	
 	        	StudentPaymentRouteOrder studentPaymentRouteOrder = new StudentPaymentRouteOrder();
 	            studentPaymentRouteOrder.setOrderNo(orderNo);
-	            studentPaymentRouteOrder.setRouteOrganId(organId);
+	            studentPaymentRouteOrder.setRouteOrganId(routeOrganId);
 	            studentPaymentRouteOrder.setFeeFlag("Y");
 	            studentPaymentRouteOrder.setRouteAmount(amount);
 	            studentPaymentRouteOrder.setRouteBalanceAmount(balanceAmount);

+ 45 - 0
mec-biz/src/main/resources/config/mybatis/ImLiveBroadcastRoomDataMapper.xml

@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ym.mec.biz.dal.dao.ImLiveBroadcastRoomDataDao">
+    <resultMap id="BaseResultMap" type="com.ym.mec.biz.dal.entity.ImLiveBroadcastRoomData">
+        <id column="id_" jdbcType="INTEGER" property="id"/>
+        <result column="tenant_id_" jdbcType="INTEGER" property="tenantId"/>
+        <result column="room_uid_" jdbcType="VARCHAR" property="roomUid"/>
+        <result column="like_num_" jdbcType="INTEGER" property="likeNum"/>
+        <result column="look_user_num_" jdbcType="INTEGER" property="lookUserNum"/>
+        <result column="total_user_num_" jdbcType="INTEGER" property="totalUserNum"/>
+        <result column="updated_time_" jdbcType="TIMESTAMP" property="updatedTime"/>
+        <result column="live_time_" jdbcType="INTEGER" property="liveTime"/>
+    </resultMap>
+
+    <sql id="Base_Column_List">
+        id_
+        , tenant_id_, room_uid_, like_num_, look_user_num_, total_user_num_, updated_time_, live_time_
+    </sql>
+
+    <insert id="insertBatch" keyColumn="id_" keyProperty="id" useGeneratedKeys="true"
+            parameterType="com.ym.mec.biz.dal.entity.ImLiveBroadcastRoomData">
+        insert into im_live_broadcast_room_data(tenant_id_, room_uid_, like_num_, look_user_num_, total_user_num_,
+        updated_time_, live_time_)
+        values
+        <foreach collection="entities" item="entity" separator=",">
+            (#{entity.tenantId}, #{entity.roomUid}, #{entity.likeNum}, #{entity.lookUserNum}, #{entity.totalUserNum},
+            #{entity.updatedTime}, #{entity.liveTime})
+        </foreach>
+    </insert>
+
+    <select id="queryByRoomUid" resultType="com.ym.mec.biz.dal.vo.ImLiveBroadcastRoomDetailVo">
+        select a.room_uid_                 as roomUid,
+               r.room_title_               as roomTitle,
+               b.real_name_                AS speakerName,
+               r.live_remark_              AS liveRemark,
+               ifnull(a.total_user_num_, 0) AS totalLookNum,
+               ifnull(a.like_num_, 0)      as totalLikeNum,
+               ifnull(a.live_time_, 0)     as totalLiveTime
+        from im_live_broadcast_room_data as a
+                 left join im_live_broadcast_room AS r on a.room_uid_ = r.room_uid_
+                 left join sys_user AS b on r.speaker_id_ = b.id_
+        where a.room_uid_ = #{roomUid}
+    </select>
+
+</mapper>

+ 92 - 0
mec-biz/src/main/resources/config/mybatis/ImLiveBroadcastRoomMapper.xml

@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ym.mec.biz.dal.dao.ImLiveBroadcastRoomDao">
+    <resultMap id="BaseResultMap" type="com.ym.mec.biz.dal.entity.ImLiveBroadcastRoom">
+        <id column="id_" jdbcType="INTEGER" property="id"/>
+        <result column="tenant_id_" jdbcType="INTEGER" property="tenantId"/>
+        <result column="speaker_id_" jdbcType="INTEGER" property="speakerId"/>
+        <result column="room_uid_" jdbcType="VARCHAR" property="roomUid"/>
+        <result column="room_title_" jdbcType="VARCHAR" property="roomTitle"/>
+        <result column="live_start_time_" jdbcType="TIMESTAMP" property="liveStartTime"/>
+        <result column="live_end_time_" jdbcType="TIMESTAMP" property="liveEndTime"/>
+        <result column="live_remark_" jdbcType="VARCHAR" property="liveRemark"/>
+        <result column="pre_template_" jdbcType="VARCHAR" property="preTemplate"/>
+        <result column="room_config_" jdbcType="VARCHAR" property="roomConfig"/>
+        <result column="live_state_" jdbcType="INTEGER" property="liveState"/>
+        <result column="room_state_" jdbcType="INTEGER" property="roomState"/>
+        <result column="created_by_" jdbcType="INTEGER" property="createdBy"/>
+        <result column="created_time_" jdbcType="TIMESTAMP" property="createdTime"/>
+        <result column="updated_by_" jdbcType="INTEGER" property="updatedBy"/>
+        <result column="updated_time_" jdbcType="TIMESTAMP" property="updatedTime"/>
+    </resultMap>
+
+    <sql id="Base_Column_List">
+        id_
+        , tenant_id_, speaker_id_, room_uid_, room_title_, live_start_time_, live_end_time_, live_remark_, pre_template_, room_config_, live_state_, room_state_, created_by_, created_time_, updated_by_, updated_time_
+    </sql>
+
+    <insert id="insertBatch" keyColumn="id_" keyProperty="id" useGeneratedKeys="true"
+            parameterType="com.ym.mec.biz.dal.entity.ImLiveBroadcastRoom">
+        insert into im_live_broadcast_room(tenant_id_, speaker_id_, room_uid_, room_title_, live_start_time_,
+        live_end_time_, live_remark_, pre_template_, room_config_, live_state_, room_state_, created_by_, created_time_,
+        updated_by_, updated_time_)
+        values
+        <foreach collection="entities" item="entity" separator=",">
+            (#{entity.tenantId}, #{entity.speakerId}, #{entity.roomUid}, #{entity.roomTitle}, #{entity.liveStartTime},
+            #{entity.liveEndTime}, #{entity.liveRemark}, #{entity.preTemplate}, #{entity.roomConfig},
+            #{entity.liveState}, #{entity.roomState}, #{entity.createdBy}, #{entity.createdTime}, #{entity.updatedBy},
+            #{entity.updatedTime})
+        </foreach>
+    </insert>
+
+    <select id="queryPage" resultType="com.ym.mec.biz.dal.vo.ImLiveBroadcastRoomVo">
+        select a.id_ AS id,
+        a.tenant_id_ AS tenantId,
+        t.name_ AS tenantName,
+        t.logo_ AS tenantLogo,
+        a.room_uid_ AS roomUid,
+        a.room_title_ AS roomTitle,
+        a.live_remark_ AS liveRemark,
+        a.speaker_id_ AS speakerId,
+        b.real_name_ AS speakerName,
+        b.im_token_ as imToken,
+        b.avatar_ AS speakerPic,
+        a.live_start_time_ AS liveStartTime,
+        a.live_state_ AS liveState,
+        a.room_state_ AS roomState,
+        c.real_name_ AS createdByName,
+        a.pre_template_ AS preTemplate,
+        a.room_config_ AS roomConfig
+        from im_live_broadcast_room as a
+        left join tenant_info AS t on a.tenant_id_ = t.id_
+        left join sys_user AS b on a.speaker_id_ = b.id_
+        left join sys_user AS c on a.created_by_ = c.id_
+        <where>
+            a.room_state_ in(0, 2)
+            <if test="param.search != null ">
+                AND (
+                a.`id_` LIKE CONCAT('%', #{param.search},'%')
+                OR a.`room_title_` LIKE CONCAT('%', #{param.search},'%')
+                )
+            </if>
+            <if test="param.tenantId != null ">
+                AND a.tenant_id_ = #{param.tenantId}
+            </if>
+            <if test="param.roomUid != null">
+                and a.room_uid_ = #{param.roomUid}
+            </if>
+            <if test="param.liveState != null">
+                and a.live_state_ = #{param.liveState}
+            </if>
+            <if test="param.startTime != null">
+                <![CDATA[ AND a.live_start_time_  >= #{param.startTime} ]]>
+            </if>
+            <if test="param.endTime != null">
+                <![CDATA[ AND a.live_start_time_  <= #{param.endTime} ]]>
+            </if>
+
+        </where>
+
+    </select>
+
+</mapper>

+ 50 - 0
mec-biz/src/main/resources/config/mybatis/ImLiveBroadcastRoomMemberMapper.xml

@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ym.mec.biz.dal.dao.ImLiveBroadcastRoomMemberDao">
+    <resultMap id="BaseResultMap" type="com.ym.mec.biz.dal.entity.ImLiveBroadcastRoomMember">
+        <id column="id_" jdbcType="INTEGER" property="id"/>
+        <result column="tenant_id_" jdbcType="INTEGER" property="tenantId"/>
+        <result column="room_uid_" jdbcType="VARCHAR" property="roomUid"/>
+        <result column="user_id_" jdbcType="INTEGER" property="userId"/>
+        <result column="join_time_" jdbcType="TIMESTAMP" property="joinTime"/>
+        <result column="total_time_" jdbcType="INTEGER" property="totalTime"/>
+    </resultMap>
+
+    <sql id="Base_Column_List">
+        id
+        , tenant_id_, room_uid_, user_id_, join_time_, total_time_
+    </sql>
+
+    <insert id="insertBatch" keyColumn="id_" keyProperty="id" useGeneratedKeys="true"
+            parameterType="com.ym.mec.biz.dal.entity.ImLiveBroadcastRoomMember">
+        insert into im_live_broadcast_room_member(tenant_id_, room_uid_, user_id_, join_time_, total_time_)
+        values
+        <foreach collection="entities" item="entity" separator=",">
+            (#{entity.tenantId}, #{entity.roomUid}, #{entity.userId}, #{entity.joinTime}, #{entity.totalTime})
+        </foreach>
+    </insert>
+
+    <select id="queryMemberPage" resultType="com.ym.mec.biz.dal.vo.ImLiveBroadcastRoomMemberVo">
+        select i.room_uid_ as roomUid,
+        i.room_title_ as roomTitle,
+        a.user_id_ as studentId,
+        su.username_ as studentName,
+        su.phone_ as phone,
+        b.name_ as subName,
+        a.join_time_ as joinTime,
+        a.total_time_ as totalViewTime
+        from im_live_broadcast_room_member as a
+        left join im_live_broadcast_room as i on a.room_uid_ = i.room_uid_
+        left join sys_user as su on su.id_ = a.user_id_
+        left join student st on su.id_ = st.user_id_
+        left join subject as b on st.subject_id_list_ = b.id_
+        where a.room_uid_ = #{param.roomUid}
+        <if test="param.search != null ">
+            AND (
+            a.user_id_ LIKE CONCAT('%', #{param.search},'%')
+            OR su.username_ LIKE CONCAT('%', #{param.search},'%')
+            )
+        </if>
+    </select>
+
+</mapper>

+ 31 - 0
mec-biz/src/main/resources/config/mybatis/ImLiveRoomVideoMapper.xml

@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.ym.mec.biz.dal.dao.ImLiveRoomVideoDao">
+    <resultMap id="BaseResultMap" type="com.ym.mec.biz.dal.entity.ImLiveRoomVideo">
+        <id column="id_" jdbcType="INTEGER" property="id"/>
+        <result column="room_uid_" jdbcType="VARCHAR" property="roomUid"/>
+        <result column="record_id_" jdbcType="VARCHAR" property="recordId"/>
+        <result column="url_" jdbcType="VARCHAR" property="url"/>
+        <result column="start_time_" jdbcType="TIMESTAMP" property="startTime"/>
+        <result column="end_time_" jdbcType="TIMESTAMP" property="endTime"/>
+        <result column="type" jdbcType="INTEGER" property="type"/>
+        <result column="created_time_" jdbcType="TIMESTAMP" property="createdTime"/>
+    </resultMap>
+
+    <sql id="Base_Column_List">
+        id_
+        , room_uid_, record_id_, url_, start_time_, end_time_, type, created_time_
+    </sql>
+
+    <insert id="insertBatch" keyColumn="id_" keyProperty="id" useGeneratedKeys="true"
+            parameterType="com.ym.mec.biz.dal.entity.ImLiveRoomVideo">
+        insert into im_live_room_video(room_uid_, record_id_, url_, start_time_, end_time_, type,
+        created_time_)
+        values
+        <foreach collection="entities" item="entity" separator=",">
+            (#{entity.roomUid}, #{entity.recordId}, #{entity.url}, #{entity.startTime},
+            #{entity.endTime}, #{entity.type}, #{entity.createdTime})
+        </foreach>
+    </insert>
+
+</mapper>

+ 1 - 1
mec-biz/src/main/resources/config/mybatis/IndexErrDataRecordMapper.xml

@@ -126,7 +126,7 @@
 	</select>
 
 	<select id="countWithGenerateTime" resultType="int">
-		SELECT COUNT(id_) FROM index_err_data_record WHERE generate_time_=#{generateTime} and tenant_id_ = #{tenantId}
+		SELECT COUNT(id_) FROM index_err_data_record WHERE generate_time_=#{generateTime}
 	</select>
 
 	<select id="getWithGenerateTime" resultMap="IndexErrDataRecord">

+ 1 - 1
mec-biz/src/main/resources/config/mybatis/MusicGroupPaymentCalenderDetailMapper.xml

@@ -554,7 +554,7 @@
 		CASE WHEN mgpcd.start_payment_date_ IS NULL THEN mgpc.start_payment_date_ ELSE mgpcd.start_payment_date_ END start_payment_date_mgpc_,
 		CASE WHEN mgpcd.deadline_payment_date_ IS NULL THEN mgpc.deadline_payment_date_ ELSE mgpcd.deadline_payment_date_ END deadline_payment_date_mgpc_,
 		CASE WHEN mgpc.status_ = 'OPEN' OR mgpcd.open_ = 1 THEN 1 ELSE 0 END open_,mgpc.payment_type_,mgpcd.payment_order_id_,mgpcd.payment_status_,mgpcd.pay_time_,
-		mgpcd.music_group_payment_calender_id_
+		mgpcd.music_group_payment_calender_id_,mgpc.current_total_amount_ expect_amount_
 		FROM music_group_payment_calender_detail mgpcd
 		LEFT JOIN sys_user su ON su.id_ = mgpcd.user_id_
 		LEFT JOIN student_registration sr ON sr.user_id_ = mgpcd.user_id_

+ 2 - 1
mec-biz/src/main/resources/config/mybatis/StudentManageDao.xml

@@ -78,6 +78,7 @@
         <result column="experience_membership_day_" property="experienceMembershipDay"/>
         <result column="experience_membership_end_time_" property="experienceMembershipEndTime"/>
         <result column="record_user_id_" property="recordUserId"/>
+        <result column="ext_subject_ids_" property="extSubjectIds"/>
     </resultMap>
 
     <update id="updateUser" parameterType="com.ym.mec.auth.api.entity.SysUser">
@@ -145,7 +146,7 @@
     </update>
 
     <select id="findStudentsByOrganId" resultMap="studentManageListDto">
-        SELECT o.`name_` organ_name_,o.grade_type_,s.`user_id_` ,su.`username_` ,su.`phone_` parents_phone_,
+        SELECT o.`name_` organ_name_,o.grade_type_,s.`user_id_` ,su.`username_` ,su.`phone_` parents_phone_,s.ext_subject_ids_,
         sut.name_ real_name_,su.`gender_` , su.organ_id_,
 		tu.`real_name_` teacher_name_,CASE s.service_tag_ WHEN 2 THEN 0 ELSE s.service_tag_ END service_tag_ ,s.`operating_tag_` ,
         s.care_package_, s.come_on_package_, suca.`course_balance_` ,suca.balance_,

+ 9 - 2
mec-biz/src/main/resources/config/mybatis/StudentMapper.xml

@@ -35,6 +35,7 @@
         <result column="phone_" property="phone"/>
         <result column="username_" property="username"/>
         <result column="count_flag_" property="countFlag"/>
+        <result column="ext_subject_ids_" property="extSubjectIds"/>
         <result column="tenant_id_" property="tenantId"/>
     </resultMap>
 
@@ -88,7 +89,7 @@
         </if>
         teacher_id_,create_time_,update_time_,service_tag_update_time_,cooperation_organ_id_,
         care_package_,come_on_package_,member_rank_setting_id_,membership_start_time_,
-        membership_end_time_,current_grade_num_,current_class_,tenant_id_)
+        membership_end_time_,current_grade_num_,current_class_,ext_subject_ids_,tenant_id_)
         VALUES
         (#{userId},#{schoolName},#{subjectIdList},
         <if test="serviceTag != null">
@@ -99,7 +100,7 @@
         </if>
         #{teacherId},NOW(),NOW(),NOW(),#{cooperationOrganId},
          #{carePackage},#{comeOnPackage},#{memberRankSettingId},#{membershipStartTime},
-         #{membershipEndTime},#{currentGradeNum},#{currentClass},#{tenantId})
+         #{membershipEndTime},#{currentGradeNum},#{currentClass},#{extSubjectIds},#{tenantId})
     </insert>
 
     <update id="update" parameterType="com.ym.mec.biz.dal.entity.Student">
@@ -160,6 +161,9 @@
             <if test="activityCourseDetail != null">
                 activity_course_detail_=#{activityCourseDetail},
             </if>
+            <if test="extSubjectIds != null">
+                ext_subject_ids_=#{extSubjectIds},
+            </if>
                 cooperation_organ_id_=#{cooperationOrganId},
                 update_time_ = NOW()
         </set>
@@ -286,6 +290,9 @@
 	            <if test="item.membershipEndTime != null">
 	                membership_end_time_=#{item.membershipEndTime},
 	            </if>
+	            <if test="item.extSubjectIds != null">
+	                ext_subject_ids_=#{item.extSubjectIds},
+	            </if>
                 update_time_ = NOW()
             </set>
             WHERE user_id_ = #{item.userId} and tenant_id_ = #{item.tenantId}

+ 2 - 2
mec-biz/src/main/resources/config/mybatis/StudentPaymentRouteOrderMapper.xml

@@ -197,7 +197,7 @@
                     #{userId}
                 </foreach>
             </if>
-            <if test="organId != null">
+            <if test="organId != null and organId != ''">
                 AND FIND_IN_SET(spo.organ_id_,#{organId})
             </if>
             <if test="orderStartDate != null">
@@ -227,7 +227,7 @@
             <if test='orderType != null and orderType.toString()=="2".toString()'>
                 AND spo.per_amount_ > 0
             </if>
-            <if test="routingOrganId != null">
+            <if test="routingOrganId != null and routingOrganId != ''">
                 AND FIND_IN_SET(spro.route_organ_id_,#{routingOrganId})
             </if>
             <if test="actualAmount != null">

+ 160 - 109
mec-client-api/src/main/java/com/ym/mec/im/ImFeignService.java

@@ -1,121 +1,172 @@
 package com.ym.mec.im;
 
-import java.util.List;
-
+import com.ym.mec.common.config.FeignConfiguration;
+import com.ym.mec.common.entity.*;
+import com.ym.mec.im.entity.GroupModel;
+import com.ym.mec.im.fallback.ImFeignServiceFallback;
 import org.springframework.cloud.openfeign.FeignClient;
 import org.springframework.http.MediaType;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestBody;
-
-import com.ym.mec.common.config.FeignConfiguration;
-import com.ym.mec.common.entity.ImGroupMessage;
-import com.ym.mec.common.entity.ImGroupModel;
-import com.ym.mec.common.entity.ImPrivateMessage;
-import com.ym.mec.common.entity.ImResult;
-import com.ym.mec.common.entity.ImUserModel;
-import com.ym.mec.im.entity.GroupModel;
-import com.ym.mec.im.fallback.ImFeignServiceFallback;
 import org.springframework.web.bind.annotation.RequestParam;
 
+import java.util.List;
+
 @FeignClient(name = "im-server", configuration = FeignConfiguration.class, fallback = ImFeignServiceFallback.class)
 public interface ImFeignService {
 
-	/**
-	 * 用户注册
-	 * @param userModel
-	 * @return
-	 */
-	@PostMapping(value = "user/register", consumes = MediaType.APPLICATION_JSON_VALUE)
-	ImResult register(@RequestBody ImUserModel userModel);
-
-	/**
-	 * 用户信息修改
-	 * @param userModel
-	 * @return
-	 */
-	@PostMapping(value = "user/update", consumes = MediaType.APPLICATION_JSON_VALUE)
-	void update(@RequestBody ImUserModel userModel);
-
-	/**
-	 * 创建群组
-	 * @param groupModel
-	 * @return
-	 */
-	@PostMapping(value = "group/create")
-	Object groupCreate(@RequestBody GroupModel groupModel);
-
-	/**
-	 * 批量创建群组
-	 * @param groupModels
-	 * @return
-	 */
-	@PostMapping(value = "group/batchCreate")
-	void groupBatchCreate(@RequestBody List<GroupModel> groupModels);
-
-	/**
-	 * 加入群组
-	 * @param groupModel
-	 * @return
-	 */
-	@PostMapping(value = "group/join")
-	Object groupJoin(@RequestBody GroupModel groupModel);
-
-	/**
-	 * 批量加入群组
-	 * @param groupModels
-	 * @return
-	 */
-	@PostMapping(value = "group/batchJoin")
-	Object groupBatchJoin(@RequestBody List<GroupModel> groupModels);
-
-	/**
-	 * 退出群组
-	 * @param groupModel
-	 * @return
-	 */
-	@PostMapping(value = "group/quit")
-	Object groupQuit(@RequestBody GroupModel groupModel);
-
-	/**
-	 * 解散群组
-	 * @param groupModel
-	 * @return
-	 */
-	@PostMapping(value = "group/dismiss")
-	Object groupDismiss(@RequestBody GroupModel groupModel);
-
-	/**
-	 * 批量解散群组
-	 * @param groupModels
-	 * @return
-	 */
-	@PostMapping(value = "group/batchDismiss")
-	Object groupBatchDismiss(@RequestBody List<ImGroupModel> groupModels);
-
-	/**
-	 * 批量退出群组
-	 * @param groupModels
-	 * @return
-	 */
-	@PostMapping(value = "group/batchQuit")
-	Object groupBatchQuit(@RequestBody List<GroupModel> groupModels);
-
-	/**
-	 * 发送私聊消息
-	 * @param privateMessage
-	 * @return
-	 */
-	@PostMapping(value = "private/send")
-	Object privateSend(@RequestBody ImPrivateMessage privateMessage);
-
-	@PostMapping(value = "group/send")
-	Object groupSend(@RequestBody ImGroupMessage imGroupMessage);
-
-	/**
-	 * 获取历史消息记录
-	 * @param date
-	 * @return
-	 */
-	@PostMapping(value = "history/get")
-	Object historyGet(@RequestParam("date") String date);
+    /**
+     * 用户注册
+     *
+     * @param userModel
+     * @return
+     */
+    @PostMapping(value = "user/register", consumes = MediaType.APPLICATION_JSON_VALUE)
+    ImResult register(@RequestBody ImUserModel userModel);
+
+    /**
+     * 用户信息修改
+     *
+     * @param userModel
+     * @return
+     */
+    @PostMapping(value = "user/update", consumes = MediaType.APPLICATION_JSON_VALUE)
+    void update(@RequestBody ImUserModel userModel);
+
+    /**
+     * 创建群组
+     *
+     * @param groupModel
+     * @return
+     */
+    @PostMapping(value = "group/create")
+    Object groupCreate(@RequestBody GroupModel groupModel);
+
+    /**
+     * 批量创建群组
+     *
+     * @param groupModels
+     * @return
+     */
+    @PostMapping(value = "group/batchCreate")
+    void groupBatchCreate(@RequestBody List<GroupModel> groupModels);
+
+    /**
+     * 加入群组
+     *
+     * @param groupModel
+     * @return
+     */
+    @PostMapping(value = "group/join")
+    Object groupJoin(@RequestBody GroupModel groupModel);
+
+    /**
+     * 批量加入群组
+     *
+     * @param groupModels
+     * @return
+     */
+    @PostMapping(value = "group/batchJoin")
+    Object groupBatchJoin(@RequestBody List<GroupModel> groupModels);
+
+    /**
+     * 退出群组
+     *
+     * @param groupModel
+     * @return
+     */
+    @PostMapping(value = "group/quit")
+    Object groupQuit(@RequestBody GroupModel groupModel);
+
+    /**
+     * 解散群组
+     *
+     * @param groupModel
+     * @return
+     */
+    @PostMapping(value = "group/dismiss")
+    Object groupDismiss(@RequestBody GroupModel groupModel);
+
+    /**
+     * 批量解散群组
+     *
+     * @param groupModels
+     * @return
+     */
+    @PostMapping(value = "group/batchDismiss")
+    Object groupBatchDismiss(@RequestBody List<ImGroupModel> groupModels);
+
+    /**
+     * 批量退出群组
+     *
+     * @param groupModels
+     * @return
+     */
+    @PostMapping(value = "group/batchQuit")
+    Object groupBatchQuit(@RequestBody List<GroupModel> groupModels);
+
+    /**
+     * 发送私聊消息
+     *
+     * @param privateMessage
+     * @return
+     */
+    @PostMapping(value = "private/send")
+    Object privateSend(@RequestBody ImPrivateMessage privateMessage);
+
+    @PostMapping(value = "group/send")
+    Object groupSend(@RequestBody ImGroupMessage imGroupMessage);
+
+    /**
+     * 获取历史消息记录
+     *
+     * @param date
+     * @return
+     */
+    @PostMapping(value = "history/get")
+    Object historyGet(@RequestParam("date") String date);
+
+    /**
+     * 创建直播房间
+     *
+     * @param roomId   直播间uid
+     * @param roomName 直播间名称
+     */
+    @PostMapping(value = "/liveRoom/create")
+    Object createLiveRoom(@RequestParam("roomId") String roomId, @RequestParam("roomName") String roomName);
+
+    /**
+     * 销毁直播房间
+     *
+     * @param roomId 直播间uid
+     */
+    @PostMapping(value = "/liveRoom/destroy")
+    Object destroyLiveRoom(@RequestParam("roomId") String roomId);
+
+    /**
+    * @description: 录制直播
+     * @param roomId
+    * @return void
+    * @author zx
+    * @date 2022/2/25 13:52
+    */
+    @PostMapping(value = "/liveRoom/startRecord")
+    void startRecord(@RequestParam("roomId") String roomId);
+
+    /**
+    * @description: 结束录制直播
+     * @param roomId
+    * @return void
+    * @author zx
+    * @date 2022/2/25 13:52
+    */
+    @PostMapping(value = "/liveRoom/stopRecord")
+    void stopRecord(@RequestParam("roomId") String roomId);
+
+    /**
+     * 向直播间发送文本消息
+     */
+    @PostMapping(value = "/liveRoom/publishRoomMsg", consumes = MediaType.APPLICATION_JSON_VALUE)
+    Object publishRoomMsg(@RequestBody ImRoomMessage message);
+
 }

+ 26 - 5
mec-client-api/src/main/java/com/ym/mec/im/fallback/ImFeignServiceFallback.java

@@ -2,13 +2,9 @@ package com.ym.mec.im.fallback;
 
 import java.util.List;
 
+import com.ym.mec.common.entity.*;
 import org.springframework.stereotype.Component;
 
-import com.ym.mec.common.entity.ImGroupMessage;
-import com.ym.mec.common.entity.ImGroupModel;
-import com.ym.mec.common.entity.ImPrivateMessage;
-import com.ym.mec.common.entity.ImResult;
-import com.ym.mec.common.entity.ImUserModel;
 import com.ym.mec.im.ImFeignService;
 import com.ym.mec.im.entity.GroupModel;
 
@@ -80,4 +76,29 @@ public class ImFeignServiceFallback implements ImFeignService {
     public Object historyGet(String date) {
         return null;
     }
+
+    @Override
+    public Object createLiveRoom(String roomId, String roomName) {
+        return null;
+    }
+
+    @Override
+    public Object destroyLiveRoom(String roomId) {
+        return null;
+    }
+
+    @Override
+    public void startRecord(String roomId) {
+
+    }
+
+    @Override
+    public Object publishRoomMsg(ImRoomMessage message) {
+        return null;
+    }
+
+    @Override
+    public void stopRecord(String roomId) {
+
+    }
 }

+ 15 - 4
mec-client-api/src/main/java/com/ym/mec/task/TaskRemoteService.java

@@ -1,10 +1,9 @@
 package com.ym.mec.task;
 
-import org.springframework.cloud.openfeign.FeignClient;
-import org.springframework.web.bind.annotation.GetMapping;
-
 import com.ym.mec.common.config.FeignConfiguration;
 import com.ym.mec.task.fallback.TaskRemoteServiceFallback;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.GetMapping;
 
 @FeignClient(name = "web-server", contextId = "TaskRemoteService", configuration = { FeignConfiguration.class }, fallback = TaskRemoteServiceFallback.class)
 public interface TaskRemoteService {
@@ -242,5 +241,17 @@ public interface TaskRemoteService {
 
 	/** 查询老师协议签订状态 */
 	@GetMapping("task/queryTeacherContractSignStatus")
-	public void queryTeacherContractSignStatus();
+	void queryTeacherContractSignStatus();
+
+    /**
+     * 每分钟-查询是否有直播间需要创建
+     */
+    @GetMapping("task/createLiveRoom")
+    void createLiveRoom();
+
+    /**
+     * 每分钟-查询是否有直播间需要销毁
+     */
+    @GetMapping("task/destroyExpiredLiveRoom")
+    void destroyExpiredLiveRoom();
 }

+ 10 - 0
mec-client-api/src/main/java/com/ym/mec/task/fallback/TaskRemoteServiceFallback.java

@@ -294,4 +294,14 @@ public class TaskRemoteServiceFallback implements TaskRemoteService {
 	public void queryTeacherContractSignStatus() {
 		logger.error("查询老师协议签订状态失败");
 	}
+
+    @Override
+    public void createLiveRoom() {
+        logger.error("直播间创建失败");
+    }
+
+    @Override
+    public void destroyExpiredLiveRoom() {
+        logger.error("销毁直播间失败");
+    }
 }

+ 1 - 1
mec-im/src/main/java/com/ym/mec/im/BaseMessage.java → mec-common/common-core/src/main/java/com/ym/mec/common/entity/BaseMessage.java

@@ -1,4 +1,4 @@
-package com.ym.mec.im;
+package com.ym.mec.common.entity;
 
 import com.alibaba.fastjson.JSONObject;
 

+ 67 - 0
mec-common/common-core/src/main/java/com/ym/mec/common/entity/ImRoomMessage.java

@@ -0,0 +1,67 @@
+package com.ym.mec.common.entity;
+
+/**
+ * @author hgw
+ * Created by 2022-02-25
+ */
+public class ImRoomMessage extends BaseMessage {
+
+    //objectName 类型-将所有人强制踢出房间
+    public static final String FORCED_OFFLINE = "RC:ForcedOffline";
+
+    //objectName 类型-观看者退出房间
+    public static final String LOOKER_LOGIN_OUT = "RC:LookerLoginOut";
+
+    /**
+     * 消息类型
+     */
+    private String objectName;
+
+    /**
+     * 消息内容
+     */
+    private Object content;
+
+    /**
+     * 发送者id
+     */
+    private String fromUserId;
+
+    /**
+     * 发送到的房间uid
+     */
+    private String toChatroomId;
+
+    @Override
+    public String getObjectName() {
+        return objectName;
+    }
+
+    public void setObjectName(String objectName) {
+        this.objectName = objectName;
+    }
+
+    public Object getContent() {
+        return content;
+    }
+
+    public void setContent(Object content) {
+        this.content = content;
+    }
+
+    public String getFromUserId() {
+        return fromUserId;
+    }
+
+    public void setFromUserId(String fromUserId) {
+        this.fromUserId = fromUserId;
+    }
+
+    public String getToChatroomId() {
+        return toChatroomId;
+    }
+
+    public void setToChatroomId(String toChatroomId) {
+        this.toChatroomId = toChatroomId;
+    }
+}

+ 70 - 0
mec-common/common-core/src/main/java/com/ym/mec/common/entity/ImUserState.java

@@ -0,0 +1,70 @@
+package com.ym.mec.common.entity;
+
+import java.io.Serializable;
+
+/**
+ * @author hgw
+ * Created by 2022-02-18
+ */
+public class ImUserState implements Serializable {
+    /**
+     * 用户 Id
+     */
+    private String userid;
+    /**
+     * 状态:0:online 上线、1:offline 离线、2:logout 登出  3:退出直播间
+     */
+    private String status;
+    /**
+     * 操作系统:iOS、Android、Websocket、PC、MiniProgram(小程序),用户上线时同步
+     */
+    private String os;
+    /**
+     * 发生时间
+     */
+    private Long time;
+    /**
+     * 用户当前的 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;
+    }
+}

+ 117 - 115
mec-im/pom.xml

@@ -1,122 +1,124 @@
 <?xml version="1.0"?>
-<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
-    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
-  <modelVersion>4.0.0</modelVersion>
-  <parent>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+         xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.ym</groupId>
+        <artifactId>mec</artifactId>
+        <version>1.0</version>
+    </parent>
     <groupId>com.ym</groupId>
-    <artifactId>mec</artifactId>
+    <artifactId>mec-im</artifactId>
     <version>1.0</version>
-  </parent>
-  <groupId>com.ym</groupId>
-  <artifactId>mec-im</artifactId>
-  <version>1.0</version>
-  <name>mec-im</name>
-  <url>http://maven.apache.org</url>
-  <properties>
-    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-  </properties>
-  <dependencies>
-    <dependency>
-      <groupId>org.springframework.boot</groupId>
-      <artifactId>spring-boot-starter-web</artifactId>
-    </dependency>
+    <name>mec-im</name>
+    <url>http://maven.apache.org</url>
+    <properties>
+        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <rongcloud.im.version>3.2.17</rongcloud.im.version>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+        </dependency>
 
-		<dependency>
-			<groupId>org.springframework.cloud</groupId>
-			<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
-		</dependency>
-    <dependency>
-      <groupId>org.springframework.boot</groupId>
-      <artifactId>spring-boot-starter-data-jpa</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework.boot</groupId>
-      <artifactId>spring-boot-starter-data-redis</artifactId>
-    </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-jpa</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
 
-    <dependency>
-      <groupId>mysql</groupId>
-      <artifactId>mysql-connector-java</artifactId>
-      <scope>runtime</scope>
-    </dependency>
-    <dependency>
-      <groupId>com.alibaba</groupId>
-      <artifactId>druid-spring-boot-starter</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>org.projectlombok</groupId>
-      <artifactId>lombok</artifactId>
-      <optional>true</optional>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework.boot</groupId>
-      <artifactId>spring-boot-starter-test</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <!-- https://mvnrepository.com/artifact/commons-lang/commons-lang -->
-    <dependency>
-      <groupId>commons-lang</groupId>
-      <artifactId>commons-lang</artifactId>
-      <version>2.6</version>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework.boot</groupId>
-      <artifactId>spring-boot-starter-aop</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>com.ym</groupId>
-      <artifactId>common-core</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>com.ym</groupId>
-      <artifactId>mec-auth-api</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>com.alibaba</groupId>
-      <artifactId>fastjson</artifactId>
-      <version>1.2.46</version>
-    </dependency>
-    <!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
-    <dependency>
-      <groupId>commons-codec</groupId>
-      <artifactId>commons-codec</artifactId>
-      <version>1.9</version>
-    </dependency>
-    <!-- https://mvnrepository.com/artifact/joda-time/joda-time -->
-    <dependency>
-      <groupId>joda-time</groupId>
-      <artifactId>joda-time</artifactId>
-      <version>2.8.1</version>
-    </dependency>
-    <dependency>
-      <groupId>com.ym</groupId>
-      <artifactId>mec-biz</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>cn.rongcloud.im</groupId>
-      <artifactId>server-sdk-java</artifactId>
-      <version>3.1.4</version>
-    </dependency>
-    <dependency>
-      <groupId>org.springframework.cloud</groupId>
-      <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>io.netty</groupId>
-      <artifactId>netty-all</artifactId>
-    </dependency>
-  </dependencies>
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <scope>runtime</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>druid-spring-boot-starter</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/commons-lang/commons-lang -->
+        <dependency>
+            <groupId>commons-lang</groupId>
+            <artifactId>commons-lang</artifactId>
+            <version>2.6</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.ym</groupId>
+            <artifactId>common-core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.ym</groupId>
+            <artifactId>mec-auth-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>1.2.46</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
+        <dependency>
+            <groupId>commons-codec</groupId>
+            <artifactId>commons-codec</artifactId>
+            <version>1.9</version>
+        </dependency>
+        <!-- https://mvnrepository.com/artifact/joda-time/joda-time -->
+        <dependency>
+            <groupId>joda-time</groupId>
+            <artifactId>joda-time</artifactId>
+            <version>2.8.1</version>
+        </dependency>
+        <dependency>
+            <groupId>com.ym</groupId>
+            <artifactId>mec-biz</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>cn.rongcloud.im</groupId>
+            <artifactId>server-sdk-java</artifactId>
+            <version>${rongcloud.im.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.cloud</groupId>
+            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>io.netty</groupId>
+            <artifactId>netty-all</artifactId>
+        </dependency>
+    </dependencies>
 
-  <build>
-    <plugins>
-      <plugin>
-        <groupId>org.springframework.boot</groupId>
-        <artifactId>spring-boot-maven-plugin</artifactId>
-      </plugin>
-      <plugin>
-        <groupId>com.spotify</groupId>
-        <artifactId>docker-maven-plugin</artifactId>
-      </plugin>
-    </plugins>
-  </build>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>com.spotify</groupId>
+                <artifactId>docker-maven-plugin</artifactId>
+            </plugin>
+        </plugins>
+    </build>
 </project>

+ 1 - 0
mec-im/src/main/java/com/ym/config/IMProperties.java

@@ -14,4 +14,5 @@ public class IMProperties {
     private String appKey;
     private String secret;
     private String host;
+    private String rtcHost;
 }

+ 6 - 4
mec-im/src/main/java/com/ym/config/ResourceServerConfig.java

@@ -10,9 +10,11 @@ import org.springframework.security.oauth2.config.annotation.web.configuration.R
 public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
     @Override
     public void configure(HttpSecurity http) throws Exception {
-        http.authorizeRequests().antMatchers("/v2/api-docs","/user/register",
-                "/group/join","/group/create","/group/quit","/room/leave","/room/statusSync",
-                "/room/statusImMsg","/group/batchDismiss","/private/send","/group/send",
-                "/group/dismiss","/room/statusImMsg","/history/get").permitAll().anyRequest().authenticated().and().csrf().disable();
+        http.authorizeRequests().antMatchers("/v2/api-docs", "/user/register",
+                        "/group/join", "/group/create", "/group/quit", "/room/leave", "/room/statusSync",
+                        "/room/statusImMsg", "/group/batchDismiss", "/private/send", "/group/send",
+                        "/group/dismiss", "/room/statusImMsg", "/history/get", "/user/statusImUser", "/liveRoom/recordSync",
+                        "/liveRoom/publishRoomMsg","/liveRoom/destroy","/liveRoom/create","/liveRoom/startRecord","/liveRoom/stopRecord")
+                .permitAll().anyRequest().authenticated().and().csrf().disable();
     }
 }

+ 1 - 1
mec-im/src/main/java/com/ym/config/WebMvcConfig.java

@@ -57,7 +57,7 @@ public class WebMvcConfig implements WebMvcConfigurer {
 		registry.addInterceptor(tenantInterceptor).addPathPatterns("/**").excludePathPatterns("/v2/api-docs",
 				"/group/*","/room/statusSync",
 				"/room/statusImMsg","/private/*","/system/*",
-				"/room/statusImMsg","/history/get","/user/*","/push/*");
+				"/room/statusImMsg","/history/get","/user/*","/push/*","/liveRoom/recordSync");
 	}
 
 	@SuppressWarnings("rawtypes")

+ 65 - 0
mec-im/src/main/java/com/ym/controller/LiveRoomController.java

@@ -0,0 +1,65 @@
+package com.ym.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import com.ym.mec.common.entity.ImRoomMessage;
+import com.ym.pojo.IMApiResultInfo;
+import com.ym.pojo.RecordNotify;
+import com.ym.service.LiveRoomService;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+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;
+
+/**
+ * @author hgw
+ * Created by 2022-02-21
+ */
+@RestController
+@RequestMapping("/liveRoom")
+@Slf4j
+public class LiveRoomController {
+
+    @Autowired
+    private LiveRoomService liveRoomService;
+
+    @ApiOperation("创建直播房间")
+    @PostMapping(value = "/create")
+    public IMApiResultInfo createLiveRoom(String roomId, String roomName) throws Exception {
+        return liveRoomService.createLiveRoom(roomId, roomName);
+    }
+
+    @ApiOperation("销毁直播房间")
+    @PostMapping(value = "/destroy")
+    public IMApiResultInfo destroyLiveRoom(String roomId) throws Exception {
+        return liveRoomService.destroyLiveRoom(roomId);
+    }
+
+    @ApiOperation("向房间发送消息")
+    @PostMapping(value = "/publishRoomMsg")
+    public IMApiResultInfo publishRoomTextMsg(@RequestBody ImRoomMessage message) {
+        return liveRoomService.publishRoomMessage(message);
+    }
+
+    @ApiOperation("录制结果回调")
+    @RequestMapping(value = "/recordSync")
+    public void recordSync(@RequestBody String body) {
+        log.info("recordSync body:{}", body);
+        RecordNotify recordNotify = JSONObject.parseObject(body, RecordNotify.class);
+        liveRoomService.recordSync(recordNotify);
+    }
+
+    @ApiOperation("录制直播")
+    @RequestMapping(value = "/startRecord")
+    public void startRecord(String roomId) throws Exception {
+        liveRoomService.startRecord(roomId);
+    }
+
+    @ApiOperation("结束录制直播")
+    @RequestMapping(value = "/stopRecord")
+    public void stopRecord(String roomId) throws Exception {
+        liveRoomService.stopRecord(roomId);
+    }
+}

+ 26 - 6
mec-im/src/main/java/com/ym/controller/UserController.java

@@ -1,26 +1,46 @@
 package com.ym.controller;
 
+import com.alibaba.fastjson.JSONObject;
+import com.ym.common.BaseResponse;
+import com.ym.mec.biz.service.ImLiveBroadcastRoomService;
+import com.ym.mec.common.entity.ImUserState;
 import com.ym.service.UserService;
 import io.rong.models.user.UserModel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
 
 @RestController
 @RequestMapping("/user")
 public class UserController {
-
+    private static final Logger log = LoggerFactory.getLogger(UserController.class);
+    @Autowired
+    private UserService userService;
     @Autowired
-    UserService userService;
+    private ImLiveBroadcastRoomService imLiveBroadcastRoomService;
 
     @RequestMapping(value = "/register", method = RequestMethod.POST)
     public Object register(@RequestBody UserModel userModel) throws Exception {
         return userService.register(userModel);
     }
+
     @RequestMapping(value = "/update", method = RequestMethod.POST)
     public Object update(@RequestBody UserModel userModel) throws Exception {
         return userService.update(userModel);
     }
+
+    /**
+     * 监听融云用户状态变更
+     *
+     * @param userState
+     */
+    @PostMapping(value = "/statusImUser")
+    public BaseResponse statusImUser(@RequestBody List<ImUserState> userState) {
+        log.info("statusImUser >>>>> : {}", JSONObject.toJSONString(userState));
+        imLiveBroadcastRoomService.opsRoom(userState);
+        return new BaseResponse<>();
+    }
 }

+ 1 - 1
mec-im/src/main/java/com/ym/pojo/ActionEnum.java → mec-im/src/main/java/com/ym/enums/ActionEnum.java

@@ -1,4 +1,4 @@
-package com.ym.pojo;
+package com.ym.enums;
 
 /**
  * Created by weiqinxiao on 2019/3/19.

+ 1 - 1
mec-im/src/main/java/com/ym/pojo/DeviceTypeEnum.java → mec-im/src/main/java/com/ym/enums/DeviceTypeEnum.java

@@ -1,4 +1,4 @@
-package com.ym.pojo;
+package com.ym.enums;
 
 public enum DeviceTypeEnum {
     Microphone,

+ 1 - 1
mec-im/src/main/java/com/ym/pojo/RoleEnum.java → mec-im/src/main/java/com/ym/enums/RoleEnum.java

@@ -1,4 +1,4 @@
-package com.ym.pojo;
+package com.ym.enums;
 
 import com.ym.common.ApiException;
 import com.ym.common.ErrorEnum;

+ 93 - 33
mec-im/src/main/java/com/ym/http/HttpHelper.java

@@ -2,7 +2,9 @@ package com.ym.http;
 
 import com.ym.config.IMProperties;
 import com.ym.utils.CodeUtil;
+import io.rong.util.GsonUtil;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
@@ -12,12 +14,12 @@ import java.io.ByteArrayOutputStream;
 import java.io.DataOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
-import java.net.ProtocolException;
-import java.net.URL;
+import java.net.*;
+import java.nio.charset.StandardCharsets;
 import java.security.cert.CertificateException;
 import java.security.cert.X509Certificate;
+import java.util.HashMap;
+import java.util.Map;
 
 @Slf4j
 @Component
@@ -31,7 +33,7 @@ public class HttpHelper {
 
     @Autowired
     IMProperties imProperties;
-    
+
     @PostConstruct
     private void init() {
         log.info("init HttpHelper");
@@ -69,7 +71,7 @@ public class HttpHelper {
 
     // 设置body体
     public void setBodyParameter(StringBuilder sb, HttpURLConnection conn)
-        throws IOException {
+            throws IOException {
         String str = sb.toString();
         log.info("Call server api with url: {}, data: {}", conn.getURL().toString(), str);
         DataOutputStream out = new DataOutputStream(conn.getOutputStream());
@@ -79,7 +81,7 @@ public class HttpHelper {
     }
 
     public HttpURLConnection createGetHttpConnection(String uri)
-        throws MalformedURLException, IOException {
+            throws MalformedURLException, IOException {
         URL url = new URL(uri);
         HttpURLConnection conn = (HttpURLConnection) url.openConnection();
         conn.setConnectTimeout(30000);
@@ -90,32 +92,23 @@ public class HttpHelper {
     public void setBodyParameter(String str, HttpURLConnection conn) throws IOException {
         log.info("Call IM server api with url: {}, data: {}", conn.getURL().toString(), str);
         DataOutputStream out = new DataOutputStream(conn.getOutputStream());
-        out.write(str.getBytes("utf-8"));
+        out.write(str.getBytes(StandardCharsets.UTF_8));
         out.flush();
         out.close();
     }
 
     public HttpURLConnection createWhiteBoardPostHttpConnection(String host, String uri, String contentType)
-        throws MalformedURLException, IOException, ProtocolException {
+            throws MalformedURLException, IOException, ProtocolException {
 
         URL url = new URL(host + uri);
         HttpURLConnection conn = (HttpURLConnection) url.openConnection();
-        conn.setUseCaches(false);
-        conn.setDoInput(true);
-        conn.setDoOutput(true);
-        conn.setRequestMethod("POST");
-        conn.setInstanceFollowRedirects(true);
-        conn.setConnectTimeout(30000);
-        conn.setReadTimeout(30000);
-
-        conn.setRequestProperty("Content-Type", contentType);
-
+        setConn(conn, contentType);
         return conn;
     }
 
     public HttpURLConnection createCommonPostHttpConnection(String host, String appKey,
-        String appSecret, String uri, String contentType)
-        throws MalformedURLException, IOException, ProtocolException {
+                                                            String appSecret, String uri, String contentType)
+            throws MalformedURLException, IOException, ProtocolException {
         String nonce = String.valueOf(Math.random() * 1000000);
         String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
         StringBuilder toSign = new StringBuilder(appSecret).append(nonce).append(timestamp);
@@ -145,8 +138,7 @@ public class HttpHelper {
     }
 
     public HttpURLConnection createCommonGetHttpConnection(String host, String appKey,
-        String appSecret, String uri, String contentType)
-        throws MalformedURLException, IOException, ProtocolException {
+                                                           String appSecret, String uri, String contentType) throws IOException {
         String nonce = String.valueOf(Math.random() * 1000000);
         String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
         StringBuilder toSign = new StringBuilder(appSecret).append(nonce).append(timestamp);
@@ -175,18 +167,16 @@ public class HttpHelper {
         return conn;
     }
 
-    public HttpURLConnection createIMGetHttpConnection(String uri, String contentType)
-        throws MalformedURLException, IOException, ProtocolException {
+    public HttpURLConnection createIMGetHttpConnection(String uri, String contentType) throws IOException {
         return createCommonGetHttpConnection(imProperties.getHost(),
-            imProperties.getAppKey(), imProperties.getSecret(), uri,
-            contentType);
+                imProperties.getAppKey(), imProperties.getSecret(), uri,
+                contentType);
     }
 
-    public HttpURLConnection createIMPostHttpConnection(String uri, String contentType)
-        throws MalformedURLException, IOException, ProtocolException {
+    public HttpURLConnection createIMPostHttpConnection(String uri, String contentType) throws IOException {
         return createCommonPostHttpConnection(imProperties.getHost(),
                 imProperties.getAppKey(), imProperties.getSecret(), uri,
-            contentType);
+                contentType);
     }
 
     public byte[] readInputStream(InputStream inStream) throws Exception {
@@ -202,6 +192,34 @@ public class HttpHelper {
         return data;
     }
 
+    public String returnResult(HttpURLConnection conn) throws Exception {
+        InputStream input;
+        String result;
+        try {
+            if (conn.getResponseCode() == 200 || conn.getResponseCode() == 201) {
+                input = conn.getInputStream();
+            } else {
+                input = conn.getErrorStream();
+            }
+            result = new String(readInputStream(input), StandardCharsets.UTF_8);
+        } catch (UnknownHostException e) {
+            result = getExceptionMessage("request:" + conn.getURL() + " ,UnknownHostException:" + e.getMessage());
+        } catch (SocketTimeoutException e) {
+            result = getExceptionMessage("request:" + conn.getURL() + " ,SocketTimeoutException:" + e.getMessage());
+        } catch (IOException e) {
+            result = getExceptionMessage("request:" + conn.getURL() + " ,IOException:" + e.getMessage());
+        }
+        log.info("IM server api response:{}", result);
+        return result;
+    }
+
+    private static String getExceptionMessage(String error) {
+        Map<String, Object> result = new HashMap<>();
+        result.put("code", 400);
+        result.put("msg", error);
+        return GsonUtil.toJson(result);
+    }
+
     public String returnResult(HttpURLConnection conn, String body) throws Exception, IOException {
         InputStream input = null;
         if (conn.getResponseCode() == 200) {
@@ -214,15 +232,49 @@ public class HttpHelper {
         return result;
     }
 
-    public HttpURLConnection createPostHttpConnection(String uri, String contentType)
-        throws IOException {
-
+    public HttpURLConnection createPostHttpConnection(String uri, String contentType) throws IOException {
         URL url = new URL(uri);
         HttpURLConnection conn = (HttpURLConnection) url.openConnection();
         if (conn == null) {
             log.info("open url connection fail, url={}", uri);
             return null;
         }
+        setConn(conn, contentType);
+        return conn;
+    }
+
+    private void setConn(HttpURLConnection conn, String contentType) throws ProtocolException {
+        conn.setUseCaches(false);
+        conn.setDoInput(true);
+        conn.setDoOutput(true);
+        conn.setRequestMethod("POST");
+        conn.setInstanceFollowRedirects(true);
+        conn.setConnectTimeout(30000);
+        conn.setReadTimeout(30000);
+        conn.setRequestProperty("Content-Type", contentType);
+    }
+
+    public HttpURLConnection createIMRtcPostHttpConnection(String uri, String contentType,String roomId)
+            throws IOException{
+        return createCommonRtcPostHttpConnection(imProperties.getRtcHost(),
+                imProperties.getAppKey(), imProperties.getSecret(), uri,
+                contentType,roomId);
+    }
+
+    public HttpURLConnection createCommonRtcPostHttpConnection(String host, String appKey,
+                                                               String appSecret, String uri, String contentType,String roomId)
+            throws IOException{
+        String nonce = String.valueOf(Math.random() * 1000000);
+        String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
+        StringBuilder toSign = new StringBuilder(appSecret).append(nonce).append(timestamp);
+        String sign = CodeUtil.hexSHA1(toSign.toString());
+        uri = host + uri;
+        URL url = new URL(uri);
+        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+        if (conn == null) {
+            log.info("open url connectin fail, url={}", uri);
+            return null;
+        }
 
         conn.setUseCaches(false);
         conn.setDoInput(true);
@@ -231,6 +283,14 @@ public class HttpHelper {
         conn.setInstanceFollowRedirects(true);
         conn.setConnectTimeout(30000);
         conn.setReadTimeout(30000);
+
+        conn.setRequestProperty("App-Key", appKey);
+        conn.setRequestProperty("Nonce", nonce);
+        conn.setRequestProperty("Timestamp", timestamp);
+        conn.setRequestProperty("Signature", sign);
+        if(StringUtils.isNotEmpty(roomId)){
+            conn.setRequestProperty("Room-Id", roomId);
+        }
         conn.setRequestProperty("Content-Type", contentType);
         return conn;
     }

+ 131 - 17
mec-im/src/main/java/com/ym/mec/im/IMHelper.java

@@ -2,15 +2,21 @@ package com.ym.mec.im;
 
 import com.alibaba.fastjson.JSON;
 import com.ym.http.HttpHelper;
+import com.ym.mec.common.entity.BaseMessage;
+import com.ym.mec.common.exception.BizException;
 import com.ym.pojo.IMApiResultInfo;
 import com.ym.pojo.IMTokenInfo;
+import io.rong.util.GsonUtil;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Component;
 
 import java.net.HttpURLConnection;
 import java.net.URLEncoder;
+import java.util.List;
+import java.util.Objects;
 
 /**
  * Created by weiqinxiao on 2019/2/28.
@@ -26,8 +32,8 @@ public class IMHelper {
     /**
      * 获取 Token 方法
      *
-     * @param userId:用户 Id,最大长度 64 字节.是用户在 App 中的唯一标识码,必须保证在同一个 App 内不重复,重复的用户 Id 将被当作是同一用户。(必传)
-     * @param name:用户名称,最大长度 128 字节.用来在 Push 推送时显示用户的名称.用户名称,最大长度 128 字节.用来在 Push 推送时显示用户的名称。(必传)
+     * @param userId:用户        Id,最大长度 64 字节.是用户在 App 中的唯一标识码,必须保证在同一个 App 内不重复,重复的用户 Id 将被当作是同一用户。(必传)
+     * @param name:用户名称,最大长度   128 字节.用来在 Push 推送时显示用户的名称.用户名称,最大长度 128 字节.用来在 Push 推送时显示用户的名称。(必传)
      * @param portraitUri:用户头像 URI,最大长度 1024 字节.用来在 Push 推送时显示用户的头像。(必传)
      * @return TokenResult
      **/
@@ -64,8 +70,8 @@ public class IMHelper {
      * 内的群组数量没有限制.注:其实本方法是加入群组方法 /group/join 的别名。)
      *
      * @param userId:要加入群的用户 Id。(必传)
-     * @param groupId:创建群组 Id。(必传)
-     * @param groupName:群组 Id 对应的名称。(必传)
+     * @param groupId:创建群组   Id。(必传)
+     * @param groupName:群组   Id 对应的名称。(必传)
      * @return CodeSuccessResult
      **/
     public IMApiResultInfo createGroup(String[] userId, String groupId, String groupName)
@@ -107,8 +113,8 @@ public class IMHelper {
     /**
      * 将用户加入指定群组,用户将可以收到该群的消息,同一用户最多可加入 500 个群,每个群最大至 3000 人。
      *
-     * @param userId:要加入群的用户 Id,可提交多个,最多不超过 1000 个。(必传)
-     * @param groupId:要加入的群 Id。(必传)
+     * @param userId:要加入群的用户  Id,可提交多个,最多不超过 1000 个。(必传)
+     * @param groupId:要加入的群   Id。(必传)
      * @param groupName:要加入的群 Id 对应的名称。(必传)
      * @return CodeSuccessResult
      **/
@@ -149,12 +155,11 @@ public class IMHelper {
     }
 
 
-
     /**
      * 退出群组方法(将用户从群中移除,不再接收该群组的消息.)
      *
      * @param userId:要退出群的用户 Id.(必传)
-     * @param groupId:要退出的群 Id.(必传)
+     * @param groupId:要退出的群  Id.(必传)
      * @return CodeSuccessResult
      **/
     public IMApiResultInfo quit(String[] userId, String groupId) throws Exception {
@@ -191,7 +196,7 @@ public class IMHelper {
      * 解散群组方法。(将该群解散,所有用户都无法再接收该群的消息。)
      *
      * @param userId:操作解散群的用户 Id。(必传)
-     * @param groupId:要解散的群 Id。(必传)
+     * @param groupId:要解散的群   Id。(必传)
      * @return CodeSuccessResult
      **/
     public IMApiResultInfo dismiss(String userId, String groupId) throws Exception {
@@ -222,15 +227,15 @@ public class IMHelper {
     /**
      * 发送群组消息方法(以一个用户身份向群组发送消息,单条消息最大 128k.每秒钟最多发送 20 条消息,每次最多向 3 个群组发送,如:一次向 3 个群组发送消息,示为 3 条消息。)
      *
-     * @param fromUserId:发送人用户 Id 。(必传)
-     * @param toGroupId:接收群Id,提供多个本参数可以实现向多群发送消息,最多不超过 3 个群组。(必传)
-     * @param pushContent:定义显示的 Push 内容,如果 objectName 为融云内置消息类型时,则发送后用户一定会收到 Push 信息. 如果为自定义消息,则
-     * pushContent 为自定义消息显示的 Push 内容,如果不传则用户不会收到 Push 通知。(可选)
-     * @param pushData:针对 iOS 平台为 Push 通知时附加到 payload 中,Android 客户端收到推送消息时对应字段名为 pushData。(可选)
-     * @param isPersisted:当前版本有新的自定义消息,而老版本没有该自定义消息时,老版本客户端收到消息后是否进行存储,0 表示为不存储、 1 表示为存储,默认为 1
-     * 存储消息。(可选)
+     * @param fromUserId:发送人用户                                             Id 。(必传)
+     * @param toGroupId:接收群Id,提供多个本参数可以实现向多群发送消息,最多不超过                     3 个群组。(必传)
+     * @param pushContent:定义显示的                                            Push 内容,如果 objectName 为融云内置消息类型时,则发送后用户一定会收到 Push 信息. 如果为自定义消息,则
+     *                                                                     pushContent 为自定义消息显示的 Push 内容,如果不传则用户不会收到 Push 通知。(可选)
+     * @param pushData:针对                                                  iOS 平台为 Push 通知时附加到 payload 中,Android 客户端收到推送消息时对应字段名为 pushData。(可选)
+     * @param isPersisted:当前版本有新的自定义消息,而老版本没有该自定义消息时,老版本客户端收到消息后是否进行存储,0   表示为不存储、 1 表示为存储,默认为 1
+     *                                                                     存储消息。(可选)
      * @param isCounted:当前版本有新的自定义消息,而老版本没有该自定义消息时,老版本客户端收到消息后是否进行未读消息计数,0 表示为不计数、 1 表示为计数,默认为 1
-     * 计数,未读消息数增加 1。(可选)
+     *                                                                     计数,未读消息数增加 1。(可选)
      * @return CodeSuccessResult
      **/
     public IMApiResultInfo publishMessage(String fromUserId, String toGroupId, BaseMessage message) throws Exception {
@@ -332,4 +337,113 @@ public class IMHelper {
 
         return JSON.parseObject(httpHelper.returnResult(conn, body), IMApiResultInfo.class);
     }
+
+    /**
+     * 创建聊天室
+     *
+     * @param chatRoomId:   要创建的聊天室 Id,长度不超过 64 字节
+     * @param chatRoomName: 聊天室的名称,每次可创建多个聊天室。
+     * @return IMApiResultInfo
+     * @throws Exception
+     */
+    public IMApiResultInfo createChatRoom(String chatRoomId, String chatRoomName) throws Exception {
+        if (chatRoomId == null) {
+            throw new BizException("房间Uid不能为空");
+        }
+
+        if (chatRoomName == null) {
+            throw new BizException("房间名称不能为空");
+        }
+
+        StringBuilder sb = new StringBuilder();
+        chatRoomId = "[" + chatRoomId + "]";
+        sb.append("&chatroom").append(URLEncoder.encode(chatRoomId, UTF8));
+        sb.append("=");
+        sb.append(URLEncoder.encode(chatRoomName, UTF8));
+        String body = sb.toString();
+        if (body.indexOf("&") == 0) {
+            body = body.substring(1);
+        }
+
+        HttpURLConnection conn = httpHelper
+                .createIMPostHttpConnection("/chatroom/create.json", "application/x-www-form-urlencoded");
+        httpHelper.setBodyParameter(body, conn);
+
+        return (IMApiResultInfo) GsonUtil.fromJson(httpHelper.returnResult(conn), IMApiResultInfo.class);
+    }
+
+
+    /**
+     * 销毁聊天室
+     *
+     * @param chatroomIds 聊天室 ID 列表(必传)
+     * @return
+     */
+    public IMApiResultInfo deleteChrm(List<String> chatroomIds)
+            throws Exception {
+        if (chatroomIds == null) {
+            throw new BizException("房间Uid不能为空");
+        }
+        StringBuilder sb = new StringBuilder();
+        for (String child : chatroomIds) {
+            sb.append("&chatroomId=").append(URLEncoder.encode(child, UTF8));
+        }
+        String body = sb.toString();
+        if (body.indexOf("&") == 0) {
+            body = body.substring(1);
+        }
+        HttpURLConnection conn = httpHelper.createIMPostHttpConnection("/chatroom/destroy.json", "application/x-www-form-urlencoded");
+        httpHelper.setBodyParameter(body, conn);
+
+        return (IMApiResultInfo) GsonUtil.fromJson(httpHelper.returnResult(conn), IMApiResultInfo.class);
+
+    }
+
+    /**
+     * 发送房间消息
+     *
+     * @param fromUserId   发送人id
+     * @param toChatroomId 房间uid
+     * @param message      发送的消息
+     */
+    public IMApiResultInfo publishRoomMessage(String fromUserId, String toChatroomId, BaseMessage message) throws Exception {
+        String[] toChatroomIds = new String[1];
+        toChatroomIds[0] = toChatroomId;
+        return publishRoomMessage(fromUserId, toChatroomIds, message);
+    }
+
+    public IMApiResultInfo publishRoomMessage(String fromUserId, String[] toChatroomIds, BaseMessage message) throws Exception {
+        if (StringUtils.isBlank(fromUserId)) {
+            throw new BizException("发送人不能为空");
+        }
+        if (Objects.isNull(toChatroomIds)) {
+            throw new BizException("房间Uid不能为空");
+        }
+        if (Objects.isNull(message)) {
+            throw new BizException("消息不能为空");
+        }
+
+        StringBuilder sb = new StringBuilder();
+        sb.append("&fromUserId=").append(URLEncoder.encode(fromUserId, UTF8));
+
+        for (String child : toChatroomIds) {
+            sb.append("&toChatroomId=").append(URLEncoder.encode(child, UTF8));
+        }
+
+        String msgStr = GsonUtil.toJson(message);
+        log.info("publish msg: {}", msgStr);
+        sb.append("&objectName=").append(URLEncoder.encode(message.getObjectName(), UTF8));
+        sb.append("&content=").append(URLEncoder.encode(msgStr, UTF8));
+
+        String body = sb.toString();
+        if (body.indexOf("&") == 0) {
+            body = body.substring(1);
+        }
+
+        HttpURLConnection conn = httpHelper
+                .createIMPostHttpConnection("/message/chatroom/publish.json", "application/x-www-form-urlencoded");
+        httpHelper.setBodyParameter(body, conn);
+
+        return (IMApiResultInfo) GsonUtil.fromJson(httpHelper.returnResult(conn), IMApiResultInfo.class);
+    }
 }

+ 1 - 1
mec-im/src/main/java/com/ym/mec/im/message/ApplyForSpeechMessage.java

@@ -1,6 +1,6 @@
 package com.ym.mec.im.message;
 
-import com.ym.mec.im.BaseMessage;
+import com.ym.mec.common.entity.BaseMessage;
 import lombok.Getter;
 import lombok.Setter;
 

+ 1 - 1
mec-im/src/main/java/com/ym/mec/im/message/AssistantTransferMessage.java

@@ -1,6 +1,6 @@
 package com.ym.mec.im.message;
 
-import com.ym.mec.im.BaseMessage;
+import com.ym.mec.common.entity.BaseMessage;
 import lombok.Getter;
 import lombok.Setter;
 

+ 1 - 1
mec-im/src/main/java/com/ym/mec/im/message/ControlDeviceNotifyMessage.java

@@ -1,6 +1,6 @@
 package com.ym.mec.im.message;
 
-import com.ym.mec.im.BaseMessage;
+import com.ym.mec.common.entity.BaseMessage;
 import lombok.Getter;
 import lombok.Setter;
 

+ 1 - 1
mec-im/src/main/java/com/ym/mec/im/message/DeviceStateChangedMessage.java

@@ -1,6 +1,6 @@
 package com.ym.mec.im.message;
 
-import com.ym.mec.im.BaseMessage;
+import com.ym.mec.common.entity.BaseMessage;
 import lombok.Getter;
 import lombok.Setter;
 

+ 1 - 1
mec-im/src/main/java/com/ym/mec/im/message/DisplayMessage.java

@@ -1,6 +1,6 @@
 package com.ym.mec.im.message;
 
-import com.ym.mec.im.BaseMessage;
+import com.ym.mec.common.entity.BaseMessage;
 import lombok.Getter;
 import lombok.Setter;
 

+ 1 - 1
mec-im/src/main/java/com/ym/mec/im/message/ExamSongDownloadMessageMessage.java

@@ -1,6 +1,6 @@
 package com.ym.mec.im.message;
 
-import com.ym.mec.im.BaseMessage;
+import com.ym.mec.common.entity.BaseMessage;
 import com.ym.pojo.ExamSongMessage;
 
 public class ExamSongDownloadMessageMessage extends BaseMessage {

+ 1 - 1
mec-im/src/main/java/com/ym/mec/im/message/ExamSongDownloadStatusMessage.java

@@ -1,6 +1,6 @@
 package com.ym.mec.im.message;
 
-import com.ym.mec.im.BaseMessage;
+import com.ym.mec.common.entity.BaseMessage;
 import lombok.Getter;
 import lombok.Setter;
 

+ 1 - 1
mec-im/src/main/java/com/ym/mec/im/message/MemberChangedMessage.java

@@ -1,6 +1,6 @@
 package com.ym.mec.im.message;
 
-import com.ym.mec.im.BaseMessage;
+import com.ym.mec.common.entity.BaseMessage;
 import lombok.Getter;
 import lombok.Setter;
 

+ 1 - 1
mec-im/src/main/java/com/ym/mec/im/message/MetronomeMessageMessage.java

@@ -1,6 +1,6 @@
 package com.ym.mec.im.message;
 
-import com.ym.mec.im.BaseMessage;
+import com.ym.mec.common.entity.BaseMessage;
 import com.ym.pojo.CustomMessage;
 import org.apache.commons.lang3.StringUtils;
 

+ 1 - 1
mec-im/src/main/java/com/ym/mec/im/message/MusicScoreDownloadMessageMessage.java

@@ -1,6 +1,6 @@
 package com.ym.mec.im.message;
 
-import com.ym.mec.im.BaseMessage;
+import com.ym.mec.common.entity.BaseMessage;
 import com.ym.pojo.MusicScoreMessage;
 
 public class MusicScoreDownloadMessageMessage extends BaseMessage {

+ 1 - 1
mec-im/src/main/java/com/ym/mec/im/message/MusicScoreDownloadStatusMessage.java

@@ -1,7 +1,7 @@
 package com.ym.mec.im.message;
 
 import com.ym.mec.biz.dal.entity.CourseScheduleStudentMusicScore;
-import com.ym.mec.im.BaseMessage;
+import com.ym.mec.common.entity.BaseMessage;
 import lombok.Getter;
 import lombok.Setter;
 

+ 1 - 1
mec-im/src/main/java/com/ym/mec/im/message/RoleChangedMessage.java

@@ -1,6 +1,6 @@
 package com.ym.mec.im.message;
 
-import com.ym.mec.im.BaseMessage;
+import com.ym.mec.common.entity.BaseMessage;
 import lombok.Getter;
 import lombok.Setter;
 

+ 1 - 1
mec-im/src/main/java/com/ym/mec/im/message/SpeechResultMessage.java

@@ -1,6 +1,6 @@
 package com.ym.mec.im.message;
 
-import com.ym.mec.im.BaseMessage;
+import com.ym.mec.common.entity.BaseMessage;
 import lombok.Getter;
 import lombok.Setter;
 

+ 1 - 1
mec-im/src/main/java/com/ym/mec/im/message/TicketExpiredMessage.java

@@ -1,6 +1,6 @@
 package com.ym.mec.im.message;
 
-import com.ym.mec.im.BaseMessage;
+import com.ym.mec.common.entity.BaseMessage;
 import lombok.Getter;
 import lombok.Setter;
 

+ 1 - 1
mec-im/src/main/java/com/ym/mec/im/message/TurnPageMessage.java

@@ -1,6 +1,6 @@
 package com.ym.mec.im.message;
 
-import com.ym.mec.im.BaseMessage;
+import com.ym.mec.common.entity.BaseMessage;
 import lombok.Getter;
 import lombok.Setter;
 

+ 1 - 1
mec-im/src/main/java/com/ym/mec/im/message/UpgradeRoleMessage.java

@@ -1,6 +1,6 @@
 package com.ym.mec.im.message;
 
-import com.ym.mec.im.BaseMessage;
+import com.ym.mec.common.entity.BaseMessage;
 import lombok.Getter;
 import lombok.Setter;
 

+ 1 - 1
mec-im/src/main/java/com/ym/mec/im/message/WhiteboardMessage.java

@@ -1,6 +1,6 @@
 package com.ym.mec.im.message;
 
-import com.ym.mec.im.BaseMessage;
+import com.ym.mec.common.entity.BaseMessage;
 import lombok.Getter;
 import lombok.Setter;
 

+ 1 - 0
mec-im/src/main/java/com/ym/pojo/ControlDeviceTaskInfo.java

@@ -1,5 +1,6 @@
 package com.ym.pojo;
 
+import com.ym.enums.DeviceTypeEnum;
 import lombok.Data;
 
 import java.io.Serializable;

+ 16 - 0
mec-im/src/main/java/com/ym/pojo/RecordConfig.java

@@ -0,0 +1,16 @@
+package com.ym.pojo;
+
+import lombok.Data;
+
+@Data
+public class RecordConfig {
+    Integer mode = 3;
+    String videoFormat = "mp4";
+    String audioFormat = "aac";
+    String videoResolution = "640x480";
+    Integer mixLayout = 3;
+    Integer sliceMin = 30;
+    Integer renderMode = 1;
+    String hostUserId;
+    String hostStreamId;
+}

+ 50 - 0
mec-im/src/main/java/com/ym/pojo/RecordNotify.java

@@ -0,0 +1,50 @@
+package com.ym.pojo;
+
+import lombok.Data;
+
+@Data
+public class RecordNotify {
+	//时间戳,单位为毫秒
+	Long timestamp;
+	//回调事件类型,如下:1: 录制开始;2: 录制切片;3: 录制结束;4: 文件上传
+	Integer type;
+	//当前使用的 App Key。
+	String appKey;
+	//录制 ID,每次录制任务的唯一标识。如果文件切片,可以通过此 ID 进行关联
+	String recordId;
+	//房间 ID 。
+	String roomId;
+	//会话 ID,每次通话的唯一标识
+	String sessionId;
+	//用户 ID。如果录制模式为 Mix,此字段为空。
+	String userId;
+	//录制模式0:音视频 single 模式; 1:纯视频 single 模式; 2:纯音频 single 模式;
+	//3:音视频 mix 模式;
+	//4:纯视频 mix 模式;
+	//5:纯音频 mix 模式;
+	//6:单人 mix 音视频模式
+	Integer mode;
+	RecordNotifyOutput output;
+	RecordNotifyConfig config;
+	//扩展字段,内容为手动录制时传入的 extra 字段的值
+	String extra;
+	//状态码,200 为正常
+	Integer code;
+	//错误信息。
+	String errorMessage;
+
+	@Override
+	public String toString() {
+		return "RecordNotify{" +
+				"recordId='" + recordId + '\'' +
+				", roomId='" + roomId + '\'' +
+				", sessionId='" + sessionId + '\'' +
+				", userId='" + userId + '\'' +
+				", output=" + output +
+				", config=" + config +
+				", extra='" + extra + '\'' +
+				", code=" + code +
+				", errorMessage='" + errorMessage + '\'' +
+				'}';
+	}
+}

+ 32 - 0
mec-im/src/main/java/com/ym/pojo/RecordNotifyConfig.java

@@ -0,0 +1,32 @@
+package com.ym.pojo;
+
+import lombok.Data;
+
+@Data
+public class RecordNotifyConfig {
+    //录制启动模式: 1 自动启动录制任务
+    Integer trigger;
+
+    //Mix模式下布局:2 悬浮布局(默认) 3 自适应布局
+    Integer mixLayout;
+
+    //文件切片时间(分钟)
+    Integer slicesMin;
+
+    //设置的音频文件格式
+    String audioFormat;
+
+    //设置的视频文件格式
+    String videoFormat;
+
+    @Override
+    public String toString() {
+        return "RecordNotifyConfig{" +
+                "trigger=" + trigger +
+                ", mixLayout=" + mixLayout +
+                ", slicesMin=" + slicesMin +
+                ", audioFormat='" + audioFormat + '\'' +
+                ", videoFormat='" + videoFormat + '\'' +
+                '}';
+    }
+}

+ 57 - 0
mec-im/src/main/java/com/ym/pojo/RecordNotifyOutput.java

@@ -0,0 +1,57 @@
+package com.ym.pojo;
+
+import lombok.Data;
+
+@Data
+public class RecordNotifyOutput {
+    //缓存的文件名
+    String fileName;
+    //音频采样率,如 48000。如果当前录制任务没有录制音频,该字段为空
+    Long audioSample;
+    //视频分辨率,如 640x480
+    String videoResoulation;
+    //切片生成的录制文件大小
+    Integer fileSize;
+    //已上传到的第三方存储的 URL
+    String fileUrl;
+
+    public String getFileName() {
+        return fileName;
+    }
+
+    public void setFileName(String fileName) {
+        this.fileName = fileName;
+    }
+
+    public Long getAudioSample() {
+        return audioSample;
+    }
+
+    public void setAudioSample(Long audioSample) {
+        this.audioSample = audioSample;
+    }
+
+    public String getVideoResoulation() {
+        return videoResoulation;
+    }
+
+    public void setVideoResoulation(String videoResoulation) {
+        this.videoResoulation = videoResoulation;
+    }
+
+    public Integer getFileSize() {
+        return fileSize;
+    }
+
+    public void setFileSize(Integer fileSize) {
+        this.fileSize = fileSize;
+    }
+
+    public String getFileUrl() {
+        return fileUrl;
+    }
+
+    public void setFileUrl(String fileUrl) {
+        this.fileUrl = fileUrl;
+    }
+}

+ 1 - 0
mec-im/src/main/java/com/ym/pojo/UpgradeRoleTaskInfo.java

@@ -1,5 +1,6 @@
 package com.ym.pojo;
 
+import com.ym.enums.RoleEnum;
 import lombok.Data;
 
 import java.io.Serializable;

+ 211 - 0
mec-im/src/main/java/com/ym/service/Impl/LiveRoomServiceImpl.java

@@ -0,0 +1,211 @@
+package com.ym.service.Impl;
+
+import com.alibaba.fastjson.JSONObject;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.ym.http.HttpHelper;
+import com.ym.mec.biz.dal.entity.ImLiveRoomVideo;
+import com.ym.mec.biz.service.ImLiveRoomVideoService;
+import com.ym.mec.common.entity.ImRoomMessage;
+import com.ym.mec.common.exception.BizException;
+import com.ym.mec.im.IMHelper;
+import com.ym.pojo.IMApiResultInfo;
+import com.ym.pojo.RecordConfig;
+import com.ym.pojo.RecordNotify;
+import com.ym.service.LiveRoomService;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.redisson.api.RBucket;
+import org.redisson.api.RedissonClient;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.net.HttpURLConnection;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * @author hgw
+ * Created by 2022-02-21
+ */
+@Slf4j
+@Service
+public class LiveRoomServiceImpl implements LiveRoomService {
+
+    @Autowired
+    private IMHelper imHelper;
+    @Autowired
+    private HttpHelper httpHelper;
+    @Autowired
+    private RedissonClient redissonClient;
+    @Autowired
+    private ImLiveRoomVideoService imLiveRoomVideoService;
+
+    /**
+     * 创建房间-聊天室
+     *
+     * @param roomId   房间Uid
+     * @param roomName 房间名称
+     */
+    public IMApiResultInfo createLiveRoom(String roomId, String roomName) {
+        IMApiResultInfo resultInfo;
+        try {
+            resultInfo = imHelper.createChatRoom(roomId, roomName);
+        } catch (Exception e) {
+            log.error("create chatRoom error >>>", e.getCause());
+            throw new BizException("创建聊天室失败!");
+        }
+        if (!resultInfo.isSuccess()) {
+            log.error("create chatRoom error: {}", resultInfo.getErrorMessage());
+            throw new BizException("创建聊天室失败!");
+        }
+        log.info("create chatRoom success: {}", roomId);
+        return resultInfo;
+    }
+
+    /**
+     * 销毁房间-聊天室
+     *
+     * @param roomId 房间Uid
+     */
+    public IMApiResultInfo destroyLiveRoom(String roomId) {
+        //删除服务器房间
+        List<String> deleteRoomIds = new ArrayList<String>() {{
+            add(roomId);
+        }};
+        IMApiResultInfo resultInfo;
+        try {
+            resultInfo = imHelper.deleteChrm(deleteRoomIds);
+        } catch (Exception e) {
+            throw new BizException("关闭聊天室失败!");
+        }
+        if (!resultInfo.isSuccess()) {
+            log.error("destroy chatRoom error: {}", resultInfo.getErrorMessage());
+            throw new BizException("关闭聊天室失败!");
+        }
+        return resultInfo;
+    }
+
+    /**
+     * 发送消息
+     *
+     * @param message
+     */
+    public IMApiResultInfo publishRoomMessage(ImRoomMessage message) {
+        log.info("publishRoomMessage message : {}", JSONObject.toJSONString(message));
+        IMApiResultInfo resultInfo;
+        try {
+            resultInfo = imHelper.publishRoomMessage(message.getFromUserId(), message.getToChatroomId(), message);
+        } catch (Exception e) {
+            throw new BizException("消息发送失败" + e.getMessage());
+        }
+        if (!resultInfo.isSuccess()) {
+            log.error("publishRoomMessage chatRoom error: {}", resultInfo.getErrorMessage());
+            throw new BizException("消息发送失败!");
+        }
+        return resultInfo;
+    }
+
+    @Override
+    public void startRecord(String roomId) throws Exception {
+        log.error("开始录制直播:roomId : {} ", roomId);
+        JSONObject paramJson = new JSONObject();
+        paramJson.put("sessionId", getRoomSessionId(roomId));
+        paramJson.put("config", new RecordConfig());
+        String body = paramJson.toJSONString();
+        HttpURLConnection conn = httpHelper.createIMRtcPostHttpConnection("/rtc/record/start.json", "application/json", roomId);
+        httpHelper.setBodyParameter(body, conn);
+        String returnResult = httpHelper.returnResult(conn, body);
+        JSONObject resultObject = JSONObject.parseObject(returnResult);
+        String code = resultObject.getString("code");
+        if (!"200".equals(code)) {
+            log.error("直播视频录制失败:resultInfo : {} ", returnResult);
+        }
+        String recordId = resultObject.getString("recordId");
+        ImLiveRoomVideo video = imLiveRoomVideoService.getOne(new QueryWrapper<ImLiveRoomVideo>()
+                .eq("room_uid_", roomId)
+                .eq("record_id_", recordId)
+                .eq("type", 0));
+        if (Objects.nonNull(video)) {
+            return;
+        } else {
+            video = new ImLiveRoomVideo();
+        }
+        video.setRoomUid(roomId);
+        video.setRecordId(recordId);
+        video.setStartTime(new Date());
+        video.setType(0);
+        video.setCreatedTime(new Date());
+        imLiveRoomVideoService.save(video);
+    }
+
+    @Override
+    public void stopRecord(String roomId) throws Exception {
+        JSONObject paramJson = new JSONObject();
+        paramJson.put("sessionId", getRoomSessionId(roomId));
+        String body = paramJson.toJSONString();
+        HttpURLConnection conn = httpHelper.createIMRtcPostHttpConnection("/rtc/record/stop.json", "application/json", roomId);
+        httpHelper.setBodyParameter(body, conn);
+        redissonClient.getBucket("sessionId:" + roomId).delete();
+        log.info("结束录制直播 roomId :{},{}", roomId, httpHelper.returnResult(conn, body));
+    }
+
+    @Override
+    public void recordSync(RecordNotify recordNotify) {
+        if (recordNotify.getCode().equals(200)) {
+            if (Objects.nonNull(recordNotify.getType()) && recordNotify.getType() == 4) {
+                //云端录制文件地址
+                String fileUrl = recordNotify.getOutput().getFileUrl();
+                String roomId = recordNotify.getRoomId();
+                //写入数据库
+                try {
+                    ImLiveRoomVideo video = imLiveRoomVideoService.getOne(new QueryWrapper<ImLiveRoomVideo>()
+                            .eq("room_uid_", roomId)
+                            .eq("record_id_", recordNotify.getRecordId())
+                            .eq("type", 0));
+                    if (Objects.isNull(video)) {
+                        return;
+                    }
+                    video.setEndTime(new Date());
+                    video.setType(2);
+                    video.setUrl(fileUrl);
+                    video.setCreatedTime(new Date());
+                    imLiveRoomVideoService.updateById(video);
+                } catch (Exception e) {
+                    log.error("recordSync >>>>  error : {}", e.getMessage());
+                }
+            }
+        }
+    }
+
+    public String getRoomSessionId(String roomId) {
+        RBucket<String> bucket = redissonClient.getBucket("sessionId:" + roomId);
+        String sessionId = bucket.get();
+        if (StringUtils.isNotEmpty(sessionId)) {
+            return sessionId;
+        }
+        JSONObject jsonObject = new JSONObject();
+        jsonObject.put("roomId", roomId);
+
+        HttpURLConnection conn = null;
+        try {
+            conn = httpHelper.createIMRtcPostHttpConnection("/rtc/room/query.json", "application/json", null);
+            httpHelper.setBodyParameter(jsonObject.toJSONString(), conn);
+            String returnResult = httpHelper.returnResult(conn, jsonObject.toJSONString());
+            JSONObject resultObject = JSONObject.parseObject(returnResult);
+            String code = resultObject.get("code").toString();
+            if ("200".equals(code)) {
+                sessionId = resultObject.get("sessionId").toString();
+                bucket.set(sessionId);
+            } else {
+                log.error("获取sessionId失败 returnResult:{}", returnResult);
+                throw new BizException("获取sessionId失败");
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return sessionId;
+    }
+
+}

+ 187 - 184
mec-im/src/main/java/com/ym/service/Impl/RoomServiceImpl.java

@@ -13,6 +13,9 @@ import com.ym.dao.RoomDao;
 import com.ym.dao.RoomMemberDao;
 import com.ym.dao.UserDao;
 import com.ym.dao.WhiteboardDao;
+import com.ym.enums.ActionEnum;
+import com.ym.enums.DeviceTypeEnum;
+import com.ym.enums.RoleEnum;
 import com.ym.job.ScheduleManager;
 import com.ym.mec.auth.api.client.SysUserFeignService;
 import com.ym.mec.auth.api.entity.SysUser;
@@ -49,8 +52,8 @@ import java.util.*;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
-import static com.ym.pojo.RoleEnum.RoleStudent;
-import static com.ym.pojo.RoleEnum.RoleTeacher;
+import static com.ym.enums.RoleEnum.RoleStudent;
+import static com.ym.enums.RoleEnum.RoleTeacher;
 
 /**
  * Created by super_zou on 2019/11/28.
@@ -99,25 +102,25 @@ public class RoomServiceImpl implements RoomService {
     @Autowired
     private TenantAssetsInfoService tenantAssetsInfoService;
     @Autowired
-    private RedisTemplate<String,String> redisTemplate;
+    private RedisTemplate<String, String> redisTemplate;
 
     @Override
-    public Integer getCurrentCourseId(String roomId){
+    public Integer getCurrentCourseId(String roomId) {
         CourseSchedule courseSchedule = courseScheduleDao.get(Long.parseLong(roomId));
         //是否是连堂课
-        String continueCourseTime = sysTenantConfigService.getTenantConfigValue(SysConfigService.ONLINE_CONTINUE_COURSE_TIME,courseSchedule.getTenantId());
+        String continueCourseTime = sysTenantConfigService.getTenantConfigValue(SysConfigService.ONLINE_CONTINUE_COURSE_TIME, courseSchedule.getTenantId());
         if (StringUtils.isEmpty(continueCourseTime)) {
             continueCourseTime = "5";
         }
         CourseSchedule schedule = courseSchedule;
         //如果当前课程是连堂课,那么获取第一节课的课程编号
-        while (true){
+        while (true) {
             String classDate = DateUtil.format(schedule.getClassDate(), DateUtil.DEFAULT_PATTERN);
             String startClassTime = DateUtil.format(schedule.getStartClassTime(), DateUtil.EXPANDED_TIME_FORMAT);
-            schedule = courseScheduleDao.getFirstCourse(schedule.getClassGroupId(),classDate + " " + startClassTime,schedule.getActualTeacherId(),continueCourseTime);
-            if(schedule != null){
+            schedule = courseScheduleDao.getFirstCourse(schedule.getClassGroupId(), classDate + " " + startClassTime, schedule.getActualTeacherId(), continueCourseTime);
+            if (schedule != null) {
                 roomId = schedule.getId().toString();
-            }else {
+            } else {
                 break;
             }
         }
@@ -125,20 +128,20 @@ public class RoomServiceImpl implements RoomService {
     }
 
     @Transactional(rollbackFor = Exception.class)
-    public String getCloseNetworkRoomTime(CourseSchedule courseSchedule,String continueCourseTime){
-        String autoCloseNetworkRoomTime = sysTenantConfigService.getTenantConfigValue(SysConfigService.COURSE_AFTER_BUFFER_TIME,courseSchedule.getTenantId());
-        if(StringUtils.isEmpty(autoCloseNetworkRoomTime)){
+    public String getCloseNetworkRoomTime(CourseSchedule courseSchedule, String continueCourseTime) {
+        String autoCloseNetworkRoomTime = sysTenantConfigService.getTenantConfigValue(SysConfigService.COURSE_AFTER_BUFFER_TIME, courseSchedule.getTenantId());
+        if (StringUtils.isEmpty(autoCloseNetworkRoomTime)) {
             autoCloseNetworkRoomTime = "15";
         }
         CourseSchedule schedule = courseSchedule;
         //如果当前课程是连堂课,那么获取第一节课的课程编号
-        while (true){
+        while (true) {
             String classDate = DateUtil.format(schedule.getClassDate(), DateUtil.DEFAULT_PATTERN);
             String endClassTime = DateUtil.format(schedule.getEndClassTime(), DateUtil.EXPANDED_TIME_FORMAT);
-            schedule = courseScheduleDao.getLastCourse(schedule.getClassGroupId(),classDate + " " + endClassTime,schedule.getActualTeacherId(),continueCourseTime);
-            if(schedule != null){
-                autoCloseNetworkRoomTime = DateUtil.minutesBetween(new Date(),schedule.getEndClassTime()) + "";
-            }else {
+            schedule = courseScheduleDao.getLastCourse(schedule.getClassGroupId(), classDate + " " + endClassTime, schedule.getActualTeacherId(), continueCourseTime);
+            if (schedule != null) {
+                autoCloseNetworkRoomTime = DateUtil.minutesBetween(new Date(), schedule.getEndClassTime()) + "";
+            } else {
                 break;
             }
         }
@@ -155,57 +158,57 @@ public class RoomServiceImpl implements RoomService {
 
         Teacher teacher = teacherDao.get(Integer.parseInt(userId));
         CourseSchedule courseSchedule = courseScheduleDao.get(Long.parseLong(roomId));
-        if(courseSchedule.getTeachMode() == TeachModeEnum.OFFLINE){
-            return new BaseResponse(ErrorEnum.JOIN_ROOM_ERROR,ErrorEnum.JOIN_ROOM_ERROR.getErrMsg(),null);
+        if (courseSchedule.getTeachMode() == TeachModeEnum.OFFLINE) {
+            return new BaseResponse(ErrorEnum.JOIN_ROOM_ERROR, ErrorEnum.JOIN_ROOM_ERROR.getErrMsg(), null);
         }
         Date curTime = DateTimeUtils.currentUTC();
         //是否提前进入教室
-        String courseBeforeBufferTime = sysTenantConfigService.getTenantConfigValue(SysConfigService.COURSE_BEFORE_BUFFER_TIME,courseSchedule.getTenantId());
+        String courseBeforeBufferTime = sysTenantConfigService.getTenantConfigValue(SysConfigService.COURSE_BEFORE_BUFFER_TIME, courseSchedule.getTenantId());
         if (StringUtils.isEmpty(courseBeforeBufferTime)) {
             courseBeforeBufferTime = "5";
         }
         Date addMinutes = DateUtil.addMinutes(curTime, Integer.parseInt(courseBeforeBufferTime));
-        if(courseSchedule.getStartClassTime().compareTo(addMinutes) > 0 ){
-            return new BaseResponse(ErrorEnum.ROOM_NOT_START,ErrorEnum.ROOM_NOT_START.getErrMsg(),null);
+        if (courseSchedule.getStartClassTime().compareTo(addMinutes) > 0) {
+            return new BaseResponse(ErrorEnum.ROOM_NOT_START, ErrorEnum.ROOM_NOT_START.getErrMsg(), null);
 //            throw new BizException("网络教室暂未开启,请在{}分钟后进入教室",DateUtil.minutesBetween(addMinutes,courseSchedule.getStartClassTime()));
         }
         final TenantAssetsInfo one = tenantAssetsInfoService.getOne(new WrapperUtil<TenantAssetsInfo>()
                 .hasEq("tenant_id_", courseSchedule.getTenantId())
                 .queryWrapper()
                 .gt("balance_", 0));
-        if(one == null){
-            return new BaseResponse(ErrorEnum.CLOUD_BALANCE_NOT_FEE,ErrorEnum.CLOUD_BALANCE_NOT_FEE.getErrMsg(),null);
+        if (one == null) {
+            return new BaseResponse(ErrorEnum.CLOUD_BALANCE_NOT_FEE, ErrorEnum.CLOUD_BALANCE_NOT_FEE.getErrMsg(), null);
         }
 
         //是否是连堂课
-        String continueCourseTime = sysTenantConfigService.getTenantConfigValue(SysConfigService.ONLINE_CONTINUE_COURSE_TIME,courseSchedule.getTenantId());
+        String continueCourseTime = sysTenantConfigService.getTenantConfigValue(SysConfigService.ONLINE_CONTINUE_COURSE_TIME, courseSchedule.getTenantId());
         if (StringUtils.isEmpty(continueCourseTime)) {
             continueCourseTime = "5";
         }
 
         RoomResult roomResult = new RoomResult();
-        roomResult.setAutoCloseNetworkRoomTime(this.getCloseNetworkRoomTime(courseSchedule,continueCourseTime));
+        roomResult.setAutoCloseNetworkRoomTime(this.getCloseNetworkRoomTime(courseSchedule, continueCourseTime));
         CourseSchedule schedule = courseSchedule;
         //如果当前课程是连堂课,那么获取第一节课的课程编号
-        while (true){
+        while (true) {
             String classDate = DateUtil.format(schedule.getClassDate(), DateUtil.DEFAULT_PATTERN);
             String startClassTime = DateUtil.format(schedule.getStartClassTime(), DateUtil.EXPANDED_TIME_FORMAT);
-            schedule = courseScheduleDao.getFirstCourse(schedule.getClassGroupId(),classDate + " " + startClassTime,schedule.getActualTeacherId(),continueCourseTime);
-            if(schedule != null){
+            schedule = courseScheduleDao.getFirstCourse(schedule.getClassGroupId(), classDate + " " + startClassTime, schedule.getActualTeacherId(), continueCourseTime);
+            if (schedule != null) {
                 roomId = schedule.getId().toString();
 //                roomResult.setAutoCloseFlag(false);
-            }else {
+            } else {
                 break;
             }
         }
         Long courseId = Long.parseLong(roomId);
         //记录用户实际选择的房间
-        if(courseSchedule.getGroupType() == GroupType.COMM){
+        if (courseSchedule.getGroupType() == GroupType.COMM) {
             roomId = "I" + roomId;
-        }else {
+        } else {
             roomId = "S" + roomId;
         }
-        redisTemplate.opsForValue().setIfAbsent(roomId + userId,courseSchedule.getId().toString());
+        redisTemplate.opsForValue().setIfAbsent(roomId + userId, courseSchedule.getId().toString());
         log.info("joinRoom current: roomId={}, userId={}", roomId, userId);
         RoleEnum roleEnum;
 
@@ -216,13 +219,13 @@ public class RoomServiceImpl implements RoomService {
         if (member == null) {
             int count = roomMemberDao.countByRidAndExcludeRole(roomId, RoleEnum.RoleAudience.getValue());
             if (count == roomProperties.getMaxCount()) {
-                log.info("join error Over max count: roomId = {}, userId = {}", roomId,userId);
-                return new BaseResponse(ErrorEnum.ERR_OVER_MAX_COUNT,ErrorEnum.ERR_OVER_MAX_COUNT.getErrMsg(),null);
+                log.info("join error Over max count: roomId = {}, userId = {}", roomId, userId);
+                return new BaseResponse(ErrorEnum.ERR_OVER_MAX_COUNT, ErrorEnum.ERR_OVER_MAX_COUNT.getErrMsg(), null);
             }
-            if(teacher != null && teacher.getId().equals(courseSchedule.getActualTeacherId())){
+            if (teacher != null && teacher.getId().equals(courseSchedule.getActualTeacherId())) {
                 roleEnum = RoleTeacher;
                 userName = sysUser.getRealName();
-            }else {
+            } else {
                 roleEnum = RoleStudent;
                 userName = sysUser.getUsername();
             }
@@ -230,11 +233,11 @@ public class RoomServiceImpl implements RoomService {
             userResult.setCamera(true);
             userResult.setHandUpOn(false);
             userResult.setJoinTime(curTime);
-            saveRoomMember(userId,sysUser.getAvatar(), userName, roomId, roleEnum.getValue(), curTime);
+            saveRoomMember(userId, sysUser.getAvatar(), userName, roomId, roleEnum.getValue(), curTime);
         } else {
             roleEnum = RoleEnum.getEnumByValue(member.getRole());
-            if(roleEnum == RoleTeacher){
-                courseScheduleStudentPaymentDao.adjustPlayMidi(courseId,null,null);
+            if (roleEnum == RoleTeacher) {
+                courseScheduleStudentPaymentDao.adjustPlayMidi(courseId, null, null);
             }
             userName = member.getName();
             userResult.setCamera(member.isCamera());
@@ -243,7 +246,7 @@ public class RoomServiceImpl implements RoomService {
         }
         imHelper.joinGroup(new String[]{userId}, roomId, roomId);
 
-        List<CourseScheduleStudentMusicScore> scheduleStudentMusicScores = courseScheduleStudentMusicScoreDao.queryByScoreIdAndCourseId(null, courseId,null, null, null);
+        List<CourseScheduleStudentMusicScore> scheduleStudentMusicScores = courseScheduleStudentMusicScoreDao.queryByScoreIdAndCourseId(null, courseId, null, null, null);
 
         Room room = roomDao.findByRid(roomId);
         String display = "";
@@ -251,13 +254,13 @@ public class RoomServiceImpl implements RoomService {
             display = "display://type=1?userId=" + userId + "?uri=";
         } else if (roleEnum == RoleEnum.RoleAssistant && display.isEmpty()) {
             display = "display://type=0?userId=" + userId + "?uri=";
-        }else {
+        } else {
             ExamSongDownloadData examSongDownloadData;
             String json = courseScheduleStudentPaymentDao.getExamJsonByCourseIdAndUserId(courseId, sysUser.getId());
-            if(StringUtils.isEmpty(json)){
+            if (StringUtils.isEmpty(json)) {
                 examSongDownloadData = new ExamSongDownloadData();
-            }else {
-                examSongDownloadData = JSON.parseObject(json,ExamSongDownloadData.class);
+            } else {
+                examSongDownloadData = JSON.parseObject(json, ExamSongDownloadData.class);
             }
             //获取学员曲目下载状态
             userResult.setExamSongDownloadJson(examSongDownloadData);
@@ -266,7 +269,7 @@ public class RoomServiceImpl implements RoomService {
             }
         }
         //已下载的伴奏列表
-        if(scheduleStudentMusicScores != null && scheduleStudentMusicScores.size() > 0){
+        if (scheduleStudentMusicScores != null && scheduleStudentMusicScores.size() > 0) {
             List<CourseScheduleStudentMusicScore> musicScores = scheduleStudentMusicScores.stream().filter(e -> e.getUserId().equals(sysUser.getId())).collect(Collectors.toList());
             String toJSONString = JSON.toJSONString(musicScores, SerializerFeature.DisableCircularReferenceDetect);
             List<CourseScheduleStudentMusicScore> lists = JSON.parseArray(toJSONString, CourseScheduleStudentMusicScore.class);
@@ -277,24 +280,24 @@ public class RoomServiceImpl implements RoomService {
         userResult.setUserId(userId);
         userResult.setRole(roleEnum.getValue());
         //获取节拍器信息
-        String midi = courseScheduleStudentPaymentDao.getMidiByCourseIdAndUserId(courseId.toString(),userId);
-        userResult.setPlayMidiJson(JSONObject.parseObject(midi,CustomMessage.class));
+        String midi = courseScheduleStudentPaymentDao.getMidiByCourseIdAndUserId(courseId.toString(), userId);
+        userResult.setPlayMidiJson(JSONObject.parseObject(midi, CustomMessage.class));
 
         //获取当前课程剩余时长
 //        String classDate = DateUtil.format(courseSchedule.getClassDate(), DateUtil.DEFAULT_PATTERN);
 //        String endClassTime = DateUtil.format(courseSchedule.getEndClassTime(), DateUtil.EXPANDED_TIME_FORMAT);
-        roomResult.setSurplusTime(DateUtil.secondsBetween(new Date(),courseSchedule.getEndClassTime()));
+        roomResult.setSurplusTime(DateUtil.secondsBetween(new Date(), courseSchedule.getEndClassTime()));
 
         roomResult.setUserInfo(userResult);
         roomResult.setDisplay(display);
         roomResult.setRoomId(roomId);
 
         List<RoomMember> roomMemberList = roomMemberDao.findByRid(roomId);
-        if(roomMemberList != null && roomMemberList.size() > 0){
+        if (roomMemberList != null && roomMemberList.size() > 0) {
             Set<String> userIds = roomMemberList.stream().map(e -> e.getUid()).collect(Collectors.toSet());
-            Map<Integer,String> midiMap = MapUtil.convertMybatisMap(courseScheduleStudentPaymentDao.queryMidiByUserIdsAndCourseId(userIds,courseId.toString()));
-            Map<Integer,String> examSongMap = MapUtil.convertMybatisMap(courseScheduleStudentPaymentDao.queryExamSongByUserIdsAndCourseId(userIds,courseId.toString()));
-            roomResult.setMembers(roomMemberList,midiMap,examSongMap,scheduleStudentMusicScores);
+            Map<Integer, String> midiMap = MapUtil.convertMybatisMap(courseScheduleStudentPaymentDao.queryMidiByUserIdsAndCourseId(userIds, courseId.toString()));
+            Map<Integer, String> examSongMap = MapUtil.convertMybatisMap(courseScheduleStudentPaymentDao.queryExamSongByUserIdsAndCourseId(userIds, courseId.toString()));
+            roomResult.setMembers(roomMemberList, midiMap, examSongMap, scheduleStudentMusicScores);
         }
         roomResult.setWhiteboards(whiteboardDao.findByRid(roomId));
         if (room != null) {
@@ -304,7 +307,7 @@ public class RoomServiceImpl implements RoomService {
         return new BaseResponse(roomResult);
     }
 
-    public RoomMember saveRoomMember(String roomId,String userId){
+    public RoomMember saveRoomMember(String roomId, String userId) {
         SysUser sysUser = sysUserFeignService.queryUserById(Integer.parseInt(userId));
 
         Teacher teacher = teacherDao.get(Integer.parseInt(userId));
@@ -316,10 +319,10 @@ public class RoomServiceImpl implements RoomService {
         RoomMember member = roomMemberDao.findByRidAndUid(roomId, userId);
         String userName;
         if (member == null) {
-            if(teacher != null && teacher.getId().equals(courseSchedule.getActualTeacherId())){
+            if (teacher != null && teacher.getId().equals(courseSchedule.getActualTeacherId())) {
                 roleEnum = RoleTeacher;
                 userName = sysUser.getRealName();
-            }else {
+            } else {
                 roleEnum = RoleStudent;
                 userName = sysUser.getUsername();
             }
@@ -333,28 +336,28 @@ public class RoomServiceImpl implements RoomService {
     @Override
     public void joinRoomFailure(String roomId, String userId) {
         RoomMember roomMember = roomMemberDao.findByRidAndUid(roomId, userId);
-        if(roomMember == null){
-            return ;
+        if (roomMember == null) {
+            return;
         }
         log.info("joinRoomFailure : roomId={}, userId={}", roomId, userId);
         //如果加入失败,删除该用户数据
-        roomMemberDao.deleteUserByRidAndUid(roomId,userId);
+        roomMemberDao.deleteUserByRidAndUid(roomId, userId);
     }
 
     @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
     @Override
-    public void joinRoomSuccess(String roomId,String userId,String deviceNum) throws Exception {
-        log.info("joinRoomSuccess: roomId={}, userId={}, deviceNum={}", roomId,userId,deviceNum);
+    public void joinRoomSuccess(String roomId, String userId, String deviceNum) throws Exception {
+        log.info("joinRoomSuccess: roomId={}, userId={}, deviceNum={}", roomId, userId, deviceNum);
         RoomMember roomMember = roomMemberDao.findByRidAndUid(roomId, userId);
-        if(roomMember == null){
-            roomMember = saveRoomMember(roomId,userId);
+        if (roomMember == null) {
+            roomMember = saveRoomMember(roomId, userId);
         }
-        String joinSuccessKey = "joinRoomSuccess"+ roomId + userId;
+        String joinSuccessKey = "joinRoomSuccess" + roomId + userId;
         Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(joinSuccessKey, roomId, 1l, TimeUnit.SECONDS);
-        if(!aBoolean){
+        if (!aBoolean) {
             RoleEnum roleEnum = RoleEnum.getEnumByValue(roomMember.getRole());
-            if(roleEnum == RoleTeacher && StringUtils.isNotEmpty(deviceNum)){
-                teacherAttendanceService.updateDeviceNum(Integer.parseInt(roomId.substring(1)),userId,deviceNum,null);
+            if (roleEnum == RoleTeacher && StringUtils.isNotEmpty(deviceNum)) {
+                teacherAttendanceService.updateDeviceNum(Integer.parseInt(roomId.substring(1)), userId, deviceNum, null);
 //                signInSuccess(roomMember,deviceNum);
             }
             return;
@@ -364,11 +367,11 @@ public class RoomServiceImpl implements RoomService {
         CourseSchedule schedule = courseScheduleDao.getLock(Long.parseLong(roomId.substring(1)));
 
         String display = "";
-        if(roleEnum == RoleTeacher){
+        if (roleEnum == RoleTeacher) {
             //如果是老师加入房间,调整节拍器状态
-            courseScheduleStudentPaymentDao.adjustPlayMidi(schedule.getId(),null,null);
+            courseScheduleStudentPaymentDao.adjustPlayMidi(schedule.getId(), null, null);
             display = "display://type=1?userId=" + userId + "?uri=";
-        }else if (roleEnum == RoleEnum.RoleAssistant){
+        } else if (roleEnum == RoleEnum.RoleAssistant) {
             display = "display://type=0?userId=" + userId + "?uri=";
         }
         Date curTime = DateTimeUtils.currentUTC();
@@ -380,8 +383,8 @@ public class RoomServiceImpl implements RoomService {
                 log.error("joinRoomSuccess createGroup error: roomId={}, {}", roomId, resultInfo.getErrorMessage());
                 throw new ApiException(ErrorEnum.ERR_CREATE_ROOM_ERROR, resultInfo.getErrorMessage());
             }
-        }else{
-            if(roleEnum == RoleTeacher || roleEnum == RoleEnum.RoleAssistant){
+        } else {
+            if (roleEnum == RoleTeacher || roleEnum == RoleEnum.RoleAssistant) {
                 updateDisplay(roomId, userId, display, 0);
             }
         }
@@ -401,17 +404,17 @@ public class RoomServiceImpl implements RoomService {
         msg.setCamera(true);
         Boolean playMidi = false;
         Boolean examSong = false;
-        if(roleEnum == RoleStudent){
+        if (roleEnum == RoleStudent) {
             String midiByCourseIdAndUserId = courseScheduleStudentPaymentDao.getMidiByCourseIdAndUserId(schedule.getId().toString(), userId);
             //获取节拍器信息
-            if(StringUtils.isNotEmpty(midiByCourseIdAndUserId)){
+            if (StringUtils.isNotEmpty(midiByCourseIdAndUserId)) {
                 JSONObject jsonObject = JSONObject.parseObject(midiByCourseIdAndUserId);
-                if(jsonObject.get("enable") != null){
+                if (jsonObject.get("enable") != null) {
                     playMidi = Boolean.parseBoolean(jsonObject.get("enable").toString());
                 }
             }
             String examJson = courseScheduleStudentPaymentDao.getExamJsonByCourseIdAndUserId(schedule.getId(), Integer.parseInt(userId));
-            if(StringUtils.isNotEmpty(examJson)){
+            if (StringUtils.isNotEmpty(examJson)) {
                 ExamSongDownloadData examSongDownloadData = JSON.parseObject(examJson, ExamSongDownloadData.class);
                 examSong = examSongDownloadData.getEnable();
             }
@@ -421,30 +424,30 @@ public class RoomServiceImpl implements RoomService {
         msg.setExamSongSwitch(examSong);
         imHelper.publishMessage(userId, roomId, msg);
         log.info("join room success: roomId = {}, userId = {}, role = {}", roomId, userId, roleEnum);
-        signInSuccess(roomMember,deviceNum);
+        signInSuccess(roomMember, deviceNum);
     }
 
-    public void signInSuccess(RoomMember roomMember,String deviceNum) {
+    public void signInSuccess(RoomMember roomMember, String deviceNum) {
         String roomId = roomMember.getRid();
         String userId = roomMember.getUid();
         String currentRoomIdKey = roomId + userId;
 
         Long firstCourseId = Long.parseLong(roomId.substring(1));
         Long currentRoomId;
-        if(redisTemplate.hasKey(currentRoomIdKey)){
+        if (redisTemplate.hasKey(currentRoomIdKey)) {
             currentRoomId = Long.parseLong(redisTemplate.opsForValue().get(currentRoomIdKey));
-        }else {
+        } else {
             log.error("signInFailure: roomId={}, userId={}", roomId, userId);
             currentRoomId = firstCourseId;
         }
-        log.info("signInSuccess: roomId={}, userId={},currentRoomId={}", roomId, userId,currentRoomId);
+        log.info("signInSuccess: roomId={}, userId={},currentRoomId={}", roomId, userId, currentRoomId);
         Integer userIdInt = Integer.parseInt(userId);
 
         RoleEnum roleEnum = RoleEnum.getEnumByValue(roomMember.getRole());
-        if(roleEnum == RoleTeacher){
-            teacherAttendanceService.addTeacherAttendanceSignIn(firstCourseId,userIdInt,currentRoomId,deviceNum);
-        }else {
-            studentAttendanceService.addStudentAttendanceSignIn(firstCourseId,userIdInt,currentRoomId);
+        if (roleEnum == RoleTeacher) {
+            teacherAttendanceService.addTeacherAttendanceSignIn(firstCourseId, userIdInt, currentRoomId, deviceNum);
+        } else {
+            studentAttendanceService.addStudentAttendanceSignIn(firstCourseId, userIdInt, currentRoomId);
         }
         redisTemplate.delete(roomId + userId);
     }
@@ -473,30 +476,30 @@ public class RoomServiceImpl implements RoomService {
         return roomMember;
     }
 
-    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class,isolation = Isolation.READ_COMMITTED)
+    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class, isolation = Isolation.READ_COMMITTED)
     @Override
-    public void leaveRoomSuccess(String roomId,String userId,String deviceNum) throws Exception {
-        log.info("leaveRoomSuccess: roomId={}, userId={},deviceNum={}", roomId,userId,deviceNum);
+    public void leaveRoomSuccess(String roomId, String userId, String deviceNum) throws Exception {
+        log.info("leaveRoomSuccess: roomId={}, userId={},deviceNum={}", roomId, userId, deviceNum);
         Integer firstCourseId = Integer.parseInt(roomId.substring(1));
         RoleEnum roleEnum;
         int parseInt = Integer.parseInt(userId);
         Teacher teacher = teacherDao.get(parseInt);
         CourseSchedule courseSchedule = courseScheduleDao.get(firstCourseId.longValue());
-        if(teacher != null && teacher.getId().equals(courseSchedule.getActualTeacherId())){
+        if (teacher != null && teacher.getId().equals(courseSchedule.getActualTeacherId())) {
             roleEnum = RoleTeacher;
-            courseScheduleStudentMusicScoreDao.closePlayStatus(courseSchedule.getId(),null,null);
-        }else {
+            courseScheduleStudentMusicScoreDao.closePlayStatus(courseSchedule.getId(), null, null);
+        } else {
             roleEnum = RoleStudent;
         }
 
-        String leaveSuccessKey = "leaveRoomSuccess"+ roomId + userId;
+        String leaveSuccessKey = "leaveRoomSuccess" + roomId + userId;
         Boolean aBoolean = redisTemplate.opsForValue().setIfAbsent(leaveSuccessKey, roomId, 1l, TimeUnit.SECONDS);
-        log.info("leaveRoomSuccess: roomId={}, userId={},deviceNum={},aBoolean={}", roomId,userId,deviceNum,aBoolean);
-        if(!aBoolean){
-            if (StringUtils.isNotEmpty(deviceNum)){
+        log.info("leaveRoomSuccess: roomId={}, userId={},deviceNum={},aBoolean={}", roomId, userId, deviceNum, aBoolean);
+        if (!aBoolean) {
+            if (StringUtils.isNotEmpty(deviceNum)) {
                 //如果设备号不为空,更新设备号
-                if(roleEnum == RoleTeacher){
-                    teacherAttendanceService.updateDeviceNum(firstCourseId,userId,null,deviceNum);
+                if (roleEnum == RoleTeacher) {
+                    teacherAttendanceService.updateDeviceNum(firstCourseId, userId, null, deviceNum);
 //                    teacherAttendanceService.addTeacherAttendanceSignOut(firstCourseId.longValue(),parseInt,deviceNum);
                 }
             }
@@ -505,14 +508,14 @@ public class RoomServiceImpl implements RoomService {
 
         String username;
         SysUser sysUser = sysUserFeignService.queryUserById(parseInt);
-        if(roleEnum == RoleTeacher){
+        if (roleEnum == RoleTeacher) {
             username = sysUser.getRealName();
-            courseScheduleStudentPaymentDao.adjustPlayMidi(firstCourseId,null,null);
-            courseScheduleStudentPaymentDao.adjustExamSong(firstCourseId.longValue(),null,null);
-            teacherAttendanceService.addTeacherAttendanceSignOut(firstCourseId.longValue(),parseInt,deviceNum);
-        }else {
+            courseScheduleStudentPaymentDao.adjustPlayMidi(firstCourseId, null, null);
+            courseScheduleStudentPaymentDao.adjustExamSong(firstCourseId.longValue(), null, null);
+            teacherAttendanceService.addTeacherAttendanceSignOut(firstCourseId.longValue(), parseInt, deviceNum);
+        } else {
             username = sysUser.getUsername();
-            studentAttendanceService.addStudentAttendanceSignOut(firstCourseId.longValue(),parseInt);
+            studentAttendanceService.addStudentAttendanceSignOut(firstCourseId.longValue(), parseInt);
         }
         Room room = roomDao.findByRid(roomId);
         if (room == null) {
@@ -528,15 +531,15 @@ public class RoomServiceImpl implements RoomService {
             roomDao.deleteByRid(roomId);
             deleteWhiteboardByUser(roomId, userId);
             imHelper.dismiss(userId, roomId);
-            courseScheduleStudentMusicScoreDao.closePlayStatus(courseSchedule.getId(),null,null);
-            log.info("leaveRoomSuccess dismiss the room: {},userId: {}", roomId,userId);
+            courseScheduleStudentMusicScoreDao.closePlayStatus(courseSchedule.getId(), null, null);
+            log.info("leaveRoomSuccess dismiss the room: {},userId: {}", roomId, userId);
         } else {
             roomMemberDao.deleteUserByRidAndUid(roomId, userId);
             MemberChangedMessage msg = new MemberChangedMessage(MemberChangedMessage.Action_Leave, userId, roleEnum.getValue());
             msg.setUserName(username);
             imHelper.publishMessage(userId, roomId, msg);
             imHelper.quit(new String[]{userId}, roomId);
-            log.info("leaveRoomSuccess quit group: roomId={},userId: {}", roomId,userId);
+            log.info("leaveRoomSuccess quit group: roomId={},userId: {}", roomId, userId);
         }
         userDao.deleteByUid(userId);
     }
@@ -632,7 +635,7 @@ public class RoomServiceImpl implements RoomService {
         CheckUtils.checkArgument(roomId != null, "roomId must't be null");
         log.info("kickMember: roomId={}, userId={}", roomId, userId);
         RoomMember roomMember = roomMemberDao.findByRidAndUid(roomId, userId);
-        if(roomMember == null){
+        if (roomMember == null) {
             return true;
         }
 
@@ -861,22 +864,22 @@ public class RoomServiceImpl implements RoomService {
         } else if (data.getHandUpOn() != null) {
             typeEnum = DeviceTypeEnum.HandUp;
             enable = data.getHandUpOn();
-        }else if (data.getExamSongOn() != null) {
+        } else if (data.getExamSongOn() != null) {
             typeEnum = DeviceTypeEnum.ExamSong;
             enable = data.getExamSongOn();
-        }else if (data.getMusicScoreOn() != null) {
+        } else if (data.getMusicScoreOn() != null) {
             typeEnum = DeviceTypeEnum.MusicScore;
             enable = data.getMusicScoreOn();
-            if(enable){
+            if (enable) {
                 //保存伴奏音量
-                roomDao.updateSoundVolumeById(roomId,data.getSoundVolume()==null?0:data.getSoundVolume());
+                roomDao.updateSoundVolumeById(roomId, data.getSoundVolume() == null ? 0 : data.getSoundVolume());
             }
-        }else if (data.getAccompanimentOn() != null) {
+        } else if (data.getAccompanimentOn() != null) {
             typeEnum = DeviceTypeEnum.MusicScoreAccompaniment;
             enable = data.getAccompanimentOn();
-            if(enable){
+            if (enable) {
                 //保存伴奏音量
-                roomDao.updateSoundVolumeById(roomId,data.getSoundVolume()==null?0:data.getSoundVolume());
+                roomDao.updateSoundVolumeById(roomId, data.getSoundVolume() == null ? 0 : data.getSoundVolume());
             }
         } else {
             throw new ApiException(ErrorEnum.ERR_REQUEST_PARA_ERR);
@@ -885,24 +888,24 @@ public class RoomServiceImpl implements RoomService {
         log.info("controlDevice: userId = {}, typeEnum = {}, enable = {} ,roomId = {}", userId, typeEnum, enable, roomId);
 
         if (enable) {
-            if (typeEnum.equals(DeviceTypeEnum.ExamSong)){
+            if (typeEnum.equals(DeviceTypeEnum.ExamSong)) {
                 long scheduleId = Long.parseLong(roomId.substring(1));
                 ExamSongDownloadData msg;
                 String examJson = courseScheduleStudentPaymentDao.getExamJsonByCourseIdAndUserId(scheduleId, Integer.parseInt(userId));
-                if(StringUtils.isEmpty(examJson)){
+                if (StringUtils.isEmpty(examJson)) {
                     throw new BizException("学员伴奏信息异常");
-                }else {
+                } else {
                     msg = JSON.parseObject(examJson, ExamSongDownloadData.class);
                     msg.setEnable(enable);
                 }
-                courseScheduleStudentPaymentDao.adjustExamSong(scheduleId,Integer.parseInt(userId),JSON.toJSONString(msg));
+                courseScheduleStudentPaymentDao.adjustExamSong(scheduleId, Integer.parseInt(userId), JSON.toJSONString(msg));
                 DeviceStateChangedMessage deviceResourceMessage = new DeviceStateChangedMessage(typeEnum.ordinal(), enable);
                 deviceResourceMessage.setUserId(userId);
                 imHelper.publishMessage(authUser.getId().toString(), roomId, deviceResourceMessage, 1);
-            }else if(typeEnum.equals(DeviceTypeEnum.MusicScore)){
+            } else if (typeEnum.equals(DeviceTypeEnum.MusicScore)) {
                 long scheduleId = Long.parseLong(roomId.substring(1));
                 //关闭所有曲目播放
-                courseScheduleStudentMusicScoreDao.closePlayStatus(scheduleId,Integer.parseInt(userId),null);
+                courseScheduleStudentMusicScoreDao.closePlayStatus(scheduleId, Integer.parseInt(userId), null);
                 DeviceStateChangedMessage deviceResourceMessage = new DeviceStateChangedMessage(typeEnum.ordinal(), enable);
                 deviceResourceMessage.setMusicScoreAccompanimentId(data.getMusicScoreAccompanimentId());
                 deviceResourceMessage.setUserId(userId);
@@ -910,17 +913,17 @@ public class RoomServiceImpl implements RoomService {
                 //原音
                 courseScheduleStudentMusicScoreDao.openPlayStatus(scheduleId, data.getMusicScoreAccompanimentId(), Integer.parseInt(userId));
                 imHelper.publishMessage(authUser.getId().toString(), roomId, deviceResourceMessage, 1);
-            }else if(typeEnum.equals(DeviceTypeEnum.MusicScoreAccompaniment)){
+            } else if (typeEnum.equals(DeviceTypeEnum.MusicScoreAccompaniment)) {
                 long scheduleId = Long.parseLong(roomId.substring(1));
                 //关闭所有曲目播放
-                courseScheduleStudentMusicScoreDao.closePlayStatus(scheduleId,Integer.parseInt(userId),null);
-                courseScheduleStudentMusicScoreDao.openAccompanimentPlayStatus(scheduleId,data.getMusicScoreAccompanimentId(),Integer.parseInt(userId));
+                courseScheduleStudentMusicScoreDao.closePlayStatus(scheduleId, Integer.parseInt(userId), null);
+                courseScheduleStudentMusicScoreDao.openAccompanimentPlayStatus(scheduleId, data.getMusicScoreAccompanimentId(), Integer.parseInt(userId));
                 DeviceStateChangedMessage deviceResourceMessage = new DeviceStateChangedMessage(typeEnum.ordinal(), enable);
                 deviceResourceMessage.setMusicScoreAccompanimentId(data.getMusicScoreAccompanimentId());
                 deviceResourceMessage.setUserId(userId);
                 deviceResourceMessage.setSoundVolume(data.getSoundVolume());
                 imHelper.publishMessage(authUser.getId().toString(), roomId, deviceResourceMessage, 1);
-            }else {
+            } else {
                 String ticket = IdentifierUtils.uuid();
                 ControlDeviceTaskInfo taskInfo = new ControlDeviceTaskInfo();
                 taskInfo.setRoomId(roomId);
@@ -940,30 +943,30 @@ public class RoomServiceImpl implements RoomService {
         } else {
             if (typeEnum.equals(DeviceTypeEnum.Camera)) {
                 roomMemberDao.updateCameraByRidAndUid(roomId, userId, false);
-            } else if (typeEnum.equals(DeviceTypeEnum.Microphone)){
+            } else if (typeEnum.equals(DeviceTypeEnum.Microphone)) {
                 roomMemberDao.updateMicByRidAndUid(roomId, userId, false);
-            } else if (typeEnum.equals(DeviceTypeEnum.HandUp)){
+            } else if (typeEnum.equals(DeviceTypeEnum.HandUp)) {
                 roomMemberDao.updateHandByRidAndUid(roomId, userId, false);
-            } else if (typeEnum.equals(DeviceTypeEnum.ExamSong)){
+            } else if (typeEnum.equals(DeviceTypeEnum.ExamSong)) {
                 long scheduleId = Long.parseLong(roomId.substring(1));
                 ExamSongDownloadData msg;
                 String examJson = courseScheduleStudentPaymentDao.getExamJsonByCourseIdAndUserId(scheduleId, Integer.parseInt(userId));
-                if(StringUtils.isEmpty(examJson)){
+                if (StringUtils.isEmpty(examJson)) {
                     throw new BizException("学员伴奏信息异常");
-                }else {
+                } else {
                     msg = JSON.parseObject(examJson, ExamSongDownloadData.class);
                     msg.setEnable(enable);
                 }
-                courseScheduleStudentPaymentDao.adjustExamSong(scheduleId,Integer.parseInt(userId),JSON.toJSONString(msg));
-            }else if(typeEnum.equals(DeviceTypeEnum.MusicScore)){
+                courseScheduleStudentPaymentDao.adjustExamSong(scheduleId, Integer.parseInt(userId), JSON.toJSONString(msg));
+            } else if (typeEnum.equals(DeviceTypeEnum.MusicScore)) {
                 long scheduleId = Long.parseLong(roomId.substring(1));
                 //关闭所有曲目播放
-                courseScheduleStudentMusicScoreDao.closePlayStatus(scheduleId,Integer.parseInt(userId),null);
-            }else if(typeEnum.equals(DeviceTypeEnum.MusicScoreAccompaniment)){
+                courseScheduleStudentMusicScoreDao.closePlayStatus(scheduleId, Integer.parseInt(userId), null);
+            } else if (typeEnum.equals(DeviceTypeEnum.MusicScoreAccompaniment)) {
                 long scheduleId = Long.parseLong(roomId.substring(1));
                 //关闭所有曲目播放
-                courseScheduleStudentMusicScoreDao.closePlayStatus(scheduleId,Integer.parseInt(userId),null);
-            }else {
+                courseScheduleStudentMusicScoreDao.closePlayStatus(scheduleId, Integer.parseInt(userId), null);
+            } else {
                 roomMemberDao.updateMusicByRidAndUid(roomId, userId, false);
             }
             DeviceStateChangedMessage deviceResourceMessage = new DeviceStateChangedMessage(typeEnum.ordinal(), false);
@@ -980,28 +983,28 @@ public class RoomServiceImpl implements RoomService {
     @Override
     @Transactional(rollbackFor = Exception.class)
     public Boolean batchControlDevice(ReqDeviceControlData data) throws Exception {
-        if(data.getExamSongOn() != null || data.getMusicScoreOn() != null || data.getAccompanimentOn() != null){
+        if (data.getExamSongOn() != null || data.getMusicScoreOn() != null || data.getAccompanimentOn() != null) {
             List<BasicUserDto> students = courseScheduleStudentPaymentDao.findStudents(Long.parseLong(data.getRoomId().substring(1)));
-            for (BasicUserDto e:students) {
+            for (BasicUserDto e : students) {
                 data.setUserId(e.getUserId().toString());
                 controlDevice(data);
             }
             return true;
         }
         List<RoomMember> roomMembers;
-        if(StringUtils.isNotEmpty(data.getUserId())){
+        if (StringUtils.isNotEmpty(data.getUserId())) {
             roomMembers = new ArrayList<>();
             String[] split = data.getUserId().split(",");
             for (int i = 0; i < split.length; i++) {
                 roomMembers.add(new RoomMember(split[i]));
             }
-        }else {
+        } else {
             roomMembers = roomMemberDao.findByRidAndRole(data.getRoomId(), RoleStudent.getValue());
         }
-        if(roomMembers.size() == 0){
+        if (roomMembers.size() == 0) {
             return false;
         }
-        for (RoomMember e:roomMembers) {
+        for (RoomMember e : roomMembers) {
             data.setUserId(e.getUid());
             controlDevice(data);
         }
@@ -1020,18 +1023,18 @@ public class RoomServiceImpl implements RoomService {
         ControlDeviceTaskInfo taskInfo = (ControlDeviceTaskInfo) scheduleManager.executeTask(ticket);
         if (taskInfo.getTypeEnum().equals(DeviceTypeEnum.Camera)) {
             roomMemberDao.updateCameraByRidAndUid(roomId, userId, taskInfo.isOnOff());
-        }else if (taskInfo.getTypeEnum().equals(DeviceTypeEnum.ExamSong)) {
+        } else if (taskInfo.getTypeEnum().equals(DeviceTypeEnum.ExamSong)) {
             long scheduleId = Long.parseLong(roomId.substring(1));
             ExamSongDownloadData msg;
             String examJson = courseScheduleStudentPaymentDao.getExamJsonByCourseIdAndUserId(scheduleId, authUser.getId());
-            if(StringUtils.isEmpty(examJson)){
+            if (StringUtils.isEmpty(examJson)) {
                 throw new BizException("学员伴奏信息异常");
-            }else {
+            } else {
                 msg = JSON.parseObject(examJson, ExamSongDownloadData.class);
                 msg.setEnable(true);
             }
-            courseScheduleStudentPaymentDao.adjustExamSong(scheduleId,authUser.getId(),JSON.toJSONString(msg));
-        }else {
+            courseScheduleStudentPaymentDao.adjustExamSong(scheduleId, authUser.getId(), JSON.toJSONString(msg));
+        } else {
             roomMemberDao.updateMicByRidAndUid(roomId, userId, taskInfo.isOnOff());
         }
         ControlDeviceNotifyMessage msg = new ControlDeviceNotifyMessage(ActionEnum.Approve.ordinal());
@@ -1078,13 +1081,13 @@ public class RoomServiceImpl implements RoomService {
         } else if (data.getMusicModeOn() != null) {
             type = DeviceTypeEnum.MusicMode;
             enable = data.getMusicModeOn();
-        }  else if (data.getHandUpOn() != null) {
+        } else if (data.getHandUpOn() != null) {
             type = DeviceTypeEnum.HandUp;
             enable = data.getHandUpOn();
-        }  else if (data.getExamSongOn() != null) {
+        } else if (data.getExamSongOn() != null) {
             type = DeviceTypeEnum.ExamSong;
             enable = data.getExamSongOn();
-        }else {
+        } else {
             throw new ApiException(ErrorEnum.ERR_REQUEST_PARA_ERR);
         }
         SysUser authUser = sysUserFeignService.queryUserInfo();
@@ -1093,22 +1096,22 @@ public class RoomServiceImpl implements RoomService {
         DeviceStateChangedMessage deviceResourceMessage = new DeviceStateChangedMessage(type.ordinal(), enable);
         if (type.equals(DeviceTypeEnum.Camera)) {
             roomMemberDao.updateCameraByRidAndUid(roomId, userId, enable);
-        } else if (type.equals(DeviceTypeEnum.Microphone)){
+        } else if (type.equals(DeviceTypeEnum.Microphone)) {
             roomMemberDao.updateMicByRidAndUid(roomId, userId, enable);
-        } else if (type.equals(DeviceTypeEnum.HandUp)){
+        } else if (type.equals(DeviceTypeEnum.HandUp)) {
             roomMemberDao.updateHandByRidAndUid(roomId, userId, enable);
-        } else if (type.equals(DeviceTypeEnum.ExamSong)){
+        } else if (type.equals(DeviceTypeEnum.ExamSong)) {
             long scheduleId = Long.parseLong(roomId.substring(1));
             ExamSongDownloadData msg;
             String examJson = courseScheduleStudentPaymentDao.getExamJsonByCourseIdAndUserId(scheduleId, authUser.getId());
-            if(StringUtils.isEmpty(examJson)){
+            if (StringUtils.isEmpty(examJson)) {
                 throw new BizException("学员伴奏信息异常");
-            }else {
+            } else {
                 msg = JSON.parseObject(examJson, ExamSongDownloadData.class);
                 msg.setEnable(enable);
             }
-            courseScheduleStudentPaymentDao.adjustExamSong(scheduleId,authUser.getId(),JSON.toJSONString(msg));
-        }else {
+            courseScheduleStudentPaymentDao.adjustExamSong(scheduleId, authUser.getId(), JSON.toJSONString(msg));
+        } else {
             roomMemberDao.updateMusicByRidAndUid(roomId, userId, enable);
         }
         Room room = roomDao.findByRid(roomId);
@@ -1124,13 +1127,13 @@ public class RoomServiceImpl implements RoomService {
     public List<RoomResult.MemberResult> getMembers(String roomId) throws Exception {
         CheckUtils.checkArgument(roomId != null, "roomId must't be null");
         List<RoomMember> roomMemberList = roomMemberDao.findByRid(roomId);
-        if(roomMemberList != null && roomMemberList.size() > 0){
+        if (roomMemberList != null && roomMemberList.size() > 0) {
             List<CourseScheduleStudentMusicScore> scheduleStudentMusicScores = courseScheduleStudentMusicScoreDao.queryByScoreIdAndCourseId(null, Long.parseLong(roomId.substring(1)), null, null, null);
             RoomResult roomResult = new RoomResult();
             Set<String> userIds = roomMemberList.stream().map(e -> e.getUid()).collect(Collectors.toSet());
-            Map<Integer,String> midiMap = MapUtil.convertMybatisMap(courseScheduleStudentPaymentDao.queryMidiByUserIdsAndCourseId(userIds,roomId.substring(1)));
-            Map<Integer,String> examSongMap = MapUtil.convertMybatisMap(courseScheduleStudentPaymentDao.queryExamSongByUserIdsAndCourseId(userIds,roomId.substring(1)));
-            roomResult.setMembers(roomMemberList,midiMap,examSongMap,scheduleStudentMusicScores);
+            Map<Integer, String> midiMap = MapUtil.convertMybatisMap(courseScheduleStudentPaymentDao.queryMidiByUserIdsAndCourseId(userIds, roomId.substring(1)));
+            Map<Integer, String> examSongMap = MapUtil.convertMybatisMap(courseScheduleStudentPaymentDao.queryExamSongByUserIdsAndCourseId(userIds, roomId.substring(1)));
+            roomResult.setMembers(roomMemberList, midiMap, examSongMap, scheduleStudentMusicScores);
             return roomResult.getMembers();
         }
         return null;
@@ -1506,7 +1509,7 @@ public class RoomServiceImpl implements RoomService {
                         roomMemberDao.deleteUserByRidAndUid(member.getRid(), member.getUid());
                         roomDao.deleteByRid(member.getRid());
                         deleteWhiteboardByUser(member.getRid(), member.getUid());
-                        log.info("dismiss the room: {},userId: {}", member.getRid(),userId);
+                        log.info("dismiss the room: {},userId: {}", member.getRid(), userId);
                     } else {
                         log.error("{} exit {} room error: {}", member.getUid(), member.getRid(), apiResultInfo.getErrorMessage());
                     }
@@ -1535,17 +1538,17 @@ public class RoomServiceImpl implements RoomService {
     @Transactional(rollbackFor = Exception.class)
     public void sendImPlayMidiMessage(PlayMidiMessageData playMidiMessageData) throws Exception {
         SysUser sysUser = sysUserFeignService.queryUserInfo();
-        if(sysUser == null){
+        if (sysUser == null) {
             throw new BizException("用户信息获取失败");
         }
         String content = playMidiMessageData.getContent();
         String roomId = playMidiMessageData.getRoomId();
-        CustomMessage customMessage = JSONObject.parseObject(content,CustomMessage.class);
+        CustomMessage customMessage = JSONObject.parseObject(content, CustomMessage.class);
         String userId = sysUser.getId().toString();
         MetronomeMessageMessage displayMessage = new MetronomeMessageMessage(customMessage);
         imHelper.publishMessage(userId, roomId, displayMessage, 1);
         //记录节拍器消息
-        courseScheduleStudentPaymentDao.adjustPlayMidi(Long.parseLong(roomId.substring(1)),playMidiMessageData.getUserId(),content);
+        courseScheduleStudentPaymentDao.adjustPlayMidi(Long.parseLong(roomId.substring(1)), playMidiMessageData.getUserId(), content);
     }
 
     @Override
@@ -1556,13 +1559,13 @@ public class RoomServiceImpl implements RoomService {
         Long courseScheduleId = Long.parseLong(roomId.substring(1));
         List<CourseScheduleStudentMusicScore> scheduleStudentMusicScores =
                 courseScheduleStudentMusicScoreDao.queryByScoreIdAndCourseId(musicScoreData.getMusicScoreAccompanimentId(),
-                        courseScheduleId,null,null,0);
+                        courseScheduleId, null, null, 0);
         SysMusicScoreAccompaniment accompaniment = sysMusicScoreAccompanimentDao.get(musicScoreData.getMusicScoreAccompanimentId());
-        if(scheduleStudentMusicScores.size() == 0){
+        if (scheduleStudentMusicScores.size() == 0) {
             //第一次下载,生成数据
             List<CourseScheduleStudentPayment> courseScheduleStudentPayments = courseScheduleStudentPaymentDao.findByCourseSchedule(courseScheduleId);
             Set<Integer> studentIds = courseScheduleStudentPayments.stream().map(e -> e.getUserId()).collect(Collectors.toSet());
-            studentIds.forEach(e->{
+            studentIds.forEach(e -> {
                 CourseScheduleStudentMusicScore musicScore = new CourseScheduleStudentMusicScore();
                 musicScore.setMusicScoreAccompanimentId(accompaniment.getId());
                 musicScore.setSpeed(accompaniment.getSpeed());
@@ -1590,7 +1593,7 @@ public class RoomServiceImpl implements RoomService {
         SysUser authUser = sysUserFeignService.queryUserInfo();
 
         SysExamSong sysExamSong = sysExamSongDao.get(examSongId);
-        if(sysExamSong == null){
+        if (sysExamSong == null) {
             throw new BizException("曲目信息不存在");
         }
         //学员曲目下载状态改为未下载
@@ -1599,7 +1602,7 @@ public class RoomServiceImpl implements RoomService {
         json.setUrl(sysExamSong.getUrl());
         json.setStatus(0);
         json.setExamSongId(examSongId);
-        courseScheduleStudentPaymentDao.adjustExamSong(Long.parseLong(roomId.substring(1)),null, JSON.toJSONString(json));
+        courseScheduleStudentPaymentDao.adjustExamSong(Long.parseLong(roomId.substring(1)), null, JSON.toJSONString(json));
 
         ExamSongMessage examSongMessage = new ExamSongMessage();
         examSongMessage.setExamSongName(sysExamSong.getName());
@@ -1611,7 +1614,7 @@ public class RoomServiceImpl implements RoomService {
 
     @Override
     public List<RongyunBasicUserDto> queryNoJoinStu(String roomId) {
-        return courseScheduleStudentPaymentDao.queryNoJoinStu(roomId,roomId.substring(1));
+        return courseScheduleStudentPaymentDao.queryNoJoinStu(roomId, roomId.substring(1));
     }
 
     @Override
@@ -1622,14 +1625,14 @@ public class RoomServiceImpl implements RoomService {
         String roomId = musicScoreData.getRoomId();
         Long scheduleId = Long.parseLong(roomId.substring(1));
         Integer accompanimentId = musicScoreData.getMusicScoreAccompanimentId();
-        List<CourseScheduleStudentMusicScore> studentMusicScores = courseScheduleStudentMusicScoreDao.queryByScoreIdAndCourseId(accompanimentId,scheduleId,studentId,null,null);
-        if(accompanimentId != null){
+        List<CourseScheduleStudentMusicScore> studentMusicScores = courseScheduleStudentMusicScoreDao.queryByScoreIdAndCourseId(accompanimentId, scheduleId, studentId, null, null);
+        if (accompanimentId != null) {
             SysMusicScoreAccompaniment accompaniment = sysMusicScoreAccompanimentDao.get(accompanimentId);
-            if(accompaniment == null){
+            if (accompaniment == null) {
                 throw new BizException("曲目信息不存在");
             }
             //修改下载状态
-            if(studentMusicScores == null || studentMusicScores.size() == 0){
+            if (studentMusicScores == null || studentMusicScores.size() == 0) {
                 throw new BizException("学员不存在此下载曲目");
             }
             CourseScheduleStudentMusicScore studentMusicScore = studentMusicScores.get(0);
@@ -1638,41 +1641,41 @@ public class RoomServiceImpl implements RoomService {
         }
         //给老师发送学员曲目下载状态
         CourseSchedule courseSchedule = courseScheduleDao.get(scheduleId);
-        MusicScoreDownloadStatusMessage statusMessage = new MusicScoreDownloadStatusMessage(studentId,studentMusicScores);
-        imHelper.publishMessage(studentId.toString(),courseSchedule.getActualTeacherId().toString(), roomId,statusMessage);
+        MusicScoreDownloadStatusMessage statusMessage = new MusicScoreDownloadStatusMessage(studentId, studentMusicScores);
+        imHelper.publishMessage(studentId.toString(), courseSchedule.getActualTeacherId().toString(), roomId, statusMessage);
     }
 
     @Override
     @Transactional(rollbackFor = Exception.class)
-    public void adjustExamSong(String roomId, Integer status,Integer examSongId) throws Exception {
-        if(roomId == null || status == null || examSongId == null){
+    public void adjustExamSong(String roomId, Integer status, Integer examSongId) throws Exception {
+        if (roomId == null || status == null || examSongId == null) {
             throw new BizException("参数校验失败");
         }
         SysUser authUser = sysUserFeignService.queryUserInfo();
         long scheduleId = Long.parseLong(roomId.substring(1));
 
         SysExamSong sysExamSong = sysExamSongDao.get(examSongId);
-        if(sysExamSong == null){
+        if (sysExamSong == null) {
             throw new BizException("曲目信息不存在");
         }
         ExamSongDownloadData msg;
         String examJson = courseScheduleStudentPaymentDao.getExamJsonByCourseIdAndUserId(scheduleId, authUser.getId());
-        if(StringUtils.isEmpty(examJson)){
+        if (StringUtils.isEmpty(examJson)) {
             msg = new ExamSongDownloadData();
             msg.setExamSongName(sysExamSong.getName());
             msg.setUrl(sysExamSong.getUrl());
             msg.setStatus(status);
             msg.setExamSongId(examSongId);
-        }else {
+        } else {
             msg = JSON.parseObject(examJson, ExamSongDownloadData.class);
             msg.setStatus(status);
         }
-        courseScheduleStudentPaymentDao.adjustExamSong(scheduleId,authUser.getId(),JSON.toJSONString(msg));
+        courseScheduleStudentPaymentDao.adjustExamSong(scheduleId, authUser.getId(), JSON.toJSONString(msg));
 
         //给老师发送学员曲目下载状态
         CourseSchedule courseSchedule = courseScheduleDao.get(scheduleId);
-        ExamSongDownloadStatusMessage deviceResourceMessage = new ExamSongDownloadStatusMessage(status,authUser.getId(),examSongId);
-        imHelper.publishMessage(authUser.getId().toString(),courseSchedule.getActualTeacherId().toString(), roomId, deviceResourceMessage);
+        ExamSongDownloadStatusMessage deviceResourceMessage = new ExamSongDownloadStatusMessage(status, authUser.getId(), examSongId);
+        imHelper.publishMessage(authUser.getId().toString(), courseSchedule.getActualTeacherId().toString(), roomId, deviceResourceMessage);
     }
 
     public void updateDisplay(String roomId, String senderId, String display, Integer isIncludeSender) throws Exception {

+ 46 - 0
mec-im/src/main/java/com/ym/service/LiveRoomService.java

@@ -0,0 +1,46 @@
+package com.ym.service;
+
+import com.ym.mec.common.entity.ImRoomMessage;
+import com.ym.pojo.IMApiResultInfo;
+import com.ym.pojo.RecordNotify;
+
+/**
+ * @author hgw
+ * Created by 2022-02-21
+ */
+public interface LiveRoomService {
+
+    IMApiResultInfo createLiveRoom(String roomId, String roomName) throws Exception;
+
+    IMApiResultInfo destroyLiveRoom(String roomId) throws Exception;
+
+    IMApiResultInfo publishRoomMessage(ImRoomMessage message);
+
+    /**
+    * @description: 开始录制直播
+     * @param roomId
+    * @return void
+    * @author zx
+    * @date 2022/2/25 10:00
+    */
+    void startRecord(String roomId) throws Exception;
+
+    /**
+    * @description: 结束录制直播
+     * @param roomId
+    * @return void
+    * @author zx
+    * @date 2022/2/25 10:00
+    */
+    void stopRecord(String roomId) throws Exception;
+
+    /**
+    * @description: 录制状态回调
+     * @param recordNotify
+    * @return void
+    * @author zx
+    * @date 2022/2/25 11:49
+    */
+    void recordSync(RecordNotify recordNotify);
+
+}

+ 6 - 0
mec-student/src/main/java/com/ym/mec/student/controller/StudentManageController.java

@@ -159,6 +159,12 @@ public class StudentManageController extends BaseController {
 			List<Subject> subjectList = subjectService.findBySubjectByIdList(student.getSubjectIdList());
 			student.setSubjectNames(subjectList.stream().map(t -> t.getName()).collect(Collectors.joining(",")));
 		}
+		
+		if(StringUtils.isNotBlank(student.getExtSubjectIds())){
+			List<Subject> subjectList = subjectService.findBySubjectByIdList(student.getExtSubjectIds());
+			
+			student.setExtSjectNamesMap(subjectList.stream().collect(Collectors.toMap(Subject :: getId, Subject :: getName)));
+		}
 
 		Map<String, Object> datas = new HashMap<String, Object>();
 		datas.put("student", student);

+ 24 - 0
mec-task/src/main/java/com/ym/mec/task/jobs/CreateLiveRoomTask.java

@@ -0,0 +1,24 @@
+package com.ym.mec.task.jobs;
+
+import com.ym.mec.task.TaskRemoteService;
+import com.ym.mec.task.core.BaseTask;
+import com.ym.mec.task.core.TaskException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author hgw
+ * Created by 2022年3月1日
+ */
+@Service
+public class CreateLiveRoomTask extends BaseTask {
+
+    @Autowired
+    private TaskRemoteService taskRemoteService;
+
+    @Override
+    public void execute() throws TaskException {
+        taskRemoteService.createLiveRoom();
+    }
+
+}

+ 24 - 0
mec-task/src/main/java/com/ym/mec/task/jobs/DestroyExpiredLiveRoomTask.java

@@ -0,0 +1,24 @@
+package com.ym.mec.task.jobs;
+
+import com.ym.mec.task.TaskRemoteService;
+import com.ym.mec.task.core.BaseTask;
+import com.ym.mec.task.core.TaskException;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * @author hgw
+ * Created by 2022-03-04
+ */
+@Service
+public class DestroyExpiredLiveRoomTask extends BaseTask {
+
+    @Autowired
+    private TaskRemoteService taskRemoteService;
+
+    @Override
+    public void execute() throws TaskException {
+        taskRemoteService.destroyExpiredLiveRoom();
+    }
+
+}

+ 2 - 0
mec-util/src/main/java/com/ym/mec/util/date/DateUtil.java

@@ -21,6 +21,8 @@ public class DateUtil {
 
 	public static final String CHINESE_DATA_FORMAT = "yyyy年MM月dd日 HH:mm:ss";
 
+	public static final String CHINESE_DATA_FORMAT_1 = "yyyy/MM/dd HH:mm";
+
 	public static final String YEAR_MONTH_DAY_HOUR = "yyyyMMddHH";
 
 	public static final String DEFAULT_PATTERN = "yyyy-MM-dd";

+ 1 - 1
mec-web/src/main/java/com/ym/mec/web/config/ResourceServerConfig.java

@@ -39,7 +39,7 @@ public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
                         "/oaContracts/*", "/eduStudent/organStudentOverView", "/activity/countCloudTeacherActive",
                         "/activity/organDoubleEleven2021Statis", "/activity/doubleEleven2021Statis", "/questionnaireTopic/getDetail", "/questionnaireUserResult/add",
                         "/tenantInfo/info/*", "/tenantInfo/checkInfo/*", "/tenantInfo/pay/*","/tenantInfo/testCheck", "/tenantInfo/getContract/*",
-                        "/tenantPreJoin/add"
+                        "/tenantPreJoin/add","/imLiveBroadcastRoom/queryRoomInfo","/imLiveBroadcastRoom/test"
                 )
                 .permitAll().anyRequest().authenticated().and().httpBasic();
     }

+ 185 - 0
mec-web/src/main/java/com/ym/mec/web/controller/ImLiveBroadcastRoomController.java

@@ -0,0 +1,185 @@
+package com.ym.mec.web.controller;
+
+
+import com.ym.mec.biz.dal.dto.ImLiveBroadcastRoomDto;
+import com.ym.mec.biz.dal.vo.ImLiveBroadcastRoomVo;
+import com.ym.mec.biz.dal.vo.RoomUserInfoVo;
+import com.ym.mec.biz.service.ImLiveBroadcastRoomService;
+import com.ym.mec.common.controller.BaseController;
+import com.ym.mec.common.entity.HttpResponseResult;
+import com.ym.mec.common.entity.ImUserState;
+import com.ym.mec.common.page.PageInfo;
+import com.ym.mec.common.page.WrapperUtil;
+import io.swagger.annotations.*;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import javax.validation.Valid;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 直播房间管理表(ImLiveBroadcastRoom)表控制层
+ *
+ * @author hgw
+ * @since 2022-02-17 20:52:04
+ */
+@Api(tags = "直播房间管理表")
+@RestController
+@RequestMapping("/imLiveBroadcastRoom")
+public class ImLiveBroadcastRoomController extends BaseController {
+    /**
+     * 服务对象
+     */
+    @Resource
+    private ImLiveBroadcastRoomService imLiveBroadcastRoomService;
+
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "search", dataType = "String", value = "模糊查询关键字"),
+            @ApiImplicitParam(name = "liveState", dataType = "Integer", value = "直播状态 0未开始 1开始 2结束"),
+            @ApiImplicitParam(name = "startTime", dataType = "String", value = "开始时间"),
+            @ApiImplicitParam(name = "endTime", dataType = "String", value = "结束时间"),
+            @ApiImplicitParam(name = "page", dataType = "Integer", value = "页数"),
+            @ApiImplicitParam(name = "rows", dataType = "Integer", value = "每页数量"),
+    })
+    @ApiOperation("分页查询直播间列表")
+    @PostMapping("/queryPage")
+    public HttpResponseResult<PageInfo<ImLiveBroadcastRoomVo>> queryPage(@RequestBody Map<String, Object> param) {
+        return succeed(imLiveBroadcastRoomService.queryPage(param));
+    }
+
+    @ApiOperation("查询房间信息并校验房间是否合规")
+    @GetMapping("/queryRoom")
+    public HttpResponseResult<ImLiveBroadcastRoomVo> queryRoomAndCheck(@ApiParam(value = "房间uid", required = true) String roomUid,
+                                                                       @ApiParam(value = "用户id", required = true) Integer userId) {
+        return succeed(imLiveBroadcastRoomService.queryRoomAndCheck(roomUid, userId));
+    }
+
+    @ApiOperation("查询房间信息")
+    @GetMapping("/queryRoomInfo")
+    public HttpResponseResult<ImLiveBroadcastRoomVo> queryRoomInfo(@ApiParam(value = "房间uid", required = true) String roomUid) {
+        return succeed(imLiveBroadcastRoomService.queryRoomInfo(roomUid));
+    }
+
+    @ApiOperation("创建直播间")
+    @PostMapping("/add")
+    @PreAuthorize("@pcs.hasPermissions('imLiveBroadcastRoom/add')")
+    public HttpResponseResult add(@Valid @RequestBody ImLiveBroadcastRoomDto dto) {
+        imLiveBroadcastRoomService.add(dto);
+        return succeed();
+    }
+
+    @ApiOperation("修改直播间信息-已开播无法修改")
+    @PostMapping("/update")
+    public HttpResponseResult update(@Valid @RequestBody ImLiveBroadcastRoomDto dto) {
+        imLiveBroadcastRoomService.update(dto);
+        return succeed();
+    }
+
+    /**
+     * 修改是否禁言
+     *
+     * @param id          房间表id
+     * @param whetherChat 是否允许聊天互动 0允许 1不允许
+     */
+    @ApiOperation("修改是否禁言")
+    @GetMapping(value = "/whetherChat/{id}")
+    public HttpResponseResult whetherChat(@ApiParam(value = "房间表id", required = true) @PathVariable("id") Integer id,
+                                          @ApiParam(value = "是否允许聊天互动 0允许 1不允许", required = true) Integer whetherChat) {
+        imLiveBroadcastRoomService.whetherChat(id, whetherChat);
+        return succeed();
+    }
+
+    /**
+     * 关闭直播间
+     *
+     * @param id 房间表id
+     */
+    @ApiOperation("关闭直播间")
+    @GetMapping(value = "/roomDestroy/{id}")
+    public HttpResponseResult roomDestroy(@ApiParam(value = "房间表id", required = true) @PathVariable("id") Integer id) {
+        imLiveBroadcastRoomService.roomDestroy(id);
+        return succeed();
+    }
+
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "id", dataType = "Integer", value = "房间id"),
+    })
+    @ApiOperation("删除直播间信息-已开播无法删除")
+    @PostMapping("/delete")
+    @PreAuthorize("@pcs.hasPermissions('imLiveBroadcastRoom/delete')")
+    public HttpResponseResult delete(@RequestBody Map<String, Object> param) {
+        Integer id = WrapperUtil.toInt(param, "id", "请传入房间id");
+        imLiveBroadcastRoomService.delete(id);
+        return succeed();
+    }
+
+    @ApiOperation("同步点赞数量")
+    @GetMapping("/syncLike")
+    public HttpResponseResult syncLike(@ApiParam(value = "房间uid", required = true) String roomUid,
+                                       @ApiParam(value = "点赞数", required = true) Integer likeNum) {
+        imLiveBroadcastRoomService.syncLike(roomUid, likeNum);
+        return succeed();
+    }
+
+    @PostMapping("/quitRoom")
+    public HttpResponseResult quitRoom(@RequestBody List<ImUserState> userState) {
+        imLiveBroadcastRoomService.opsRoom(userState);
+        return succeed();
+    }
+
+    @ApiOperation("主讲人进入房间")
+    @GetMapping("/speakerJoinRoom")
+    public HttpResponseResult<ImLiveBroadcastRoomVo> speakerJoinRoom(String roomUid) {
+        return succeed(imLiveBroadcastRoomService.speakerJoinRoom(roomUid));
+    }
+
+    @ApiOperation("进入房间")
+    @GetMapping("/joinRoom")
+    public HttpResponseResult joinRoom(String roomUid, Integer userId) {
+        imLiveBroadcastRoomService.joinRoom(roomUid, userId);
+        return succeed();
+    }
+
+    @ApiOperation("开启/关闭直播的录像")
+    @GetMapping("/opsLiveVideo")
+    public HttpResponseResult opsLiveVideo(@ApiParam(value = "房间uid", required = true) String roomUid,
+                                           @ApiParam(value = "用户id", required = true) Integer userId,
+                                           @ApiParam(value = "type 1:开始直播-开始录像     2:关闭直播关闭录像", required = true) Integer type) {
+        if (type == 1) {
+            imLiveBroadcastRoomService.startLive(roomUid, userId);
+        } else if (type == 2) {
+            imLiveBroadcastRoomService.closeLive(roomUid, userId);
+        } else {
+            failed("type参数错误");
+        }
+        return succeed();
+    }
+
+    @GetMapping("/test")
+    public Object test(String roomUid, Integer userId) {
+        return imLiveBroadcastRoomService.test(roomUid, userId);
+    }
+
+    @GetMapping("/destroyExpiredLiveRoom")
+    public Object destroyExpiredLiveRoom() {
+        imLiveBroadcastRoomService.destroyExpiredLiveRoom();
+        return succeed();
+    }
+
+    @GetMapping("/shareGroup")
+    public HttpResponseResult shareGroup(@ApiParam(value = "房间uid", required = true) String roomUid,
+                                         @ApiParam(value = "群编号", required = true) String groupIds) {
+        imLiveBroadcastRoomService.shareGroup(roomUid, groupIds);
+        return succeed();
+    }
+
+    @ApiOperation("获取房间人员")
+    @GetMapping("/queryRoomUserInfo")
+    public HttpResponseResult<List<RoomUserInfoVo>> queryRoomUserInfo(@ApiParam(value = "房间uid", required = true) String roomUid) {
+        return succeed(imLiveBroadcastRoomService.queryRoomUserInfo(roomUid));
+    }
+
+}
+

+ 29 - 0
mec-web/src/main/java/com/ym/mec/web/controller/ImLiveBroadcastRoomDataController.java

@@ -0,0 +1,29 @@
+package com.ym.mec.web.controller;
+
+
+import com.ym.mec.biz.dal.entity.ImLiveBroadcastRoomData;
+import com.ym.mec.biz.service.ImLiveBroadcastRoomDataService;
+import org.springframework.web.bind.annotation.*;
+import com.ym.mec.common.controller.BaseController;
+import io.swagger.annotations.Api;
+
+import javax.annotation.Resource;
+
+/**
+ * 直播房间数据表(ImLiveBroadcastRoomData)表控制层
+ *
+ * @author hgw
+ * @since 2022-02-21 14:26:58
+ */
+@Api(tags = "直播房间数据表")
+@RestController
+@RequestMapping("/imLiveBroadcastRoomData")
+public class ImLiveBroadcastRoomDataController extends BaseController {
+    /**
+     * 服务对象
+     */
+    @Resource
+    private ImLiveBroadcastRoomDataService imLiveBroadcastRoomDataService;
+
+}
+

+ 59 - 0
mec-web/src/main/java/com/ym/mec/web/controller/ImLiveBroadcastRoomMemberController.java

@@ -0,0 +1,59 @@
+package com.ym.mec.web.controller;
+
+import com.ym.mec.biz.dal.vo.ImLiveBroadcastRoomDetailVo;
+import com.ym.mec.biz.dal.vo.ImLiveBroadcastRoomMemberVo;
+import com.ym.mec.biz.service.ImLiveBroadcastRoomMemberService;
+import com.ym.mec.common.controller.BaseController;
+import com.ym.mec.common.entity.HttpResponseResult;
+import com.ym.mec.common.page.PageInfo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+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.Map;
+
+/**
+ * 直播间人员关系表(ImLiveBroadcastRoomMember)表控制层
+ *
+ * @author hgw
+ * @since 2022-02-21 14:26:58
+ */
+@Api(tags = "直播间人员关系表")
+@RestController
+@RequestMapping("/imLiveBroadcastRoomMember")
+public class ImLiveBroadcastRoomMemberController extends BaseController {
+    /**
+     * 服务对象
+     */
+    @Resource
+    private ImLiveBroadcastRoomMemberService imLiveBroadcastRoomMemberService;
+
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "roomUid", dataType = "String", value = "房间uid")
+    })
+    @ApiOperation("直播间详情")
+    @PostMapping("/queryRoomDetail")
+    public HttpResponseResult<ImLiveBroadcastRoomDetailVo> queryRoomDetail(@RequestBody Map<String, Object> param) {
+        return succeed(imLiveBroadcastRoomMemberService.queryRoomDetail(param));
+    }
+
+    @ApiImplicitParams({
+            @ApiImplicitParam(name = "search", dataType = "String", value = "模糊搜索 学员编号姓名"),
+            @ApiImplicitParam(name = "roomUid", dataType = "String", value = "房间uid"),
+            @ApiImplicitParam(name = "page", dataType = "Integer", value = "页数"),
+            @ApiImplicitParam(name = "rows", dataType = "Integer", value = "每页数量"),
+    })
+    @ApiOperation("分页查询直播间人员列表")
+    @PostMapping("/queryRoomMember")
+    public HttpResponseResult<PageInfo<ImLiveBroadcastRoomMemberVo>> queryRoomMember(@RequestBody Map<String, Object> param) {
+        return succeed(imLiveBroadcastRoomMemberService.queryRoomMember(param));
+    }
+
+}
+

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio