assistant.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. // 房间行为助手
  2. import { EVENT, CODEMEG, FROMTYPE } from "../../enum/index.js";
  3. import { getCurrentUser, updateUser, removeRoomAllUsers, getAllRoomUsers, updateRoomUser, removeRoomUser } from "../../service/userService.js";
  4. import { setRoomConfig, getRoomConfig, isRoomMaster } from "../../service/roomConfigService.js";
  5. import { subClient } from "../../connection/redis.js";
  6. const prefix = process.env.REDIS_PREFIX || "chat";
  7. const getInKey = (realKey) => {
  8. return `${prefix}:${realKey}`;
  9. };
  10. export class RoomAssistant {
  11. constructor(socket, redis, room) {
  12. this.socket = socket;
  13. this.redis = redis;
  14. this.roomId = null;
  15. this.hasCall = false;
  16. this.room = room;
  17. this.roomMax = false;
  18. }
  19. /**
  20. * 准备房间
  21. * @param {*} roomSessionId
  22. * @param {*} roomId
  23. * @returns
  24. */
  25. async prepearRoom(roomSessionId, roomId) {
  26. // const uRoomId = await this.redis.get(getInKey(roomSessionId));
  27. // const mergeRoomId = uRoomId || roomId;
  28. // this.roomId = mergeRoomId;
  29. this.room.logger.info("prepearRoom", roomSessionId, roomId);
  30. await this.redis.set(getInKey(roomSessionId), roomId);
  31. return Promise.resolve(roomId);
  32. }
  33. // async prepearRoom(roomSessionId, roomId) {
  34. // const uRoomId = await this.redis.get(getInKey(roomSessionId));
  35. // const mergeRoomId = uRoomId || roomId;
  36. // this.roomId = mergeRoomId;
  37. // this.room.logger.info("prepearRoom", roomSessionId, this.roomId);
  38. // await this.redis.set(getInKey(roomSessionId), mergeRoomId);
  39. // return Promise.resolve(this.roomId);
  40. // }
  41. async destoryRoom(roomSessionId, roomConfigId) {
  42. this.room.logger.info("destoryRoom", roomSessionId, roomConfigId);
  43. await this.redis.del(getInKey(roomSessionId));
  44. await this.redis.del(getInKey(roomConfigId));
  45. this.disconnect();
  46. return Promise.resolve(true);
  47. }
  48. /**
  49. * kickPersion LEADER or assistant 房主或助手
  50. */
  51. async kickPersion(roomId, userId) {
  52. console.log("kickPersion", roomId, userId);
  53. getInKey(roomId);
  54. try {
  55. const hasJoin = await this.redis.HVALS(getInKey(roomId), userId);
  56. // const blackListId = ""
  57. if (hasJoin.length > 0) {
  58. await this.redis.hDel(getInKey(roomId), userId);
  59. return Promise.resolve(true);
  60. } else {
  61. return Promise.resolve(false);
  62. }
  63. } catch (error) {
  64. return Promise.resolve(false);
  65. }
  66. }
  67. /**
  68. * 设置助手 LEADER(权限) 房主或助手
  69. * @param {*} roomId
  70. * @param {*} userId
  71. */
  72. async setAssistant(roomId, userId, cancel) {
  73. try {
  74. const userRes = await getCurrentUser(roomId, userId, FROMTYPE.MiniAPP);
  75. const user = JSON.parse(userRes);
  76. const roomConfigRes = await getRoomConfig(roomId);
  77. if (this.room.userId == userId) {
  78. console.log("不能设置自己为助理!");
  79. return;
  80. }
  81. // const role = cancel ? "customer" : "assistant";
  82. const isAssistant = cancel ? 0 : 1;
  83. // assistant是助手,customer是普通角色,操作role会好些
  84. const userObj = Object.assign({}, user, { role: "customer", order: 1, isAssistant });
  85. const roomObj = Object.assign({}, roomConfigRes, { assistantId: cancel ? '' : user.userId });
  86. // console.log("setAssistant", userObj, roomObj);
  87. // console.error("roomObj", roomObj);
  88. await updateRoomUser(roomId, userId, userObj);
  89. // // 更新roomConfig 设置助手id
  90. await setRoomConfig(roomId, roomObj);
  91. const AllRoomUsers = await getAllRoomUsers(roomId);
  92. // 同房间的其他人重置
  93. const resetOther = Array.from(AllRoomUsers)
  94. .filter((i) => i.role !== "leader" && i.userId !== userObj.userId)
  95. .map((roomer) => {
  96. const userKey = `user:${roomer.userId}`;
  97. const unsetUserObj = Object.assign({}, roomer, { isAssistant: 0, role: "customer", order: 2 });
  98. // console.log("同房间的其他人重置", userKey, unsetUserObj);
  99. return updateRoomUser(roomId, userKey, unsetUserObj);
  100. });
  101. //总处理完成
  102. Promise.all(resetOther).then(() => {
  103. this.room.notify.notifyBeAssistant(roomId, userObj, this.room.userId);
  104. });
  105. // console.log("AllRoomUsers", AllRoomUsers);
  106. // callback(user);
  107. } catch (error) {
  108. this.room.logger.error("setAssistant:error", error);
  109. }
  110. }
  111. async getRoomAssistant(roomId) {
  112. const roomConfig = await getRoomConfig(roomId);
  113. const assistantId = roomConfig.assistantId || '';
  114. return Promise.resolve(assistantId);
  115. }
  116. /**
  117. * 设置MIC权 LEADER(权限) 房主或助手
  118. * 主要
  119. * @param {*} roomId
  120. * @param {*} userId
  121. */
  122. async setMicRight(roomId, userId, isAllowMic) {
  123. try {
  124. const userRes = await getCurrentUser(roomId, userId, FROMTYPE.MiniAPP);
  125. const user = JSON.parse(userRes);
  126. const roomConfigRes = await getRoomConfig(roomId);
  127. // if (this.room.userId == userId && this.room.isHoster(this.room.user.role)) {
  128. // console.log("房主不用设置自己的MIC!");
  129. // return;
  130. // }
  131. const reveseMic = Number(isAllowMic) === 0 ? 1 : 0;
  132. console.log("设置MIC权当前用户:: %s 新MIC权", user.userId, reveseMic);
  133. const userObj = Object.assign({}, user, { isAllowMic: reveseMic });
  134. const roomObj = Object.assign({}, roomConfigRes, { allowMicId: user.userId });
  135. await updateRoomUser(roomId, userId, userObj);
  136. await setRoomConfig(roomId, roomObj);
  137. const AllRoomUsers = await getAllRoomUsers(roomId);
  138. // 已存在的设置为false
  139. let preMicUser = null;
  140. const resetOther = Array.from(AllRoomUsers)
  141. .filter((i) => i.role !== "leader" && i.userId !== userObj.userId)
  142. .map((roomer) => {
  143. if (Number(roomer.isAllowMic) === 1) {
  144. preMicUser = roomer.userId;
  145. }
  146. const userKey = `user:${roomer.userId}`;
  147. const unsetUserObj = Object.assign({}, roomer, { isAllowMic: 0 });
  148. return updateRoomUser(roomId, userKey, unsetUserObj);
  149. });
  150. Promise.all(resetOther).then(() => {
  151. this.room.notify.notifyBeHasMic(roomId, userObj, this.room.userId, preMicUser);
  152. });
  153. } catch (error) {
  154. this.room.logger.error("setMicRight::error", error);
  155. }
  156. }
  157. /**
  158. * 创建房间 LEADER or assistant 房主或助手
  159. * @param {*string} roomId
  160. * @param {*string} userId
  161. * @param {*Object} user
  162. */
  163. async buildRoom(roomId, userId, user) {
  164. const hasJoin = await this.redis.HVALS(getInKey(roomId), userId);
  165. if (hasJoin.length === 0) {
  166. await this.redis.hSet(getInKey(roomId), userId, JSON.stringify(user));
  167. }
  168. }
  169. /**
  170. * 关闭房间
  171. * @param {*} roomId
  172. */
  173. async removeRoom(roomId) {
  174. this.room.logger.info("removeRoom", { roomId });
  175. await this.redis.del(getInKey(roomId));
  176. }
  177. /**
  178. * 加入房间
  179. * @param {*} roomId
  180. * @param {*} userId
  181. * @param {*} user
  182. */
  183. async joinRoom(roomId, userId, user) {
  184. const hasRoom = await this.redis.exists(getInKey(roomId));
  185. const isJoinRoom = await this.redis.hExists(getInKey(roomId), userId);
  186. if (hasRoom) {
  187. await this.redis.hSet(getInKey(roomId), userId, JSON.stringify(user));
  188. } else {
  189. await this.buildRoom(roomId, userId, user);
  190. this.room.logger.error("不存在房间", roomId);
  191. }
  192. this.socket.join(roomId);
  193. this.room.logger.info("加入房间 :", { userId, roomId, user });
  194. // if (!isJoinRoom) {
  195. // this.room.logger.info("加入房间 :", { userId, roomId, user });
  196. // const AllRoomUsers = await getAllRoomUsers(roomId);
  197. // const roomConfig = await getRoomConfig(roomId);
  198. // this.socket.emit(EVENT.roomIn, {
  199. // user,
  200. // roomsPerson: AllRoomUsers,
  201. // roomsConfig: roomConfig,
  202. // });
  203. // this.socket.broadcast.to(roomId).emit(EVENT.someOneInRoom, {
  204. // user,
  205. // roomsPerson: AllRoomUsers,
  206. // roomsConfig: roomConfig,
  207. // });
  208. // } else {
  209. // this.room.logger.info(`已加入房间 :`, { userId });
  210. // }
  211. }
  212. /**
  213. * 退出房间
  214. * @param {*} roomId
  215. * @param {*} userId
  216. * @param {*} user
  217. */
  218. async leaveRoom(roomId, userId, user) {
  219. try {
  220. await this.redis.hDel(getInKey(roomId), userId);
  221. await removeRoomUser(roomId, userId);
  222. const AllRoomUsers = await getAllRoomUsers(roomId);
  223. const roomConfig = await getRoomConfig(roomId);
  224. this.room.logger.info("退出房间", userId, AllRoomUsers);
  225. this.socket.broadcast.to(roomId).emit(EVENT.roomOut, {
  226. user,
  227. roomsPerson: AllRoomUsers,
  228. roomsConfig: roomConfig,
  229. });
  230. this.socket.broadcast.to(roomId).emit(EVENT.someOneLeaveRoom, {
  231. user,
  232. roomsPerson: AllRoomUsers,
  233. });
  234. await this.socket.leave(roomId);
  235. } catch (error) {
  236. console.log("leaveRoom::error", error);
  237. }
  238. }
  239. /**
  240. * 房主关闭房间
  241. * @param {*} clientRoom
  242. * @param {*} userUniqueId
  243. * @param {*} roomUniqueId
  244. */
  245. async closeRoom(roomId, userId, user) {
  246. try {
  247. this.room.logger.info("房主关闭房间", userId);
  248. console.log("isInRoom", this.socket.rooms.has(roomId));
  249. this.socket.broadcast.to(roomId).emit(EVENT.roomClose, { code: 3002, msg: CODEMEG[3002] });
  250. await removeRoomAllUsers(roomId);
  251. this.socket.leave(roomId);
  252. } catch (error) {
  253. this.room.logger.error("RoomAssistant::closeRoom", error);
  254. }
  255. }
  256. /**
  257. * 呼叫房间
  258. * @param {*} roomId
  259. * @param {*} userId
  260. * @param {*} user
  261. */
  262. async startCall(roomId, userId, user) {
  263. try {
  264. if (!this.roomMax) {
  265. if (user.oid) {
  266. console.log("hasDuplicateUser-存在oid", user.oid);
  267. const hasDuplicateUser = await this.getOpenidInRoom(roomId, user.oid);
  268. if (hasDuplicateUser && hasDuplicateUser.length > 0) {
  269. const removeAll = [];
  270. Array.from(hasDuplicateUser).forEach((duplicateUser) => {
  271. if (duplicateUser.userId !== user.userId) {
  272. console.log("duplicateUser-去重用户", duplicateUser);
  273. const deleteUserKey = `user:${duplicateUser.userId}`;
  274. console.log("deleteUserKey", deleteUserKey);
  275. removeAll.push(removeRoomUser(roomId, deleteUserKey));
  276. } else {
  277. user = Object.assign({}, user, {
  278. isAllowMic: Number(duplicateUser.isAllowMic),
  279. voiceStatus: Number(duplicateUser.voiceStatus),
  280. });
  281. console.log("在房间内延续部分数据", user);
  282. if (Number(duplicateUser.isAllowMic) === 1) {
  283. console.log("在房间内延续强制开MIC voiceStatus", Number(duplicateUser.voiceStatus));
  284. this.socket.broadcast.to(this.room.syncId).emit(EVENT.serverOnMic, {
  285. voiceStatus: Number(duplicateUser.voiceStatus),
  286. });
  287. }
  288. }
  289. });
  290. const res = await Promise.all(removeAll);
  291. console.log("去重完成", res);
  292. }
  293. }
  294. if (!this.room.isHoster(user.role)) {
  295. this.room.logger.info("不是房主", JSON.stringify(user));
  296. await this.joinRoom(roomId, userId, user);
  297. } else {
  298. const hasRoom = await this.redis.hVals(getInKey(roomId));
  299. if (hasRoom.length === 0) {
  300. this.room.logger.info("房主主动创建房间 :", { roomId, userId });
  301. await this.buildRoom(roomId, userId, user);
  302. } else {
  303. //TODO
  304. const checkIsRoomMaster = await isRoomMaster(roomId, userId);
  305. console.log("isRoomMaster", checkIsRoomMaster);
  306. if (checkIsRoomMaster) {
  307. this.room.logger.info("房主已存在房间 :", { roomId, userId, from: user.from });
  308. await this.joinRoom(roomId, userId, user);
  309. // this.notifyUserJitter(roomId);
  310. } else {
  311. this.room.logger.error("存在非法房主", userId);
  312. }
  313. }
  314. }
  315. user.isInRoom = true;
  316. this.hasCall = true;
  317. const AllRoomUsers = await getAllRoomUsers(roomId);
  318. const roomConfig = await getRoomConfig(roomId);
  319. await updateRoomUser(roomId, userId, user);
  320. this.room.logger.info("roomId", roomId);
  321. this.room.logger.info("AllRoomUsers", AllRoomUsers.length);
  322. this.socket.emit(EVENT.roomIn, {
  323. user,
  324. roomsPerson: AllRoomUsers,
  325. roomsConfig: roomConfig,
  326. });
  327. this.socket.emit(EVENT.someOneInRoom, {
  328. user,
  329. roomsPerson: AllRoomUsers,
  330. });
  331. this.socket.broadcast.to(roomId).emit(EVENT.someOneInRoom, {
  332. user,
  333. roomsPerson: AllRoomUsers,
  334. });
  335. } else {
  336. this.room.logger.warn("超出房间上限");
  337. this.socket.emit(EVENT.roomMaximum, user);
  338. this.socket.broadcast.to(this.room.syncId).emit(EVENT.roomMaximum, user);
  339. }
  340. // await this.notifyUsersChange(roomId, user, true);
  341. } catch (error) {
  342. this.room.logger.error("assistant::startCall:", error);
  343. }
  344. }
  345. async notifyUserJitter(roomId, userId) {
  346. const AllRoomUsers = await getAllRoomUsers(roomId);
  347. const roomConfig = await getRoomConfig(roomId);
  348. const currentUser = await getCurrentUser(userId, FROMTYPE.MiniAPP);
  349. const user = JSON.parse(currentUser);
  350. await updateRoomUser(roomId, userId, user);
  351. this.room.logger.info("notifyUserJitter", roomId, AllRoomUsers.length);
  352. this.socket.emit(EVENT.roomIn, {
  353. user,
  354. roomsPerson: AllRoomUsers,
  355. roomsConfig: roomConfig,
  356. });
  357. this.socket.broadcast.to(roomId).emit(EVENT.someOneInRoom, {
  358. user,
  359. roomsPerson: AllRoomUsers,
  360. });
  361. }
  362. /**
  363. * 通知房间人员变动
  364. */
  365. async notifyUsersChange(roomId, user, inter = true) {
  366. const AllRoomUsers = await getAllRoomUsers(roomId);
  367. // const roomConfig = await getRoomConfig(roomId);
  368. this.room.logger.info("notifyUsersChange", roomId, AllRoomUsers.length);
  369. const actionName = inter ? "inRoom" : "outRoom";
  370. this.socket.broadcast.to(roomId).emit(EVENT.roomPersonChange, {
  371. user: user,
  372. actionName: actionName,
  373. roomsPerson: AllRoomUsers,
  374. });
  375. }
  376. /**
  377. * 关闭呼叫房间
  378. * @param {*} roomId
  379. * @param {*} userId
  380. * @param {*} user
  381. */
  382. stopCall(roomId, userId, user) {
  383. if (!this.room.isHoster(user.role)) {
  384. this.leaveRoom(roomId, userId, user);
  385. } else {
  386. this.closeRoom(roomId, userId, user);
  387. }
  388. this.removeRoomSession(this.room);
  389. }
  390. async removeRoomSession(roomSessionId) {
  391. await this.redis.del(getInKey(roomSessionId));
  392. }
  393. async getOpenidInRoom(roomId, oid) {
  394. const AllRoomUsers = await getAllRoomUsers(roomId);
  395. if (AllRoomUsers.length > 0) {
  396. const users = AllRoomUsers.filter((item) => item.oid === oid);
  397. return Promise.resolve(users);
  398. } else {
  399. return Promise.resolve([]);
  400. }
  401. }
  402. // 下线用户强制上线
  403. async setOnlineStatus(roomId, userId, user) {
  404. user.onlineStatus = 1;
  405. await updateRoomUser(roomId, userId, user);
  406. // this.silentUpdateRoom(roomId);
  407. await this.notifyUsersChange(roomId, user, true);
  408. }
  409. // 静默认更新房间状态
  410. async silentUpdateRoom(roomId) {
  411. const AllRoomUsers = await getAllRoomUsers(roomId);
  412. this.socket.broadcast.to(roomId).emit(EVENT.silentUpdateRoom, {
  413. users: AllRoomUsers,
  414. });
  415. }
  416. async checkRoomMaximum(roomId) {
  417. const roomConfigRes = await getRoomConfig(roomId);
  418. const userLimitNum = Number(roomConfigRes.userLimitNum) || 50;
  419. console.log(`${roomId} 上限人数:`, userLimitNum);
  420. const users = await getAllRoomUsers(roomId);
  421. console.log(`${roomId} 当前人数:`, users.length);
  422. if (users && users.length >= Number(userLimitNum)) {
  423. return Promise.resolve({
  424. isMax: true,
  425. num: userLimitNum,
  426. });
  427. } else {
  428. return Promise.resolve({
  429. isMax: false,
  430. num: userLimitNum,
  431. });
  432. }
  433. }
  434. // 主动断开
  435. async disconnect() {
  436. try {
  437. const syncId = this.room.syncId;
  438. const roomId = this.room.roomId;
  439. const userId = this.room.userId;
  440. this.socket.leave(syncId);
  441. this.socket.leave(roomId);
  442. await removeRoomUser(roomId, userId);
  443. await this.redis.del(getInKey(syncId));
  444. await this.redis.del(getInKey(userId));
  445. this.notifyUsersChange(roomId, this.room.user, false);
  446. } catch (error) {
  447. console.log("disconnect::error", error);
  448. }
  449. }
  450. // RoomSessionId 房间有效时间
  451. setRoomUnlimit(roomSessionId) {
  452. return this.redis.expire(getInKey(roomSessionId), -1);
  453. }
  454. setRoomAvailableBySeconds(roomSessionId, seconds) {
  455. return this.redis.expire(getInKey(roomSessionId), seconds);
  456. }
  457. setRoomAvailableByHours(roomSessionId, hours) {
  458. return this.redis.expire(getInKey(roomSessionId), 60 * 60 * hours);
  459. }
  460. watchRoomExpired(callback) {
  461. subClient.subscribe("__keyevent@0__:expired", this.watchRoomExpiredFn);
  462. }
  463. async watchRoomExpiredFn(key) {
  464. console.log("key=> ", key);
  465. }
  466. unWatchRoomExpired() {
  467. subClient.unsubscribe("__keyevent@0__:expired", this.watchRoomExpiredFn);
  468. }
  469. }