UserController.java 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  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, userInfo.getAvatar());
  88. // 用户注册接入Token
  89. tokenResult.setToken(register.getImToken());
  90. // 用户注册接入Id
  91. tokenResult.setUserId(register.getImUserId());
  92. SysUser sysUser = new SysUser();
  93. sysUser.setId(Integer.parseInt(userModel.getId()));
  94. // 更新用户imToken值
  95. sysUser.setImToken(register.getImToken());
  96. sysUser.setUpdateTime(new Date());
  97. sysUserService.updateSysUser(sysUser);
  98. }
  99. } else {
  100. // 融云
  101. tokenResult = userService.register(userModel);
  102. }
  103. return tokenResult;
  104. }
  105. @ApiOperation("IM用户Token信息")
  106. @RequestMapping(value = "/userImTokenInfo", method = RequestMethod.POST)
  107. public ImUserModel.ImUserResult userImTokenInfo(@RequestBody ImUserModel userModel) {
  108. ImUserModel.ImUserResult tokenResult = new ImUserModel.ImUserResult();
  109. // 用户对象
  110. BasicUserInfo userInfo = teacherDao.getBasicUserInfo(Integer.parseInt(userModel.getId()));
  111. if (Objects.nonNull(userInfo)) {
  112. String username = userInfo.getUsername();
  113. List<String> userTypes = Lists.newArrayList(userInfo.getUserType().split(","));
  114. if (userTypes.size() > 1 || userTypes.contains(ClientEnum.TEACHER.getCode())) {
  115. username = Optional.ofNullable(userInfo.getRealName()).filter(StringUtils::isNotBlank).orElse(username);
  116. }
  117. try {
  118. ImGroupWrapper.ImUserInfo register = imGroupCoreService.register(userModel.getId(), userModel.getClientType(),
  119. username, Optional.ofNullable(userInfo.getAvatar()).orElse(""));
  120. // 用户注册接入Token
  121. tokenResult.setImToken(register.getImToken());
  122. // 用户注册接入Id
  123. tokenResult.setImUserId(register.getImUserId());
  124. } catch (Exception e) {
  125. log.error("注册IM用户Token失败", e);
  126. }
  127. }
  128. return tokenResult;
  129. }
  130. @RequestMapping(value = "/update", method = RequestMethod.POST)
  131. public Object update(@RequestBody UserModel userModel) throws Exception {
  132. Result result = new ResponseResult(0, "");
  133. // 默认使用腾讯云IM
  134. if (TencentCloudImPlugin.PLUGIN_NAME.equals(imPluginContext.defaultService())) {
  135. // 腾讯云IM
  136. BasicUserInfo userInfo = teacherDao.getBasicUserInfo(Integer.parseInt(userModel.getId()));
  137. if (Objects.nonNull(userInfo)) {
  138. String username = userInfo.getUsername();
  139. List<String> userTypes = Lists.newArrayList(userInfo.getUserType().split(","));
  140. if (userTypes.size() > 1 || userTypes.contains(ClientEnum.TEACHER.getCode())) {
  141. username = Optional.ofNullable(userInfo.getRealName()).filter(StringUtils::isNotBlank).orElse(username);
  142. }
  143. try {
  144. ImGroupWrapper.ImUserInfo register = imGroupCoreService.register(userModel.getId(), "",
  145. username, Optional.ofNullable(userModel.getPortrait()).orElse(userInfo.getAvatar()));
  146. log.info("update IM_USER register: {}", JSONObject.toJSONString(register));
  147. } catch (Exception e) {
  148. log.error("更新注册IM用户信息", e);
  149. }
  150. }
  151. } else {
  152. // 融云IM
  153. result = userService.update(userModel);
  154. log.info("update IM_USER result: {}", JSONObject.toJSONString(result));
  155. }
  156. return result;
  157. }
  158. /**
  159. * 监听融云用户状态变更
  160. *
  161. * @param userState List<ImUserState>
  162. */
  163. @PostMapping(value = "/statusImUser")
  164. public BaseResponse statusImUser(@RequestBody List<ImUserState> userState) {
  165. log.info("statusImUser >>>>> : {}", JSONObject.toJSONString(userState));
  166. imLiveBroadcastRoomService.opsRoom(userState);
  167. return new BaseResponse<>();
  168. }
  169. /**
  170. * 监听融云用户状态变更
  171. *
  172. * @param userState List<ImUserState>
  173. */
  174. @PostMapping(value = "/update/statusImUser")
  175. public Object updateStatusImUser(@RequestBody List<ImUserState> userState) {
  176. log.info("statusImUser >>>>> : {}", JSONObject.toJSONString(userState));
  177. imLiveBroadcastRoomService.opsRoom(userState);
  178. return succeed();
  179. }
  180. @PostMapping(value = "/syncLikeCount")
  181. public Object syncLikeCount(String roomUid) {
  182. log.info("statusImUser >>>>> : {}", JSONObject.toJSONString(roomUid));
  183. imLiveBroadcastRoomService.syncLikeCount(roomUid);
  184. return succeed();
  185. }
  186. @ApiOperation("腾讯im 回调接口")
  187. @PostMapping(value = "/tencentImCallback")
  188. public TencentImCallbackResult tencentImCallback(@RequestBody String body, HttpServletRequest request) {
  189. log.info("tencentImCallback body:{}", body);
  190. LivePluginService pluginService = livePluginContext.getPluginService(TencentCloudLivePlugin.PLUGIN_NAME);
  191. String appKey = pluginService.getLiveRoomConfig().getAppKey();
  192. log.info("tencentImCallback request param:{}", JSON.toJSONString(request.getParameterMap()));
  193. List<String> sdkList = Arrays.asList(request.getParameterValues("SdkAppid"));
  194. if (sdkList == null || sdkList.size() == 0) {
  195. log.error("tencentImCallback sdkAppid is null");
  196. return new TencentImCallbackResult();
  197. }
  198. if (!sdkList.contains(appKey)) {
  199. log.error("tencentImCallback sdkAppid is not match");
  200. return new TencentImCallbackResult();
  201. }
  202. String clientIP = request.getParameter("ClientIP");
  203. String optPlatform = request.getParameter("OptPlatform");
  204. if(request.getParameter("CallbackCommand").equals(ETencentImCallbackCommand.GROUP_CALLBACKONMEMBERSTATECHANGE.getCommand())) {
  205. // 直播群成员在线状态回调
  206. TencentData.CallbackOnMemberStateChange callbackOnMemberStateChange = TencentData.CallbackOnMemberStateChange.toObject(
  207. body);
  208. // 检查imUserId 是否是当前项目的
  209. for (TencentData.MemberListDTO memberListDTO : callbackOnMemberStateChange.getMemberList()) {
  210. if (!imGroupCoreService.checkImUserId(memberListDTO.getMemberAccount())) {
  211. log.info("tencentImCallback imUserId is not match, imUserId={}", memberListDTO.getMemberAccount());
  212. return new TencentImCallbackResult();
  213. }
  214. }
  215. log.debug("callbackOnMemberStateChange: {}", callbackOnMemberStateChange);
  216. callbackOnMemberStateChange.setClientIP(clientIP);
  217. callbackOnMemberStateChange.setOptPlatform(optPlatform);
  218. if (callbackOnMemberStateChange.getGroupId().startsWith("LIVE")) {
  219. // 直播间成员状态变更
  220. imLiveBroadcastRoomService.callbackOnMemberStateChange(callbackOnMemberStateChange);
  221. // 直播课学生签退
  222. String[] values = callbackOnMemberStateChange.getGroupId().split("-");
  223. String roomId = values.length > 2 ? values[1] : "";
  224. // 直播课学生签退、签退
  225. if ((roomId.startsWith("S") || roomId.startsWith("I"))
  226. && CollectionUtils.isNotEmpty(callbackOnMemberStateChange.getMemberList())) {
  227. // 学生编号
  228. String userId = callbackOnMemberStateChange.getMemberList().get(0).getMemberAccount();
  229. String imUserId = imGroupCoreService.analysisImUserId(userId);
  230. String eventType = callbackOnMemberStateChange.getEventType();
  231. // 学生离线消息通知
  232. if ("Offline".equals(eventType)) {
  233. try {
  234. roomService.leaveRoomSuccess(roomId, imUserId, null);
  235. } catch (Exception e) {
  236. log.error("tencentImCallback leaveRoomSuccess error, roomId={}, imUserId={}", roomId, imUserId, e);
  237. }
  238. }
  239. // 学生在线消息通知
  240. if ("Online".equals(eventType)) {
  241. try {
  242. roomService.joinRoomSuccess(roomId, imUserId, null);
  243. } catch (Exception e) {
  244. log.error("tencentImCallback joinRoomSuccess error, roomId={}, imUserId={}", roomId, imUserId, e);
  245. }
  246. }
  247. }
  248. }
  249. } else if(request.getParameter("CallbackCommand").equals(ETencentImCallbackCommand.GROUP_CALLBACKAFTERMEMBEREXIT.getCommand())) {
  250. // 群成员离开之后回调
  251. TencentData.CallbackAfterMemberExit callbackAfterMemberExit = TencentData.CallbackAfterMemberExit.toObject(
  252. body);
  253. // 检查imUserId 是否是当前项目的
  254. for (TencentData.MemberListDTO memberListDTO : callbackAfterMemberExit.getExitMemberList()) {
  255. if (!imGroupCoreService.checkImUserId(memberListDTO.getMemberAccount())) {
  256. log.info("tencentImCallback imUserId is not match, imUserId={}", memberListDTO.getMemberAccount());
  257. return new TencentImCallbackResult();
  258. }
  259. }
  260. log.debug("callbackAfterMemberExit: {}", callbackAfterMemberExit);
  261. callbackAfterMemberExit.setClientIP(clientIP);
  262. callbackAfterMemberExit.setOptPlatform(optPlatform);
  263. if (callbackAfterMemberExit.getGroupId().startsWith("LIVE")) {
  264. // 直播间成员状态变更
  265. imLiveBroadcastRoomService.callbackAfterMemberExit(callbackAfterMemberExit);
  266. // 直播课学生签退
  267. String[] values = callbackAfterMemberExit.getGroupId().split("-");
  268. String roomId = values.length > 2 ? values[1] : "";
  269. // 直播课学生签退、签退
  270. if ((roomId.startsWith("S") || roomId.startsWith("I"))
  271. && CollectionUtils.isNotEmpty(callbackAfterMemberExit.getExitMemberList())) {
  272. // 学生编号
  273. String userId = callbackAfterMemberExit.getExitMemberList().get(0).getMemberAccount();
  274. String imUserId = imGroupCoreService.analysisImUserId(userId);
  275. try {
  276. roomService.leaveRoomSuccess(roomId, imUserId, null);
  277. } catch (Exception e) {
  278. log.error("tencentImCallback leaveRoomSuccess error, roomId={}, imUserId={}", roomId, imUserId, e);
  279. }
  280. }
  281. }
  282. } else if(request.getParameter("CallbackCommand").equals(ETencentImCallbackCommand.GROUP_CALLBACKAFTERNEWMEMBERJOIN.getCommand())) {
  283. // 新成员入群之后回调
  284. TencentData.CallbackAfterNewMemberJoin callbackAfterNewMemberJoin = TencentData.CallbackAfterNewMemberJoin.toObject(
  285. body);
  286. // 检查imUserId 是否是当前项目的
  287. for (TencentData.MemberListDTO memberListDTO : callbackAfterNewMemberJoin.getNewMemberList()) {
  288. if (!imGroupCoreService.checkImUserId(memberListDTO.getMemberAccount())) {
  289. log.info("tencentImCallback imUserId is not match, imUserId={}", memberListDTO.getMemberAccount());
  290. return new TencentImCallbackResult();
  291. }
  292. }
  293. log.debug("CallbackAfterNewMemberJoin: {}", callbackAfterNewMemberJoin);
  294. callbackAfterNewMemberJoin.setClientIP(clientIP);
  295. callbackAfterNewMemberJoin.setOptPlatform(optPlatform);
  296. if (callbackAfterNewMemberJoin.getGroupId().startsWith("LIVE")) {
  297. // 直播间成员状态变更
  298. imLiveBroadcastRoomService.callbackAfterNewMemberJoin(callbackAfterNewMemberJoin);
  299. // 直播课学生签到
  300. String[] values = callbackAfterNewMemberJoin.getGroupId().split("-");
  301. String roomId = values.length > 2 ? values[1] : "";
  302. // 直播课学生签退、签退
  303. if ((roomId.startsWith("S") || roomId.startsWith("I"))
  304. && CollectionUtils.isNotEmpty(callbackAfterNewMemberJoin.getNewMemberList())) {
  305. // 学生编号
  306. String userId = callbackAfterNewMemberJoin.getNewMemberList().get(0).getMemberAccount();
  307. String imUserId = imGroupCoreService.analysisImUserId(userId);
  308. try {
  309. roomService.joinRoomSuccess(roomId, imUserId, null);
  310. } catch (Exception e) {
  311. log.error("tencentImCallback joinRoomSuccess error, roomId={}, imUserId={}", roomId, imUserId, e);
  312. }
  313. }
  314. }
  315. }
  316. return new TencentImCallbackResult();
  317. }
  318. @ApiOperation("腾讯云直播-推流 回调接口")
  319. @PostMapping(value = "/tencentStreamEventCallback")
  320. public TencentData.StreamEventCallbackResult tencentStreamEventCallback(@RequestBody String body) {
  321. log.info("tencentStreamEventCallback body:{}", body);
  322. TencentData.CallbackStreamStateEvent event = TencentData.CallbackStreamStateEvent.from(body);
  323. boolean b = checkStream(event.getStreamId());
  324. if (!b) {
  325. return TencentData.StreamEventCallbackResult.builder().code(0).build();
  326. }
  327. // 直播间推流事件
  328. if (event.getStreamId().startsWith("LIVE")) {
  329. ImUserState imUserState = new ImUserState();
  330. imUserState.setUserid(getSpeakerId(event.getStreamId()).toString());
  331. // 断流事件通知
  332. if (event.getEventType() == 0) {
  333. Integer speakerId = getSpeakerId(event.getStreamId());
  334. if (speakerId > 0) {
  335. // 更新推流时长
  336. if (StringUtils.isNotBlank(event.getPushDuration()) && event.getPushDuration().matches("\\d+")) {
  337. // 更新直播推流时长
  338. imLiveBroadcastRoomService.updateLiveRoomPushStreamTime(event);
  339. }
  340. imUserState.setStatus("3");
  341. // 自动关闭录制
  342. imLiveBroadcastRoomService.closeLive(getRoomUid(event.getStreamId()), speakerId, event.getSequence());
  343. // 同步点赞数
  344. imLiveBroadcastRoomService.syncLikeCount(getRoomUid(event.getStreamId()));
  345. }
  346. }
  347. // 推流事件通知
  348. if (event.getEventType() == 1) {
  349. Integer speakerId = getSpeakerId(event.getStreamId());
  350. if (speakerId > 0) {
  351. // 自动开启录制
  352. imLiveBroadcastRoomService.startLive(getRoomUid(event.getStreamId()), getSpeakerId(event.getStreamId()), null,event.getSequence());
  353. imUserState.setStatus("0");
  354. }
  355. }
  356. //imLiveBroadcastRoomService.opsRoom(Lists.newArrayList(imUserState));
  357. }
  358. return TencentData.StreamEventCallbackResult.builder().code(0).build();
  359. }
  360. private boolean checkStream(String streamId) {
  361. return imGroupCoreService.checkImUserId(streamId.split("_",2)[1]);
  362. }
  363. private Integer getSpeakerId(String streamId) {
  364. return Integer.parseInt(imGroupCoreService.analysisImUserId(streamId.split("_",2)[1]));
  365. }
  366. private String getRoomUid(String streamId) {
  367. return streamId.split("_")[0];
  368. }
  369. @ApiOperation("腾讯云直播-录制 回调接口")
  370. @PostMapping(value = "/tencentStreamRecordCallback")
  371. public TencentData.StreamEventCallbackResult tencentStreamRecordCallback(@RequestBody String body) {
  372. log.info("tencentStreamRecordCallback body:{}", body);
  373. TencentData.CallbackSteamRecordEvent event = TencentData.CallbackSteamRecordEvent.from(body);
  374. boolean b = checkStream(event.getStreamId());
  375. if (!b) {
  376. return TencentData.StreamEventCallbackResult.builder().code(0).build();
  377. }
  378. // 直播录制事件通知
  379. if (event.getStreamId().startsWith("LIVE")) {
  380. log.info("taskId={}, url={}", event.getTaskId(), event.getVideoUrl());
  381. // 生成直播录制信息
  382. liveRoomService.createLiveRoomVideoRecord(event);
  383. }
  384. return TencentData.StreamEventCallbackResult.builder().code(0).build();
  385. }
  386. @ApiOperation("腾讯云直播-推流异常 回调接口")
  387. @PostMapping(value = "/tencentStreamExceptionCallback")
  388. public TencentData.StreamEventCallbackResult tencentStreamExceptionCallback(@RequestBody String body) {
  389. log.info("tencentStreamExceptionCallback body:{}", body);
  390. return TencentData.StreamEventCallbackResult.builder().code(0).build();
  391. }
  392. }