index.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. // 云函数入口文件
  2. const cloud = require('wx-server-sdk')
  3. cloud.init({
  4. env: cloud.DYNAMIC_CURRENT_ENV
  5. })
  6. const db = cloud.database()
  7. const _ = db.command
  8. const roomCollection = db.collection('rooms')
  9. const userCollection = db.collection('users')
  10. // 生成6位随机房间号
  11. function generateRoomCode() {
  12. return Math.floor(100000 + Math.random() * 900000).toString()
  13. }
  14. // 云函数入口函数
  15. exports.main = async (event, context) => {
  16. const wxContext = cloud.getWXContext()
  17. const openid = wxContext.OPENID
  18. if (!openid) {
  19. return {
  20. code: 401,
  21. message: '未能获取到用户OpenID',
  22. }
  23. }
  24. const { action, gameType, roomCode, roomId, maxPlayers } = event
  25. try {
  26. // 创建房间
  27. if (action === 'create') {
  28. if (!gameType) {
  29. return {
  30. code: 400,
  31. message: '缺少必要参数:gameType',
  32. }
  33. }
  34. // 查询用户信息
  35. const userResult = await userCollection.where({
  36. userId: openid
  37. }).get()
  38. if (userResult.data.length === 0) {
  39. return {
  40. code: 404,
  41. message: '用户不存在',
  42. }
  43. }
  44. const user = userResult.data[0]
  45. // 生成房间号并确保唯一
  46. let newRoomCode = generateRoomCode()
  47. let isUnique = false
  48. let attempts = 0
  49. while (!isUnique && attempts < 10) {
  50. const existingRoom = await roomCollection.where({
  51. roomCode: newRoomCode,
  52. status: _.neq('ended')
  53. }).get()
  54. if (existingRoom.data.length === 0) {
  55. isUnique = true
  56. } else {
  57. newRoomCode = generateRoomCode()
  58. attempts++
  59. }
  60. }
  61. if (!isUnique) {
  62. return {
  63. code: 500,
  64. message: '无法生成唯一房间号,请稍后再试',
  65. }
  66. }
  67. // 创建新房间
  68. const now = db.serverDate()
  69. const newRoom = {
  70. roomCode: newRoomCode,
  71. gameType,
  72. status: 'waiting', // waiting, playing, ended
  73. createdAt: now,
  74. updatedAt: now,
  75. hostId: openid,
  76. hostInfo: {
  77. userId: user.userId,
  78. nickName: user.nickName,
  79. avatarUrl: user.avatarUrl
  80. },
  81. players: [{
  82. userId: user.userId,
  83. nickName: user.nickName,
  84. avatarUrl: user.avatarUrl,
  85. isHost: true,
  86. joinedAt: now,
  87. status: 'active' // active, left
  88. }],
  89. maxPlayers: maxPlayers || 8,
  90. gameData: {},
  91. gameResult: null
  92. }
  93. const roomResult = await roomCollection.add({
  94. data: newRoom
  95. })
  96. return {
  97. code: 200,
  98. message: '创建房间成功',
  99. data: {
  100. roomId: roomResult._id,
  101. roomCode: newRoomCode,
  102. gameType,
  103. status: 'waiting',
  104. hostId: openid,
  105. players: newRoom.players,
  106. createdAt: new Date(),
  107. maxPlayers: newRoom.maxPlayers
  108. }
  109. }
  110. }
  111. // 加入房间
  112. else if (action === 'join') {
  113. if (!roomCode) {
  114. return {
  115. code: 400,
  116. message: '缺少必要参数:roomCode',
  117. }
  118. }
  119. // 查询用户信息
  120. const userResult = await userCollection.where({
  121. userId: openid
  122. }).get()
  123. if (userResult.data.length === 0) {
  124. return {
  125. code: 404,
  126. message: '用户不存在',
  127. }
  128. }
  129. const user = userResult.data[0]
  130. // 查询房间信息
  131. const roomResult = await roomCollection.where({
  132. roomCode: roomCode,
  133. status: _.neq('ended')
  134. }).get()
  135. if (roomResult.data.length === 0) {
  136. return {
  137. code: 404,
  138. message: '房间不存在或已结束',
  139. }
  140. }
  141. const room = roomResult.data[0]
  142. // 检查房间是否已满
  143. const activePlayers = room.players.filter(p => p.status === 'active')
  144. if (activePlayers.length >= room.maxPlayers) {
  145. return {
  146. code: 403,
  147. message: '房间已满',
  148. }
  149. }
  150. // 检查用户是否已在房间中
  151. const playerIndex = room.players.findIndex(p => p.userId === openid)
  152. const now = db.serverDate()
  153. if (playerIndex >= 0) {
  154. // 用户已经在房间中,更新状态为active
  155. await roomCollection.doc(room._id).update({
  156. data: {
  157. [`players.${playerIndex}.status`]: 'active',
  158. [`players.${playerIndex}.joinedAt`]: now,
  159. updatedAt: now
  160. }
  161. })
  162. } else {
  163. // 用户不在房间中,添加到players数组
  164. await roomCollection.doc(room._id).update({
  165. data: {
  166. players: _.push({
  167. userId: user.userId,
  168. nickName: user.nickName,
  169. avatarUrl: user.avatarUrl,
  170. isHost: false,
  171. joinedAt: now,
  172. status: 'active'
  173. }),
  174. updatedAt: now
  175. }
  176. })
  177. }
  178. // 获取更新后的房间信息
  179. const updatedRoomResult = await roomCollection.doc(room._id).get()
  180. const updatedRoom = updatedRoomResult.data
  181. return {
  182. code: 200,
  183. message: '加入房间成功',
  184. data: {
  185. roomId: updatedRoom._id,
  186. roomCode: updatedRoom.roomCode,
  187. gameType: updatedRoom.gameType,
  188. status: updatedRoom.status,
  189. hostId: updatedRoom.hostId,
  190. players: updatedRoom.players,
  191. createdAt: updatedRoom.createdAt,
  192. maxPlayers: updatedRoom.maxPlayers,
  193. gameData: updatedRoom.gameData
  194. }
  195. }
  196. }
  197. // 获取房间信息
  198. else if (action === 'getRoomInfo') {
  199. if (!roomId && !roomCode) {
  200. return {
  201. code: 400,
  202. message: '缺少必要参数:roomId或roomCode',
  203. }
  204. }
  205. let roomResult
  206. if (roomId) {
  207. roomResult = await roomCollection.doc(roomId).get()
  208. } else {
  209. roomResult = await roomCollection.where({
  210. roomCode: roomCode,
  211. status: _.neq('ended')
  212. }).get()
  213. if (roomResult.data.length === 0) {
  214. return {
  215. code: 404,
  216. message: '房间不存在或已结束',
  217. }
  218. }
  219. roomResult = { data: roomResult.data[0] }
  220. }
  221. const room = roomResult.data
  222. return {
  223. code: 200,
  224. message: '获取房间信息成功',
  225. data: {
  226. roomId: room._id,
  227. roomCode: room.roomCode,
  228. gameType: room.gameType,
  229. status: room.status,
  230. hostId: room.hostId,
  231. players: room.players,
  232. createdAt: room.createdAt,
  233. maxPlayers: room.maxPlayers,
  234. gameData: room.gameData,
  235. gameResult: room.gameResult
  236. }
  237. }
  238. }
  239. // 开始游戏
  240. else if (action === 'startGame') {
  241. if (!roomId) {
  242. return {
  243. code: 400,
  244. message: '缺少必要参数:roomId',
  245. }
  246. }
  247. // 查询房间信息
  248. const roomResult = await roomCollection.doc(roomId).get()
  249. const room = roomResult.data
  250. // 检查是否是房主
  251. if (room.hostId !== openid) {
  252. return {
  253. code: 403,
  254. message: '只有房主可以开始游戏',
  255. }
  256. }
  257. // 检查玩家数量
  258. const activePlayers = room.players.filter(p => p.status === 'active')
  259. if (activePlayers.length < 2) {
  260. return {
  261. code: 400,
  262. message: '至少需要2名玩家才能开始游戏',
  263. }
  264. }
  265. // 更新房间状态
  266. await roomCollection.doc(roomId).update({
  267. data: {
  268. status: 'playing',
  269. updatedAt: db.serverDate(),
  270. startedAt: db.serverDate()
  271. }
  272. })
  273. return {
  274. code: 200,
  275. message: '游戏已开始',
  276. data: {
  277. roomId,
  278. status: 'playing'
  279. }
  280. }
  281. }
  282. // 更新游戏数据
  283. else if (action === 'updateGameData') {
  284. if (!roomId || !event.gameData) {
  285. return {
  286. code: 400,
  287. message: '缺少必要参数:roomId或gameData',
  288. }
  289. }
  290. // 检查房间是否存在
  291. const roomResult = await roomCollection.doc(roomId).get()
  292. const room = roomResult.data
  293. // 检查权限(只有房主或玩家可以更新)
  294. const playerExists = room.players.some(p => p.userId === openid && p.status === 'active')
  295. if (!playerExists) {
  296. return {
  297. code: 403,
  298. message: '您不是该房间的成员',
  299. }
  300. }
  301. // 更新游戏数据
  302. await roomCollection.doc(roomId).update({
  303. data: {
  304. gameData: event.gameData,
  305. updatedAt: db.serverDate()
  306. }
  307. })
  308. return {
  309. code: 200,
  310. message: '游戏数据已更新',
  311. data: {
  312. roomId,
  313. gameData: event.gameData
  314. }
  315. }
  316. }
  317. // 结束游戏
  318. else if (action === 'endGame') {
  319. if (!roomId) {
  320. return {
  321. code: 400,
  322. message: '缺少必要参数:roomId',
  323. }
  324. }
  325. // 查询房间信息
  326. const roomResult = await roomCollection.doc(roomId).get()
  327. const room = roomResult.data
  328. // 检查是否是房主
  329. if (room.hostId !== openid) {
  330. return {
  331. code: 403,
  332. message: '只有房主可以结束游戏',
  333. }
  334. }
  335. // 更新房间状态和游戏结果
  336. await roomCollection.doc(roomId).update({
  337. data: {
  338. status: 'ended',
  339. updatedAt: db.serverDate(),
  340. endedAt: db.serverDate(),
  341. gameResult: event.gameResult || {}
  342. }
  343. })
  344. // 更新所有玩家的游戏历史
  345. for (const player of room.players) {
  346. if (player.status === 'active') {
  347. await userCollection.where({
  348. userId: player.userId
  349. }).update({
  350. data: {
  351. gameHistory: _.push({
  352. gameType: room.gameType,
  353. roomId: room._id,
  354. roomCode: room.roomCode,
  355. playedAt: db.serverDate(),
  356. role: player.isHost ? 'host' : 'player',
  357. result: event.gameResult || {}
  358. })
  359. }
  360. })
  361. }
  362. }
  363. return {
  364. code: 200,
  365. message: '游戏已结束',
  366. data: {
  367. roomId,
  368. status: 'ended',
  369. gameResult: event.gameResult || {}
  370. }
  371. }
  372. }
  373. // 离开房间
  374. else if (action === 'leaveRoom') {
  375. if (!roomId) {
  376. return {
  377. code: 400,
  378. message: '缺少必要参数:roomId',
  379. }
  380. }
  381. // 查询房间信息
  382. const roomResult = await roomCollection.doc(roomId).get()
  383. const room = roomResult.data
  384. // 查找玩家在数组中的索引
  385. const playerIndex = room.players.findIndex(p => p.userId === openid)
  386. if (playerIndex === -1) {
  387. return {
  388. code: 404,
  389. message: '您不在该房间中',
  390. }
  391. }
  392. // 如果是房主离开
  393. if (room.hostId === openid) {
  394. // 如果还有其他玩家,选择一个新房主
  395. const activePlayers = room.players.filter(p => p.userId !== openid && p.status === 'active')
  396. if (activePlayers.length > 0) {
  397. const newHost = activePlayers[0]
  398. // 更新房间信息,标记该玩家为离开状态,并更换房主
  399. await roomCollection.doc(roomId).update({
  400. data: {
  401. [`players.${playerIndex}.status`]: 'left',
  402. hostId: newHost.userId,
  403. [`players.${room.players.findIndex(p => p.userId === newHost.userId)}.isHost`]: true,
  404. hostInfo: {
  405. userId: newHost.userId,
  406. nickName: newHost.nickName,
  407. avatarUrl: newHost.avatarUrl
  408. },
  409. updatedAt: db.serverDate()
  410. }
  411. })
  412. return {
  413. code: 200,
  414. message: '您已离开房间,房主已转交',
  415. data: {
  416. roomId,
  417. newHostId: newHost.userId
  418. }
  419. }
  420. } else {
  421. // 如果没有其他玩家,直接结束房间
  422. await roomCollection.doc(roomId).update({
  423. data: {
  424. status: 'ended',
  425. [`players.${playerIndex}.status`]: 'left',
  426. updatedAt: db.serverDate(),
  427. endedAt: db.serverDate()
  428. }
  429. })
  430. return {
  431. code: 200,
  432. message: '您已离开房间,房间已关闭',
  433. data: {
  434. roomId,
  435. status: 'ended'
  436. }
  437. }
  438. }
  439. } else {
  440. // 如果是普通玩家离开,只更新状态
  441. await roomCollection.doc(roomId).update({
  442. data: {
  443. [`players.${playerIndex}.status`]: 'left',
  444. updatedAt: db.serverDate()
  445. }
  446. })
  447. return {
  448. code: 200,
  449. message: '您已离开房间',
  450. data: {
  451. roomId
  452. }
  453. }
  454. }
  455. }
  456. else {
  457. return {
  458. code: 400,
  459. message: '无效的action参数',
  460. }
  461. }
  462. } catch (error) {
  463. console.error('房间操作失败:', error)
  464. return {
  465. code: 500,
  466. message: '操作失败: ' + error.message,
  467. }
  468. }
  469. }