UserController.java 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. package com.ym.controller;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.JSONObject;
  4. import com.google.common.collect.Lists;
  5. import com.microsvc.toolkit.middleware.im.ImPluginContext;
  6. import com.microsvc.toolkit.middleware.im.impl.TencentCloudImPlugin;
  7. import com.microsvc.toolkit.middleware.live.LivePluginContext;
  8. import com.microsvc.toolkit.middleware.live.LivePluginService;
  9. import com.microsvc.toolkit.middleware.live.impl.TencentCloudLivePlugin;
  10. import com.ym.common.BaseResponse;
  11. import com.ym.mec.auth.api.entity.SysUser;
  12. import com.ym.mec.biz.dal.dao.TeacherDao;
  13. import com.ym.mec.biz.dal.dto.TencentData;
  14. import com.ym.mec.biz.dal.dto.TencentImCallbackResult;
  15. import com.ym.mec.biz.dal.dto.im.BasicUserInfo;
  16. import com.ym.mec.biz.dal.enums.ETencentImCallbackCommand;
  17. import com.ym.mec.biz.dal.enums.im.ClientEnum;
  18. import com.ym.mec.biz.dal.wrapper.ImGroupWrapper;
  19. import com.ym.mec.biz.service.ImLiveBroadcastRoomService;
  20. import com.ym.mec.biz.service.SysUserService;
  21. import com.ym.mec.biz.service.im.ImGroupCoreService;
  22. import com.ym.mec.common.entity.ImUserModel;
  23. import com.ym.mec.common.entity.ImUserState;
  24. import com.ym.service.LiveRoomService;
  25. import com.ym.service.RoomService;
  26. import com.ym.service.UserService;
  27. import io.rong.models.Result;
  28. import io.rong.models.response.ResponseResult;
  29. import io.rong.models.response.TokenResult;
  30. import io.rong.models.user.UserModel;
  31. import io.swagger.annotations.ApiOperation;
  32. import org.apache.commons.collections.CollectionUtils;
  33. import org.apache.commons.lang3.StringUtils;
  34. import org.slf4j.Logger;
  35. import org.slf4j.LoggerFactory;
  36. import org.springframework.beans.factory.annotation.Autowired;
  37. import org.springframework.web.bind.annotation.PostMapping;
  38. import org.springframework.web.bind.annotation.RequestBody;
  39. import org.springframework.web.bind.annotation.RequestMapping;
  40. import org.springframework.web.bind.annotation.RequestMethod;
  41. import org.springframework.web.bind.annotation.RequestParam;
  42. import org.springframework.web.bind.annotation.RestController;
  43. import javax.servlet.http.HttpServletRequest;
  44. import java.util.Arrays;
  45. import java.util.Date;
  46. import java.util.List;
  47. import java.util.Objects;
  48. import java.util.Optional;
  49. import static com.ym.mec.common.controller.BaseController.succeed;
  50. @RestController
  51. @RequestMapping("/user")
  52. public class UserController {
  53. private static final Logger log = LoggerFactory.getLogger(UserController.class);
  54. @Autowired
  55. private UserService userService;
  56. @Autowired
  57. private ImLiveBroadcastRoomService imLiveBroadcastRoomService;
  58. @Autowired
  59. private LiveRoomService liveRoomService;
  60. @Autowired
  61. private LivePluginContext livePluginContext;
  62. @Autowired
  63. private RoomService roomService;
  64. @Autowired
  65. private ImGroupCoreService imGroupCoreService;
  66. @Autowired
  67. private ImPluginContext imPluginContext;
  68. @Autowired
  69. private TeacherDao teacherDao;
  70. @Autowired
  71. private SysUserService sysUserService;
  72. @RequestMapping(value = "/register", method = RequestMethod.POST)
  73. public Object register(@RequestParam(required = false, defaultValue = "") String clientType,
  74. @RequestBody UserModel userModel) throws Exception {
  75. TokenResult tokenResult = new TokenResult(0, "", "", "");
  76. // 默认使用腾讯云IM
  77. if (TencentCloudImPlugin.PLUGIN_NAME.equals(imPluginContext.defaultService())
  78. && StringUtils.isNotBlank(clientType)) {
  79. // 用户对象
  80. BasicUserInfo userInfo = teacherDao.getBasicUserInfo(Integer.parseInt(userModel.getId()));
  81. if (Objects.nonNull(userInfo)) {
  82. String username = userInfo.getUsername();
  83. List<String> userTypes = Lists.newArrayList(userInfo.getUserType().split(","));
  84. if (userTypes.size() > 1 || userTypes.contains(ClientEnum.TEACHER.getCode())) {
  85. username = Optional.ofNullable(userInfo.getRealName()).filter(StringUtils::isNotBlank).orElse(username);
  86. }
  87. ImGroupWrapper.ImUserInfo register = imGroupCoreService.register(userModel.getId(), clientType, username,
  88. sysUserService.getImAvatar(teacherDao.getUser(userInfo.getUserId().intValue())));
  89. // 用户注册接入Token
  90. tokenResult.setToken(register.getImToken());
  91. // 用户注册接入Id
  92. tokenResult.setUserId(register.getImUserId());
  93. SysUser sysUser = new SysUser();
  94. sysUser.setId(Integer.parseInt(userModel.getId()));
  95. // 更新用户imToken值
  96. sysUser.setImToken(register.getImToken());
  97. sysUser.setUpdateTime(new Date());
  98. sysUserService.updateSysUser(sysUser);
  99. }
  100. } else {
  101. // 融云
  102. tokenResult = userService.register(userModel);
  103. }
  104. return tokenResult;
  105. }
  106. @ApiOperation("IM用户Token信息")
  107. @RequestMapping(value = "/userImTokenInfo", method = RequestMethod.POST)
  108. public ImUserModel.ImUserResult userImTokenInfo(@RequestBody ImUserModel userModel) {
  109. ImUserModel.ImUserResult tokenResult = new ImUserModel.ImUserResult();
  110. // 用户对象
  111. BasicUserInfo userInfo = teacherDao.getBasicUserInfo(Integer.parseInt(userModel.getId()));
  112. if (Objects.nonNull(userInfo)) {
  113. String username = userInfo.getUsername();
  114. List<String> userTypes = Lists.newArrayList(userInfo.getUserType().split(","));
  115. if (userTypes.size() > 1 || userTypes.contains(ClientEnum.TEACHER.getCode())) {
  116. username = Optional.ofNullable(userInfo.getRealName()).filter(StringUtils::isNotBlank).orElse(username);
  117. }
  118. try {
  119. ImGroupWrapper.ImUserInfo register = imGroupCoreService.register(userModel.getId(), userModel.getClientType(),
  120. username, sysUserService.getImAvatar(teacherDao.getUser(userInfo.getUserId().intValue())));
  121. // 用户注册接入Token
  122. tokenResult.setImToken(register.getImToken());
  123. // 用户注册接入Id
  124. tokenResult.setImUserId(register.getImUserId());
  125. } catch (Exception e) {
  126. log.error("注册IM用户Token失败", e);
  127. }
  128. }
  129. return tokenResult;
  130. }
  131. @RequestMapping(value = "/update", method = RequestMethod.POST)
  132. public Object update(@RequestBody UserModel userModel) throws Exception {
  133. Result result = new ResponseResult(0, "");
  134. // 默认使用腾讯云IM
  135. if (TencentCloudImPlugin.PLUGIN_NAME.equals(imPluginContext.defaultService())) {
  136. // 腾讯云IM
  137. BasicUserInfo userInfo = teacherDao.getBasicUserInfo(Integer.parseInt(userModel.getId()));
  138. if (Objects.nonNull(userInfo)) {
  139. String username = userInfo.getUsername();
  140. List<String> userTypes = Lists.newArrayList(userInfo.getUserType().split(","));
  141. if (userTypes.size() > 1 || userTypes.contains(ClientEnum.TEACHER.getCode())) {
  142. username = Optional.ofNullable(userInfo.getRealName()).filter(StringUtils::isNotBlank).orElse(username);
  143. }
  144. try {
  145. ImGroupWrapper.ImUserInfo register = imGroupCoreService.register(userModel.getId(), "",
  146. username, sysUserService.getImAvatar(teacherDao.getUser(userInfo.getUserId().intValue())));
  147. log.info("update IM_USER register: {}", JSONObject.toJSONString(register));
  148. } catch (Exception e) {
  149. log.error("更新注册IM用户信息", e);
  150. }
  151. }
  152. } else {
  153. // 融云IM
  154. result = userService.update(userModel);
  155. log.info("update IM_USER result: {}", JSONObject.toJSONString(result));
  156. }
  157. return result;
  158. }
  159. /**
  160. * 监听融云用户状态变更
  161. *
  162. * @param userState List<ImUserState>
  163. */
  164. @PostMapping(value = "/statusImUser")
  165. public BaseResponse statusImUser(@RequestBody List<ImUserState> userState) {
  166. log.info("statusImUser >>>>> : {}", JSONObject.toJSONString(userState));
  167. imLiveBroadcastRoomService.opsRoom(userState);
  168. return new BaseResponse<>();
  169. }
  170. /**
  171. * 监听融云用户状态变更
  172. *
  173. * @param userState List<ImUserState>
  174. */
  175. @PostMapping(value = "/update/statusImUser")
  176. public Object updateStatusImUser(@RequestBody List<ImUserState> userState) {
  177. log.info("statusImUser >>>>> : {}", JSONObject.toJSONString(userState));
  178. imLiveBroadcastRoomService.opsRoom(userState);
  179. return succeed();
  180. }
  181. @PostMapping(value = "/syncLikeCount")
  182. public Object syncLikeCount(String roomUid) {
  183. log.info("statusImUser >>>>> : {}", JSONObject.toJSONString(roomUid));
  184. imLiveBroadcastRoomService.syncLikeCount(roomUid);
  185. return succeed();
  186. }
  187. @ApiOperation("腾讯im 回调接口")
  188. @PostMapping(value = "/tencentImCallback")
  189. public TencentImCallbackResult tencentImCallback(@RequestBody String body, HttpServletRequest request) {
  190. log.info("tencentImCallback body:{}", body);
  191. LivePluginService pluginService = livePluginContext.getPluginService(TencentCloudLivePlugin.PLUGIN_NAME);
  192. String appKey = pluginService.getLiveRoomConfig().getAppKey();
  193. log.info("tencentImCallback request param:{}", JSON.toJSONString(request.getParameterMap()));
  194. List<String> sdkList = Arrays.asList(request.getParameterValues("SdkAppid"));
  195. if (sdkList == null || sdkList.size() == 0) {
  196. log.error("tencentImCallback sdkAppid is null");
  197. return new TencentImCallbackResult();
  198. }
  199. if (!sdkList.contains(appKey)) {
  200. log.error("tencentImCallback sdkAppid is not match");
  201. return new TencentImCallbackResult();
  202. }
  203. String clientIP = request.getParameter("ClientIP");
  204. String optPlatform = request.getParameter("OptPlatform");
  205. if(request.getParameter("CallbackCommand").equals(ETencentImCallbackCommand.GROUP_CALLBACKONMEMBERSTATECHANGE.getCommand())) {
  206. // 直播群成员在线状态回调
  207. TencentData.CallbackOnMemberStateChange callbackOnMemberStateChange = TencentData.CallbackOnMemberStateChange.toObject(
  208. body);
  209. // 检查imUserId 是否是当前项目的
  210. for (TencentData.MemberListDTO memberListDTO : callbackOnMemberStateChange.getMemberList()) {
  211. if (!imGroupCoreService.checkImUserId(memberListDTO.getMemberAccount())) {
  212. log.info("tencentImCallback imUserId is not match, imUserId={}", memberListDTO.getMemberAccount());
  213. return new TencentImCallbackResult();
  214. }
  215. }
  216. log.debug("callbackOnMemberStateChange: {}", callbackOnMemberStateChange);
  217. callbackOnMemberStateChange.setClientIP(clientIP);
  218. callbackOnMemberStateChange.setOptPlatform(optPlatform);
  219. if (callbackOnMemberStateChange.getGroupId().startsWith("LIVE")) {
  220. // 直播间成员状态变更
  221. imLiveBroadcastRoomService.callbackOnMemberStateChange(callbackOnMemberStateChange);
  222. // 直播课学生签退
  223. String[] values = callbackOnMemberStateChange.getGroupId().split("-");
  224. String roomId = values.length > 2 ? values[1] : "";
  225. // 直播课学生签退、签退
  226. if ((roomId.startsWith("S") || roomId.startsWith("I"))
  227. && CollectionUtils.isNotEmpty(callbackOnMemberStateChange.getMemberList())) {
  228. // 学生编号
  229. String userId = callbackOnMemberStateChange.getMemberList().get(0).getMemberAccount();
  230. String imUserId = imGroupCoreService.analysisImUserId(userId);
  231. String eventType = callbackOnMemberStateChange.getEventType();
  232. // 学生离线消息通知
  233. if ("Offline".equals(eventType)) {
  234. try {
  235. roomService.leaveRoomSuccess(roomId, imUserId, null);
  236. } catch (Exception e) {
  237. log.error("tencentImCallback leaveRoomSuccess error, roomId={}, imUserId={}", roomId, imUserId, e);
  238. }
  239. }
  240. // 学生在线消息通知
  241. if ("Online".equals(eventType)) {
  242. try {
  243. roomService.joinRoomSuccess(roomId, imUserId, null);
  244. } catch (Exception e) {
  245. log.error("tencentImCallback joinRoomSuccess error, roomId={}, imUserId={}", roomId, imUserId, e);
  246. }
  247. }
  248. }
  249. }
  250. } else if(request.getParameter("CallbackCommand").equals(ETencentImCallbackCommand.GROUP_CALLBACKAFTERMEMBEREXIT.getCommand())) {
  251. // 群成员离开之后回调
  252. TencentData.CallbackAfterMemberExit callbackAfterMemberExit = TencentData.CallbackAfterMemberExit.toObject(
  253. body);
  254. // 检查imUserId 是否是当前项目的
  255. for (TencentData.MemberListDTO memberListDTO : callbackAfterMemberExit.getExitMemberList()) {
  256. if (!imGroupCoreService.checkImUserId(memberListDTO.getMemberAccount())) {
  257. log.info("tencentImCallback imUserId is not match, imUserId={}", memberListDTO.getMemberAccount());
  258. return new TencentImCallbackResult();
  259. }
  260. }
  261. log.debug("callbackAfterMemberExit: {}", callbackAfterMemberExit);
  262. callbackAfterMemberExit.setClientIP(clientIP);
  263. callbackAfterMemberExit.setOptPlatform(optPlatform);
  264. if (callbackAfterMemberExit.getGroupId().startsWith("LIVE")) {
  265. // 直播间成员状态变更
  266. imLiveBroadcastRoomService.callbackAfterMemberExit(callbackAfterMemberExit);
  267. // 直播课学生签退
  268. String[] values = callbackAfterMemberExit.getGroupId().split("-");
  269. String roomId = values.length > 2 ? values[1] : "";
  270. // 直播课学生签退、签退
  271. if ((roomId.startsWith("S") || roomId.startsWith("I"))
  272. && CollectionUtils.isNotEmpty(callbackAfterMemberExit.getExitMemberList())) {
  273. // 学生编号
  274. String userId = callbackAfterMemberExit.getExitMemberList().get(0).getMemberAccount();
  275. String imUserId = imGroupCoreService.analysisImUserId(userId);
  276. try {
  277. roomService.leaveRoomSuccess(roomId, imUserId, null);
  278. } catch (Exception e) {
  279. log.error("tencentImCallback leaveRoomSuccess error, roomId={}, imUserId={}", roomId, imUserId, e);
  280. }
  281. }
  282. }
  283. } else if(request.getParameter("CallbackCommand").equals(ETencentImCallbackCommand.GROUP_CALLBACKAFTERNEWMEMBERJOIN.getCommand())) {
  284. // 新成员入群之后回调
  285. TencentData.CallbackAfterNewMemberJoin callbackAfterNewMemberJoin = TencentData.CallbackAfterNewMemberJoin.toObject(
  286. body);
  287. // 检查imUserId 是否是当前项目的
  288. for (TencentData.MemberListDTO memberListDTO : callbackAfterNewMemberJoin.getNewMemberList()) {
  289. if (!imGroupCoreService.checkImUserId(memberListDTO.getMemberAccount())) {
  290. log.info("tencentImCallback imUserId is not match, imUserId={}", memberListDTO.getMemberAccount());
  291. return new TencentImCallbackResult();
  292. }
  293. }
  294. log.debug("CallbackAfterNewMemberJoin: {}", callbackAfterNewMemberJoin);
  295. callbackAfterNewMemberJoin.setClientIP(clientIP);
  296. callbackAfterNewMemberJoin.setOptPlatform(optPlatform);
  297. if (callbackAfterNewMemberJoin.getGroupId().startsWith("LIVE")) {
  298. // 直播间成员状态变更
  299. imLiveBroadcastRoomService.callbackAfterNewMemberJoin(callbackAfterNewMemberJoin);
  300. // 直播课学生签到
  301. String[] values = callbackAfterNewMemberJoin.getGroupId().split("-");
  302. String roomId = values.length > 2 ? values[1] : "";
  303. // 直播课学生签退、签退
  304. if ((roomId.startsWith("S") || roomId.startsWith("I"))
  305. && CollectionUtils.isNotEmpty(callbackAfterNewMemberJoin.getNewMemberList())) {
  306. // 学生编号
  307. String userId = callbackAfterNewMemberJoin.getNewMemberList().get(0).getMemberAccount();
  308. String imUserId = imGroupCoreService.analysisImUserId(userId);
  309. try {
  310. roomService.joinRoomSuccess(roomId, imUserId, null);
  311. } catch (Exception e) {
  312. log.error("tencentImCallback joinRoomSuccess error, roomId={}, imUserId={}", roomId, imUserId, e);
  313. }
  314. }
  315. }
  316. }
  317. return new TencentImCallbackResult();
  318. }
  319. @ApiOperation("腾讯云直播-推流 回调接口")
  320. @PostMapping(value = "/tencentStreamEventCallback")
  321. public TencentData.StreamEventCallbackResult tencentStreamEventCallback(@RequestBody String body) {
  322. log.info("tencentStreamEventCallback body:{}", body);
  323. TencentData.CallbackStreamStateEvent event = TencentData.CallbackStreamStateEvent.from(body);
  324. boolean b = checkStream(event.getStreamId());
  325. if (!b) {
  326. return TencentData.StreamEventCallbackResult.builder().code(0).build();
  327. }
  328. // 直播间推流事件
  329. if (event.getStreamId().startsWith("LIVE")) {
  330. ImUserState imUserState = new ImUserState();
  331. imUserState.setUserid(getSpeakerId(event.getStreamId()).toString());
  332. // 断流事件通知
  333. if (event.getEventType() == 0) {
  334. Integer speakerId = getSpeakerId(event.getStreamId());
  335. if (speakerId > 0) {
  336. // 更新推流时长
  337. if (StringUtils.isNotBlank(event.getPushDuration()) && event.getPushDuration().matches("\\d+")) {
  338. // 更新直播推流时长
  339. imLiveBroadcastRoomService.updateLiveRoomPushStreamTime(event);
  340. }
  341. imUserState.setStatus("3");
  342. // 自动关闭录制
  343. imLiveBroadcastRoomService.closeLive(getRoomUid(event.getStreamId()), speakerId, event.getSequence());
  344. // 同步点赞数
  345. imLiveBroadcastRoomService.syncLikeCount(getRoomUid(event.getStreamId()));
  346. }
  347. }
  348. // 推流事件通知
  349. if (event.getEventType() == 1) {
  350. Integer speakerId = getSpeakerId(event.getStreamId());
  351. if (speakerId > 0) {
  352. // 自动开启录制
  353. imLiveBroadcastRoomService.startLive(getRoomUid(event.getStreamId()), getSpeakerId(event.getStreamId()), null,event.getSequence());
  354. imUserState.setStatus("0");
  355. }
  356. }
  357. //imLiveBroadcastRoomService.opsRoom(Lists.newArrayList(imUserState));
  358. }
  359. return TencentData.StreamEventCallbackResult.builder().code(0).build();
  360. }
  361. private boolean checkStream(String streamId) {
  362. return imGroupCoreService.checkImUserId(streamId.split("_",2)[1]);
  363. }
  364. private Integer getSpeakerId(String streamId) {
  365. return Integer.parseInt(imGroupCoreService.analysisImUserId(streamId.split("_",2)[1]));
  366. }
  367. private String getRoomUid(String streamId) {
  368. return streamId.split("_")[0];
  369. }
  370. @ApiOperation("腾讯云直播-录制 回调接口")
  371. @PostMapping(value = "/tencentStreamRecordCallback")
  372. public TencentData.StreamEventCallbackResult tencentStreamRecordCallback(@RequestBody String body) {
  373. log.info("tencentStreamRecordCallback body:{}", body);
  374. TencentData.CallbackSteamRecordEvent event = TencentData.CallbackSteamRecordEvent.from(body);
  375. boolean b = checkStream(event.getStreamId());
  376. if (!b) {
  377. return TencentData.StreamEventCallbackResult.builder().code(0).build();
  378. }
  379. // 直播录制事件通知
  380. if (event.getStreamId().startsWith("LIVE")) {
  381. log.info("taskId={}, url={}", event.getTaskId(), event.getVideoUrl());
  382. // 生成直播录制信息
  383. liveRoomService.createLiveRoomVideoRecord(event);
  384. }
  385. return TencentData.StreamEventCallbackResult.builder().code(0).build();
  386. }
  387. @ApiOperation("腾讯云直播-推流异常 回调接口")
  388. @PostMapping(value = "/tencentStreamExceptionCallback")
  389. public TencentData.StreamEventCallbackResult tencentStreamExceptionCallback(@RequestBody String body) {
  390. log.info("tencentStreamExceptionCallback body:{}", body);
  391. return TencentData.StreamEventCallbackResult.builder().code(0).build();
  392. }
  393. }