const { Match, MatchGame, MatchPlayer, MatchRound, LadderUser, User, Store, sequelize } = require('../models'); const { MATCH_TYPES, MATCH_STATUS, CONFIRM_STATUS, RANKING_STAGE, MATCH_WEIGHTS, POWER_CALC } = require('../config/constants'); const { generateMatchCode, success, error, getPagination, pageResult } = require('../utils/helper'); const PowerCalculator = require('../services/powerCalculator'); const { sendChallengeNotification, sendScoreConfirmNotification, sendMatchNotification } = require('../websocket'); const { Op } = require('sequelize'); class MatchController { // 检查是否可以发起挑战 async checkChallenge(req, res) { try { const { targetMemberCode } = req.params; const { store_id } = req.query; const user = req.user; // 获取自己的天梯用户 const myLadderUser = await LadderUser.findOne({ where: { user_id: user.id, store_id, status: 1 } }); if (!myLadderUser) { return res.json(success({ canChallenge: false, reason: '您还不是该门店的天梯用户' })); } // 通过会员码获取对手 const targetUser = await User.findOne({ where: { member_code: targetMemberCode } }); if (!targetUser) { return res.json(success({ canChallenge: false, reason: '未找到该用户' })); } if (targetUser.id === user.id) { return res.json(success({ canChallenge: false, reason: '不能挑战自己' })); } // 获取对手的天梯用户 const targetLadderUser = await LadderUser.findOne({ where: { user_id: targetUser.id, store_id, status: 1 } }); if (!targetLadderUser) { return res.json(success({ canChallenge: false, reason: '对方不是该门店的天梯用户' })); } // 检查30天内是否已经比赛过 const recentMatch = await MatchGame.findOne({ where: { [Op.or]: [ { player1_id: myLadderUser.id, player2_id: targetLadderUser.id }, { player1_id: targetLadderUser.id, player2_id: myLadderUser.id } ], confirm_status: CONFIRM_STATUS.CONFIRMED, created_at: { [Op.gte]: new Date(Date.now() - POWER_CALC.CHALLENGE_COOLDOWN * 24 * 60 * 60 * 1000) } }, include: [{ model: Match, as: 'match', where: { type: MATCH_TYPES.CHALLENGE } }] }); if (recentMatch) { const days = PowerCalculator.getCooldownDays(recentMatch.created_at); return res.json(success({ canChallenge: false, reason: `30天内已与对方比赛过,还需等待${days}天` })); } res.json(success({ canChallenge: true, targetUser: { id: targetUser.id, nickname: targetUser.nickname, avatar: targetUser.avatar, ladderUser: { id: targetLadderUser.id, realName: targetLadderUser.real_name, level: targetLadderUser.level, powerScore: targetLadderUser.power_score } }, myLadderUser: { id: myLadderUser.id, realName: myLadderUser.real_name, level: myLadderUser.level, powerScore: myLadderUser.power_score } })); } catch (err) { console.error('检查挑战失败:', err); res.status(500).json(error('检查失败')); } } // 发起挑战 async createChallenge(req, res) { const t = await sequelize.transaction(); try { const { store_id, target_member_code } = req.body; const user = req.user; // 获取双方天梯用户 const myLadderUser = await LadderUser.findOne({ where: { user_id: user.id, store_id, status: 1 } }); const targetUser = await User.findOne({ where: { member_code: target_member_code } }); const targetLadderUser = await LadderUser.findOne({ where: { user_id: targetUser.id, store_id, status: 1 } }); if (!myLadderUser || !targetLadderUser) { await t.rollback(); return res.status(400).json(error('天梯用户信息无效', 400)); } // 检查发起者是否有未结束的挑战赛 const myOngoingMatch = await Match.findOne({ where: { store_id, type: MATCH_TYPES.CHALLENGE, status: { [Op.in]: [MATCH_STATUS.PENDING, MATCH_STATUS.ONGOING] }, [Op.or]: [ { challenger_id: myLadderUser.id }, { defender_id: myLadderUser.id } ] } }); if (myOngoingMatch) { await t.rollback(); return res.status(400).json(error('您有未结束的挑战赛,请先完成后再发起新的挑战', 400)); } // 检查被挑战者是否有未结束的挑战赛 const targetOngoingMatch = await Match.findOne({ where: { store_id, type: MATCH_TYPES.CHALLENGE, status: { [Op.in]: [MATCH_STATUS.PENDING, MATCH_STATUS.ONGOING] }, [Op.or]: [ { challenger_id: targetLadderUser.id }, { defender_id: targetLadderUser.id } ] } }); if (targetOngoingMatch) { await t.rollback(); return res.status(400).json(error('对方有未结束的挑战赛,请稍后再试', 400)); } // 创建挑战赛 const match = await Match.create({ store_id, match_code: generateMatchCode(), type: MATCH_TYPES.CHALLENGE, name: `${myLadderUser.real_name} vs ${targetLadderUser.real_name}`, weight: MATCH_WEIGHTS.CHALLENGE, challenger_id: myLadderUser.id, defender_id: targetLadderUser.id, status: MATCH_STATUS.PENDING }, { transaction: t }); // 创建对局 await MatchGame.create({ match_id: match.id, player1_id: myLadderUser.id, player2_id: targetLadderUser.id, status: 0 }, { transaction: t }); await t.commit(); // 发送WebSocket通知给被挑战者 sendChallengeNotification(targetUser.id, { matchId: match.id, matchCode: match.match_code, challenger: { id: myLadderUser.id, realName: myLadderUser.real_name, level: myLadderUser.level, powerScore: myLadderUser.power_score, nickname: user.nickname, avatar: user.avatar } }); res.json(success({ matchId: match.id, matchCode: match.match_code }, '挑战已发起')); } catch (err) { await t.rollback(); console.error('发起挑战失败:', err); res.status(500).json(error('发起挑战失败')); } } // 响应挑战 async respondChallenge(req, res) { const t = await sequelize.transaction(); try { const { match_id, accept } = req.body; const user = req.user; const match = await Match.findByPk(match_id, { include: [{ model: MatchGame, as: 'games' }] }); if (!match || match.status !== MATCH_STATUS.PENDING) { await t.rollback(); return res.status(400).json(error('比赛不存在或状态无效', 400)); } // 验证是否是被挑战者 const defenderLadderUser = await LadderUser.findByPk(match.defender_id); if (defenderLadderUser.user_id !== user.id) { await t.rollback(); return res.status(403).json(error('您不是被挑战者', 403)); } // 如果接受挑战,检查是否有其他未结束的挑战赛 if (accept) { const otherOngoingMatch = await Match.findOne({ where: { store_id: match.store_id, type: MATCH_TYPES.CHALLENGE, status: { [Op.in]: [MATCH_STATUS.PENDING, MATCH_STATUS.ONGOING] }, id: { [Op.ne]: match_id }, [Op.or]: [ { challenger_id: defenderLadderUser.id }, { defender_id: defenderLadderUser.id } ] } }); if (otherOngoingMatch) { await t.rollback(); return res.status(400).json(error('您有未结束的挑战赛,请先完成后再接受新的挑战', 400)); } } if (accept) { // 接受挑战 await match.update({ status: MATCH_STATUS.ONGOING, start_time: new Date() }, { transaction: t }); await MatchGame.update( { status: 1 }, { where: { match_id: match.id }, transaction: t } ); } else { // 拒绝挑战 await match.update({ status: MATCH_STATUS.CANCELLED }, { transaction: t }); } await t.commit(); // 通知挑战者 const challengerLadderUser = await LadderUser.findByPk(match.challenger_id); const challengerUser = await User.findByPk(challengerLadderUser.user_id); sendChallengeNotification(challengerUser.id, { type: accept ? 'challenge_accepted' : 'challenge_rejected', matchId: match.id }); res.json(success(null, accept ? '已接受挑战' : '已拒绝挑战')); } catch (err) { await t.rollback(); console.error('响应挑战失败:', err); res.status(500).json(error('操作失败')); } } // 提交比分 async submitScore(req, res) { const t = await sequelize.transaction(); try { const { match_id, my_score, opponent_score } = req.body; const user = req.user; const match = await Match.findByPk(match_id, { include: [{ model: MatchGame, as: 'games' }] }); if (!match || match.status !== MATCH_STATUS.ONGOING) { await t.rollback(); return res.status(400).json(error('比赛不存在或状态无效', 400)); } const game = match.games[0]; // 获取提交者的天梯用户 const myLadderUser = await LadderUser.findOne({ where: { user_id: user.id, store_id: match.store_id, status: 1 } }); if (!myLadderUser) { await t.rollback(); return res.status(403).json(error('您不是参赛者', 403)); } // 确定比分 let player1Score, player2Score, winnerId, loserId; if (myLadderUser.id === game.player1_id) { player1Score = my_score; player2Score = opponent_score; } else if (myLadderUser.id === game.player2_id) { player1Score = opponent_score; player2Score = my_score; } else { await t.rollback(); return res.status(403).json(error('您不是参赛者', 403)); } // 确定胜败(比分高者胜) if (player1Score > player2Score) { winnerId = game.player1_id; loserId = game.player2_id; } else { winnerId = game.player2_id; loserId = game.player1_id; } // 只有胜者可以提交比分 if (myLadderUser.id !== winnerId) { await t.rollback(); return res.status(400).json(error('败方不能提交比分,请等待胜方提交', 400)); } await game.update({ player1_score: player1Score, player2_score: player2Score, winner_id: winnerId, loser_id: loserId, submit_by: myLadderUser.id, confirm_status: CONFIRM_STATUS.PENDING, status: 2 }, { transaction: t }); await t.commit(); // 通知败方确认 const loserLadderUser = await LadderUser.findByPk(loserId); const loserUser = await User.findByPk(loserLadderUser.user_id); sendScoreConfirmNotification(loserUser.id, { matchId: match.id, gameId: game.id, player1Score, player2Score, submitter: { realName: myLadderUser.real_name } }); res.json(success(null, '比分已提交,等待对方确认')); } catch (err) { await t.rollback(); console.error('提交比分失败:', err); res.status(500).json(error('提交失败')); } } // 确认比分 async confirmScore(req, res) { const t = await sequelize.transaction(); try { const { game_id, confirm } = req.body; const user = req.user; const game = await MatchGame.findByPk(game_id, { include: [{ model: Match, as: 'match' }] }); if (!game || game.confirm_status !== CONFIRM_STATUS.PENDING) { await t.rollback(); return res.status(400).json(error('比赛不存在或状态无效', 400)); } // 获取确认者的天梯用户 const myLadderUser = await LadderUser.findOne({ where: { user_id: user.id, store_id: game.match.store_id, status: 1 } }); // 败方确认 if (myLadderUser.id !== game.loser_id) { await t.rollback(); return res.status(403).json(error('只有败方可以确认比分', 403)); } if (!confirm) { // 有争议,重置比分 await game.update({ player1_score: null, player2_score: null, winner_id: null, loser_id: null, submit_by: null, confirm_status: CONFIRM_STATUS.DISPUTED, status: 1 }, { transaction: t }); await t.commit(); return res.json(success(null, '已标记为有争议,请重新比赛')); } // 确认比分,计算战力值变动 const winner = await LadderUser.findByPk(game.winner_id); const loser = await LadderUser.findByPk(game.loser_id); const { winnerChange, loserChange } = PowerCalculator.calculate( { powerScore: winner.power_score, level: winner.level, protectedMatches: winner.protected_matches }, { powerScore: loser.power_score, level: loser.level, protectedMatches: loser.protected_matches }, parseFloat(game.match.weight) ); // 更新游戏记录 await game.update({ confirm_status: CONFIRM_STATUS.CONFIRMED, confirmed_by: myLadderUser.id, confirmed_at: new Date(), winner_power_change: winnerChange, loser_power_change: loserChange }, { transaction: t }); // 更新胜者战力值和统计 await winner.update({ power_score: winner.power_score + winnerChange, match_count: winner.match_count + 1, monthly_match_count: winner.monthly_match_count + 1, win_count: winner.win_count + 1, last_match_time: new Date() }, { transaction: t }); // 更新败者战力值和统计 const newLoserProtected = loser.level <= 2 && loser.protected_matches < POWER_CALC.NEWBIE_PROTECTION ? loser.protected_matches + 1 : loser.protected_matches; await loser.update({ power_score: Math.max(0, loser.power_score + loserChange), match_count: loser.match_count + 1, monthly_match_count: loser.monthly_match_count + 1, protected_matches: newLoserProtected, last_match_time: new Date() }, { transaction: t }); // 更新比赛状态 await game.match.update({ status: MATCH_STATUS.FINISHED, end_time: new Date() }, { transaction: t }); await t.commit(); res.json(success({ winnerChange, loserChange, winnerNewScore: winner.power_score + winnerChange, loserNewScore: Math.max(0, loser.power_score + loserChange) }, '比分已确认')); } catch (err) { await t.rollback(); console.error('确认比分失败:', err); res.status(500).json(error('确认失败')); } } // 加入排位赛 async joinRankingMatch(req, res) { const t = await sequelize.transaction(); try { const { match_code } = req.body; const user = req.user; const match = await Match.findOne({ where: { match_code, type: MATCH_TYPES.RANKING } }); if (!match) { await t.rollback(); return res.status(404).json(error('比赛不存在', 404)); } if (match.stage !== RANKING_STAGE.SIGNUP) { await t.rollback(); return res.status(400).json(error('比赛已开始,无法加入', 400)); } // 获取天梯用户 const ladderUser = await LadderUser.findOne({ where: { user_id: user.id, store_id: match.store_id, status: 1 } }); if (!ladderUser) { await t.rollback(); return res.status(400).json(error('您不是该门店的天梯用户', 400)); } // 检查是否已加入 const existing = await MatchPlayer.findOne({ where: { match_id: match.id, ladder_user_id: ladderUser.id } }); if (existing) { await t.rollback(); return res.status(400).json(error('您已加入该比赛', 400)); } // 加入比赛 await MatchPlayer.create({ match_id: match.id, ladder_user_id: ladderUser.id, initial_power: ladderUser.power_score, player_status: 'waiting' }, { transaction: t }); await t.commit(); res.json(success({ matchId: match.id, matchName: match.name }, '加入成功')); } catch (err) { await t.rollback(); console.error('加入排位赛失败:', err); res.status(500).json(error('加入失败')); } } // 获取排位赛详情 async getRankingDetail(req, res) { try { const { matchCode } = req.params; const match = await Match.findOne({ where: { match_code: matchCode }, include: [ { model: MatchPlayer, as: 'players', include: [{ model: LadderUser, as: 'ladderUser', include: [{ model: User, as: 'user' }] }] }, { model: MatchRound, as: 'rounds', include: [{ model: MatchGame, as: 'games' }] }, { model: Store, as: 'store' } ] }); if (!match) { return res.status(404).json(error('比赛不存在', 404)); } const stageNames = ['报名中', '循环赛', '淘汰赛', '已结束']; const statusNames = ['待开始', '进行中', '已结束', '已取消']; res.json(success({ id: match.id, matchCode: match.match_code, name: match.name, status: match.status, statusName: statusNames[match.status] || '未知', stage: match.stage, stageName: stageNames[match.stage] || '未知', weight: match.weight, storeName: match.store?.name, startTime: match.start_time, players: match.players.map(p => ({ id: p.id, ladderUserId: p.ladder_user_id, realName: p.ladderUser?.real_name, nickname: p.ladderUser?.user?.nickname, avatar: p.ladderUser?.user?.avatar, level: p.ladderUser?.level, initialPower: p.initial_power, winCount: p.win_count, loseCount: p.lose_count, rank: p.rank, status: p.player_status })), rounds: match.rounds.map(r => ({ id: r.id, roundNumber: r.round_number, roundType: r.round_type, roundName: r.round_name, status: r.status, games: r.games.map(g => ({ id: g.id, player1Id: g.player1_id, player2Id: g.player2_id, player1Score: g.player1_score, player2Score: g.player2_score, winnerId: g.winner_id, confirmStatus: g.confirm_status, status: g.status })) })) })); } catch (err) { console.error('获取排位赛详情失败:', err); res.status(500).json(error('获取失败')); } } // 获取我的当前对局 async getMyCurrentGame(req, res) { try { const { matchCode } = req.params; const user = req.user; const match = await Match.findOne({ where: { match_code: matchCode } }); if (!match) { return res.status(404).json(error('比赛不存在', 404)); } const ladderUser = await LadderUser.findOne({ where: { user_id: user.id, store_id: match.store_id, status: 1 } }); if (!ladderUser) { return res.status(400).json(error('您不是参赛者', 400)); } const player = await MatchPlayer.findOne({ where: { match_id: match.id, ladder_user_id: ladderUser.id } }); if (!player) { return res.status(400).json(error('您不是参赛者', 400)); } // 查找当前进行中的对局 const currentGame = await MatchGame.findOne({ where: { match_id: match.id, [Op.or]: [ { player1_id: ladderUser.id }, { player2_id: ladderUser.id } ], status: { [Op.lt]: 2 } // 未结束 }, include: [{ model: MatchRound, as: 'round' }] }); if (!currentGame) { return res.json(success({ status: player.player_status, currentGame: null })); } // 获取对手信息 const opponentId = currentGame.player1_id === ladderUser.id ? currentGame.player2_id : currentGame.player1_id; const opponent = await LadderUser.findByPk(opponentId, { include: [{ model: User, as: 'user' }] }); res.json(success({ status: player.player_status, currentGame: { id: currentGame.id, roundName: currentGame.round?.round_name, opponent: { id: opponent.id, realName: opponent.real_name, nickname: opponent.user?.nickname, avatar: opponent.user?.avatar, level: opponent.level, powerScore: opponent.power_score }, myScore: currentGame.player1_id === ladderUser.id ? currentGame.player1_score : currentGame.player2_score, opponentScore: currentGame.player1_id === ladderUser.id ? currentGame.player2_score : currentGame.player1_score, confirmStatus: currentGame.confirm_status, gameStatus: currentGame.status } })); } catch (err) { console.error('获取当前对局失败:', err); res.status(500).json(error('获取失败')); } } // 提交排位赛比分 async submitRankingScore(req, res) { // 逻辑与挑战赛类似,但需要处理排位赛的轮次逻辑 return this.submitScore(req, res); } // 确认排位赛比分 async confirmRankingScore(req, res) { return this.confirmScore(req, res); } // 获取我的比赛记录 async getMyMatches(req, res) { try { const { store_id, page = 1, pageSize = 20 } = req.query; const { limit, offset } = getPagination(page, pageSize); const user = req.user; const ladderUser = await LadderUser.findOne({ where: { user_id: user.id, store_id, status: 1 } }); if (!ladderUser) { return res.json(pageResult([], 0, page, pageSize)); } const { rows, count } = await MatchGame.findAndCountAll({ where: { [Op.or]: [ { player1_id: ladderUser.id }, { player2_id: ladderUser.id } ], confirm_status: CONFIRM_STATUS.CONFIRMED }, include: [ { model: Match, as: 'match', attributes: ['id', 'name', 'type', 'weight'] } ], order: [['confirmed_at', 'DESC']], limit, offset }); const list = await Promise.all(rows.map(async game => { const isPlayer1 = game.player1_id === ladderUser.id; const opponentId = isPlayer1 ? game.player2_id : game.player1_id; const opponent = await LadderUser.findByPk(opponentId); return { id: game.id, matchId: game.match_id, matchName: game.match?.name, matchType: game.match?.type, opponentName: opponent?.real_name, myScore: isPlayer1 ? game.player1_score : game.player2_score, opponentScore: isPlayer1 ? game.player2_score : game.player1_score, isWin: game.winner_id === ladderUser.id, powerChange: game.winner_id === ladderUser.id ? game.winner_power_change : game.loser_power_change, confirmedAt: game.confirmed_at }; })); res.json(pageResult(list, count, page, pageSize)); } catch (err) { console.error('获取比赛记录失败:', err); res.status(500).json(error('获取失败')); } } // 获取正在进行中的比赛 async getOngoingMatches(req, res) { try { const { store_id } = req.query; const user = req.user; const ladderUser = await LadderUser.findOne({ where: { user_id: user.id, store_id, status: 1 } }); if (!ladderUser) { return res.json(success([])); } // 先获取用户参与的排位赛比赛ID const myMatchPlayers = await MatchPlayer.findAll({ where: { ladder_user_id: ladderUser.id }, attributes: ['match_id'] }); const rankingMatchIds = myMatchPlayers.map(mp => mp.match_id); // 查找用户参与的正在进行中的比赛 const whereCondition = { store_id, status: { [Op.in]: [MATCH_STATUS.PENDING, MATCH_STATUS.ONGOING] }, [Op.or]: [ // 挑战赛:作为挑战者或被挑战者 { challenger_id: ladderUser.id }, { defender_id: ladderUser.id } ] }; // 如果有排位赛参与记录,添加到查询条件 if (rankingMatchIds.length > 0) { whereCondition[Op.or].push({ id: { [Op.in]: rankingMatchIds } }); } const ongoingMatches = await Match.findAll({ where: whereCondition, include: [ { model: Store, as: 'store', attributes: ['id', 'name'] } ], order: [['created_at', 'DESC']] }); const list = await Promise.all(ongoingMatches.map(async match => { let opponent = null; let myStatus = 'waiting'; let currentGame = null; if (match.type === MATCH_TYPES.CHALLENGE) { // 挑战赛:获取对手信息 const opponentId = match.challenger_id === ladderUser.id ? match.defender_id : match.challenger_id; const opponentLadder = await LadderUser.findByPk(opponentId, { include: [{ model: User, as: 'user', attributes: ['nickname', 'avatar'] }] }); if (opponentLadder) { opponent = { id: opponentLadder.id, realName: opponentLadder.real_name, nickname: opponentLadder.user?.nickname, avatar: opponentLadder.user?.avatar, level: opponentLadder.level, powerScore: opponentLadder.power_score }; } myStatus = match.status === MATCH_STATUS.PENDING ? 'waiting' : 'playing'; } else { // 排位赛:获取参赛者状态和当前对局 const player = await MatchPlayer.findOne({ where: { match_id: match.id, ladder_user_id: ladderUser.id } }); if (player) { myStatus = player.player_status; // 获取当前对局 const game = await MatchGame.findOne({ where: { match_id: match.id, [Op.or]: [ { player1_id: ladderUser.id }, { player2_id: ladderUser.id } ], status: { [Op.lt]: 2 } } }); if (game) { const opponentId = game.player1_id === ladderUser.id ? game.player2_id : game.player1_id; const opponentLadder = await LadderUser.findByPk(opponentId, { include: [{ model: User, as: 'user', attributes: ['nickname', 'avatar'] }] }); if (opponentLadder) { opponent = { id: opponentLadder.id, realName: opponentLadder.real_name, nickname: opponentLadder.user?.nickname, avatar: opponentLadder.user?.avatar, level: opponentLadder.level, powerScore: opponentLadder.power_score }; } currentGame = { id: game.id, myScore: game.player1_id === ladderUser.id ? game.player1_score : game.player2_score, opponentScore: game.player1_id === ladderUser.id ? game.player2_score : game.player1_score, confirmStatus: game.confirm_status, gameStatus: game.status }; } } } // 获取排位赛参赛人数 let playerCount = 0; if (match.type === MATCH_TYPES.RANKING) { playerCount = await MatchPlayer.count({ where: { match_id: match.id } }); } return { id: match.id, matchCode: match.match_code, type: match.type, typeName: match.type === MATCH_TYPES.CHALLENGE ? '挑战赛' : '排位赛', name: match.name, weight: match.weight, status: match.status, statusName: match.status === MATCH_STATUS.PENDING ? '待开始' : '进行中', stage: match.stage, stageName: match.type === MATCH_TYPES.RANKING ? ['报名中', '循环赛', '淘汰赛', '已结束'][match.stage] : null, myStatus, opponent, currentGame, playerCount, startTime: match.start_time, createdAt: match.created_at }; })); res.json(success(list)); } catch (err) { console.error('获取进行中比赛失败:', err); res.status(500).json(error('获取失败')); } } // 获取待确认的比赛 async getPendingConfirm(req, res) { try { const { store_id } = req.query; const user = req.user; const ladderUser = await LadderUser.findOne({ where: { user_id: user.id, store_id, status: 1 } }); if (!ladderUser) { return res.json(success([])); } // 查找需要我确认的比赛(我是败方) const pendingGames = await MatchGame.findAll({ where: { loser_id: ladderUser.id, confirm_status: CONFIRM_STATUS.PENDING }, include: [ { model: Match, as: 'match' } ], order: [['created_at', 'DESC']] }); const list = await Promise.all(pendingGames.map(async game => { const winner = await LadderUser.findByPk(game.winner_id); return { id: game.id, matchId: game.match_id, matchName: game.match?.name, matchType: game.match?.type, opponentName: winner?.real_name, myScore: game.player1_id === ladderUser.id ? game.player1_score : game.player2_score, opponentScore: game.player1_id === ladderUser.id ? game.player2_score : game.player1_score, createdAt: game.created_at }; })); res.json(success(list)); } catch (err) { console.error('获取待确认比赛失败:', err); res.status(500).json(error('获取失败')); } } // 获取比赛详情 async getMatchDetail(req, res) { try { const { id } = req.params; const user = req.user; const match = await Match.findByPk(id, { include: [ { model: MatchGame, as: 'games' }, { model: Store, as: 'store' } ] }); if (!match) { return res.status(404).json(error('比赛不存在', 404)); } // 获取当前用户的天梯用户信息 const myLadderUser = await LadderUser.findOne({ where: { user_id: user.id, store_id: match.store_id, status: 1 }, include: [{ model: User, as: 'user', attributes: ['nickname', 'avatar'] }] }); // 调试日志 console.log('获取比赛详情 - 用户信息:', { userId: user.id, storeId: match.store_id, myLadderUserId: myLadderUser?.id, matchChallengerId: match.challenger_id, matchDefenderId: match.defender_id, matchStatus: match.status }); let challengerInfo = null; let defenderInfo = null; let myRole = null; // 'challenger' | 'defender' | null let canAccept = false; let canReject = false; let canSubmitScore = false; let canConfirmScore = false; if (match.type === MATCH_TYPES.CHALLENGE) { // 挑战赛:获取挑战者和被挑战者信息 const challengerLadder = await LadderUser.findByPk(match.challenger_id, { include: [{ model: User, as: 'user', attributes: ['nickname', 'avatar'] }] }); const defenderLadder = await LadderUser.findByPk(match.defender_id, { include: [{ model: User, as: 'user', attributes: ['nickname', 'avatar'] }] }); if (challengerLadder) { challengerInfo = { id: challengerLadder.id, realName: challengerLadder.real_name, nickname: challengerLadder.user?.nickname, avatar: challengerLadder.user?.avatar, level: challengerLadder.level, powerScore: challengerLadder.power_score, userId: challengerLadder.user_id, // 添加 user_id,用于前端判断 phone: challengerLadder.phone // 添加手机号,用于前端判断 }; } if (defenderLadder) { defenderInfo = { id: defenderLadder.id, realName: defenderLadder.real_name, nickname: defenderLadder.user?.nickname, avatar: defenderLadder.user?.avatar, level: defenderLadder.level, powerScore: defenderLadder.power_score, userId: defenderLadder.user_id, // 添加 user_id,用于前端判断 phone: defenderLadder.phone // 添加手机号,用于前端判断 }; } // 确定当前用户角色 // 方法1:通过 myLadderUser.id 比较(如果用户是该门店的天梯用户) if (myLadderUser) { // 使用 == 比较,因为可能是数字或字符串 if (myLadderUser.id == match.challenger_id) { myRole = 'challenger'; console.log('用户是挑战者(通过myLadderUser):', { myLadderUserId: myLadderUser.id, challengerId: match.challenger_id }); } else if (myLadderUser.id == match.defender_id) { myRole = 'defender'; console.log('用户是被挑战者(通过myLadderUser):', { myLadderUserId: myLadderUser.id, defenderId: match.defender_id }); } else { console.log('用户角色不匹配(通过myLadderUser):', { myLadderUserId: myLadderUser.id, challengerId: match.challenger_id, defenderId: match.defender_id }); } } // 方法2:如果 myLadderUser 为 null,通过挑战者和被挑战者的 user_id 来判断 if (!myRole && challengerLadder && defenderLadder) { // 检查挑战者的 user_id if (challengerLadder.user_id && challengerLadder.user_id == user.id) { myRole = 'challenger'; console.log('用户是挑战者(通过user_id):', { userId: user.id, challengerUserId: challengerLadder.user_id, challengerLadderId: challengerLadder.id, matchChallengerId: match.challenger_id }); } // 检查被挑战者的 user_id else if (defenderLadder.user_id && defenderLadder.user_id == user.id) { myRole = 'defender'; console.log('用户是被挑战者(通过user_id):', { userId: user.id, defenderUserId: defenderLadder.user_id, defenderLadderId: defenderLadder.id, matchDefenderId: match.defender_id }); } else { console.log('用户角色不匹配(通过user_id):', { userId: user.id, challengerUserId: challengerLadder.user_id, challengerLadderId: challengerLadder.id, defenderUserId: defenderLadder.user_id, defenderLadderId: defenderLadder.id, matchChallengerId: match.challenger_id, matchDefenderId: match.defender_id }); } } // 方法3:如果前两种方法都失败,尝试通过手机号匹配(如果天梯用户有手机号且用户有手机号) if (!myRole && challengerLadder && defenderLadder && user.phone) { if (challengerLadder.phone && challengerLadder.phone === user.phone) { myRole = 'challenger'; console.log('用户是挑战者(通过手机号):', { userPhone: user.phone, challengerPhone: challengerLadder.phone }); } else if (defenderLadder.phone && defenderLadder.phone === user.phone) { myRole = 'defender'; console.log('用户是被挑战者(通过手机号):', { userPhone: user.phone, defenderPhone: defenderLadder.phone }); } } if (!myRole) { console.log('无法确定用户角色:', { userId: user.id, userPhone: user.phone, storeId: match.store_id, hasMyLadderUser: !!myLadderUser, myLadderUserId: myLadderUser?.id, challengerLadderId: challengerLadder?.id, challengerUserId: challengerLadder?.user_id, challengerPhone: challengerLadder?.phone, defenderLadderId: defenderLadder?.id, defenderUserId: defenderLadder?.user_id, defenderPhone: defenderLadder?.phone, matchChallengerId: match.challenger_id, matchDefenderId: match.defender_id }); } // 判断操作权限 // 待接受状态且是被挑战者,可以接受或拒绝 if (match.status === MATCH_STATUS.PENDING) { if (myRole === 'defender') { canAccept = true; canReject = true; console.log('设置接受/拒绝权限为true:', { myRole, status: match.status, canAccept, canReject }); } else { console.log('不能接受/拒绝:', { myRole, status: match.status, isDefender: myRole === 'defender' }); } } else { console.log('比赛状态不是待接受:', { status: match.status, myRole }); } if (match.status === MATCH_STATUS.ONGOING && myLadderUser) { const game = match.games?.[0]; if (game) { // 检查是否可以提交比分(必须是胜者且未提交) if (game.status === 1 && !game.submit_by) { // 比赛进行中,还未提交比分,双方都可以填写 canSubmitScore = true; } else if (game.status === 2 && game.submit_by && game.submit_by !== myLadderUser.id) { // 对方已提交比分,等待我确认 if (game.confirm_status === CONFIRM_STATUS.PENDING) { canConfirmScore = true; } } } } } const result = { id: match.id, matchCode: match.match_code, type: match.type, name: match.name, weight: match.weight, status: match.status, stage: match.stage, storeName: match.store?.name, startTime: match.start_time, endTime: match.end_time, challenger: challengerInfo, defender: defenderInfo, myRole: myRole || null, canAccept: Boolean(canAccept), canReject: Boolean(canReject), canSubmitScore: Boolean(canSubmitScore), canConfirmScore: Boolean(canConfirmScore), games: match.games?.map(game => ({ id: game.id, player1Id: game.player1_id, player2Id: game.player2_id, player1Score: game.player1_score, player2Score: game.player2_score, winnerId: game.winner_id, loserId: game.loser_id, submitBy: game.submit_by, confirmStatus: game.confirm_status, status: game.status })) || [] }; // 调试日志 console.log('比赛详情返回数据:', { matchId: match.id, matchStatus: match.status, storeId: match.store_id, userId: user.id, myLadderUserId: myLadderUser?.id, myLadderUserType: typeof myLadderUser?.id, challengerId: match.challenger_id, challengerIdType: typeof match.challenger_id, defenderId: match.defender_id, defenderIdType: typeof match.defender_id, myRole, canAccept: result.canAccept, canReject: result.canReject, hasMyLadderUser: !!myLadderUser, challengerMatch: myLadderUser ? (myLadderUser.id == match.challenger_id) : false, defenderMatch: myLadderUser ? (myLadderUser.id == match.defender_id) : false }); // 确保返回所有必要的字段 res.json(success(result)); } catch (err) { console.error('获取比赛详情失败:', err); res.status(500).json(error('获取失败')); } } } module.exports = new MatchController();