实时php项目显示场上谁更占优势?

wen PHP项目 1

我可以帮您设计一个实时PHP项目来显示比赛双方的占优情况,这需要前端实时通信(如WebSocket或轮询)和后端计算逻辑的结合。

实时php项目显示场上谁更占优势?

完整解决方案

数据库设计(MySQL)

CREATE TABLE matches (
    id INT PRIMARY KEY AUTO_INCREMENT,
    match_name VARCHAR(100),
    status ENUM('live', 'paused', 'ended') DEFAULT 'live',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE team_stats (
    id INT PRIMARY KEY AUTO_INCREMENT,
    match_id INT,
    team_name VARCHAR(50),
    score INT DEFAULT 0,
    possession FLOAT DEFAULT 50.0,  -- 控球率
    shots INT DEFAULT 0,           -- 射门次数
    shots_on_target INT DEFAULT 0, -- 射正次数
    corners INT DEFAULT 0,
    fouls INT DEFAULT 0,
    passes INT DEFAULT 0,
    pass_accuracy FLOAT DEFAULT 0, -- 传球成功率
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (match_id) REFERENCES matches(id)
);
CREATE TABLE match_events (
    id INT PRIMARY KEY AUTO_INCREMENT,
    match_id INT,
    team_name VARCHAR(50),
    event_type ENUM('goal', 'shot', 'corner', 'foul', 'card'),
    minute INT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

优势计算逻辑(PHP类)

<?php
// AdvantageCalculator.php
class AdvantageCalculator {
    private $db;
    public function __construct($pdo) {
        $this->db = $pdo;
    }
    // 计算综合优势分数 (0-100)
    public function calculateAdvantage($matchId) {
        $stats = $this->getTeamStats($matchId);
        if (!$stats || count($stats) < 2) {
            return ['team_a' => 50, 'team_b' => 50, 'details' => []];
        }
        $teamA = $stats[0];
        $teamB = $stats[1];
        // 各维度权重
        $weights = [
            'score' => 0.25,          // 比分权重
            'possession' => 0.20,     // 控球率
            'shots' => 0.15,          // 射门
            'shots_on_target' => 0.20, // 射正
            'corners' => 0.10,        // 角球
            'pass_accuracy' => 0.10   // 传球成功率
        ];
        // 计算各维度得分 (0-100)
        $scores = [];
        // 比分得分
        $totalGoals = $teamA['score'] + $teamB['score'];
        if ($totalGoals > 0) {
            $scores['score'] = ($teamA['score'] / max($totalGoals, 1)) * 100;
        } else {
            $scores['score'] = 50;
        }
        // 控球率(已经是一个百分比)
        $scores['possession'] = $teamA['possession'];
        // 射门次数
        $totalShots = $teamA['shots'] + $teamB['shots'];
        $scores['shots'] = $totalShots > 0 ? 
            ($teamA['shots'] / $totalShots) * 100 : 50;
        // 射正次数(比重更高)
        $totalOnTarget = $teamA['shots_on_target'] + $teamB['shots_on_target'];
        $scores['shots_on_target'] = $totalOnTarget > 0 ? 
            ($teamA['shots_on_target'] / $totalOnTarget) * 100 : 50;
        // 角球
        $totalCorners = $teamA['corners'] + $teamB['corners'];
        $scores['corners'] = $totalCorners > 0 ? 
            ($teamA['corners'] / $totalCorners) * 100 : 50;
        // 传球成功率
        $scores['pass_accuracy'] = $this->calculatePassAdvantage(
            $teamA['pass_accuracy'], $teamB['pass_accuracy']
        );
        // 加权计算最终优势
        $finalAdvantage = 0;
        foreach ($weights as $key => $weight) {
            $finalAdvantage += $scores[$key] * $weight;
        }
        return [
            'team_a' => round($finalAdvantage, 1),
            'team_b' => round(100 - $finalAdvantage, 1),
            'details' => [
                'teams' => [$teamA['team_name'], $teamB['team_name']],
                'metrics' => $scores,
                'weights' => $weights
            ]
        ];
    }
    private function calculatePassAdvantage($accA, $accB) {
        // 将传球成功率转换为相对优势
        $diff = $accA - $accB;
        $advantage = 50 + ($diff * 5); // 每5%差距 = 25分差距
        return max(0, min(100, $advantage));
    }
    private function getTeamStats($matchId) {
        $stmt = $this->db->prepare("
            SELECT * FROM team_stats 
            WHERE match_id = ? 
            ORDER BY id ASC
        ");
        $stmt->execute([$matchId]);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
}

实时数据更新接口

<?php
// update_stats.php
header('Content-Type: application/json');
require_once 'db_connection.php';
require_once 'AdvantageCalculator.php';
$pdo = getConnection();
$calculator = new AdvantageCalculator($pdo);
$matchId = $_POST['match_id'] ?? 1;
$teamName = $_POST['team_name'] ?? '';
$action = $_POST['action'] ?? '';
// 更新统计数据
try {
    $pdo->beginTransaction();
    switch ($action) {
        case 'goal':
            $stmt = $pdo->prepare("UPDATE team_stats SET score = score + 1 WHERE match_id = ? AND team_name = ?");
            $stmt->execute([$matchId, $teamName]);
            // 记录事件
            $eventStmt = $pdo->prepare("INSERT INTO match_events (match_id, team_name, event_type, minute) VALUES (?, ?, 'goal', ?)");
            $eventStmt->execute([$matchId, $teamName, time()]);
            break;
        case 'shot':
            $stmt = $pdo->prepare("UPDATE team_stats SET shots = shots + 1 WHERE match_id = ? AND team_name = ?");
            $stmt->execute([$matchId, $teamName]);
            break;
        case 'corner':
            $stmt = $pdo->prepare("UPDATE team_stats SET corners = corners + 1 WHERE match_id = ? AND team_name = ?");
            $stmt->execute([$matchId, $teamName]);
            break;
        case 'possession':
            $possession = floatval($_POST['value'] ?? 50);
            $opponent = 100 - $possession;
            $stmt = $pdo->prepare("UPDATE team_stats SET possession = ? WHERE match_id = ? AND team_name = ?");
            $stmt->execute([$possession, $matchId, $teamName]);
            // 更新对手
            $oppStmt = $pdo->prepare("UPDATE team_stats SET possession = ? WHERE match_id = ? AND team_name != ?");
            $oppStmt->execute([$opponent, $matchId, $teamName]);
            break;
    }
    // 计算最新优势
    $advantage = $calculator->calculateAdvantage($matchId);
    $pdo->commit();
    echo json_encode([
        'success' => true,
        'data' => $advantage,
        'timestamp' => time()
    ]);
} catch (Exception $e) {
    $pdo->rollBack();
    echo json_encode([
        'success' => false,
        'error' => $e->getMessage()
    ]);
}

前端实时显示(使用轮询)

<?php
// live_match.php
?>
<!DOCTYPE html>
<html>
<head>实时比赛优势显示</title>
    <style>
        body { font-family: Arial, sans-serif; background: #f0f0f0; }
        .container { max-width: 800px; margin: 0 auto; padding: 20px; }
        .match-header { text-align: center; margin-bottom: 30px; }
        .team-row { display: flex; justify-content: space-between; margin: 20px 0; }
        .team { flex: 1; padding: 20px; background: #fff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,.1); }
        .vs { flex: 0 0 100px; text-align: center; font-size: 24px; font-weight: bold; line-height: 100px; }
        .advantage-bar { background: #e0e0e0; height: 30px; border-radius: 15px; margin: 20px 0; position: relative; }
        .advantage-fill { height: 100%; border-radius: 15px; transition: width 0.5s ease; background: linear-gradient(90deg, #4CAF50, #8BC34A); }
        .advantage-text { position: absolute; top: 50%; transform: translateY(-50%); font-weight: bold; }
        .team-name { font-size: 24px; font-weight: bold; text-align: center; }
        .score { font-size: 48px; font-weight: bold; text-align: center; }
        .metric { margin: 10px 0; }
        .metric-label { text-align: center; color: #666; }
        .metric-value { text-align: center; font-size: 20px; }
        .stats-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
        #live-indicator {
            display: inline-block;
            width: 10px; height: 10px;
            background: #ff1744;
            border-radius: 50%;
            animation: pulse 1s infinite;
            margin-right: 5px;
        }
        @keyframes pulse {
            0% { opacity: 1; }
            50% { opacity: 0.3; }
            100% { opacity: 1; }
        }
        .advantage-badge {
            display: inline-block;
            padding: 5px 10px;
            border-radius: 20px;
            font-weight: bold;
            margin-left: 10px;
        }
        .leading { background: #4CAF50; color: white; }
        .trailing { background: #ff1744; color: white; }
        .equal { background: #ffc107; color: black; }
    </style>
</head>
<body>
    <div class="container">
        <div class="match-header">
            <h1>实时比赛优势分析 <span id="live-indicator"></span></h1>
            <div id="match-status">比赛进行中</div>
        </div>
        <div class="team-row" id="team-info">
            <div class="team">
                <div class="team-name" id="teamA-name">主队</div>
                <div class="score" id="teamA-score">0</div>
                <div id="teamA-advantage" class="advantage-badge">50%</div>
            </div>
            <div class="vs">VS</div>
            <div class="team">
                <div class="team-name" id="teamB-name">客队</div>
                <div class="score" id="teamB-score">0</div>
                <div id="teamB-advantage" class="advantage-badge">50%</div>
            </div>
        </div>
        <div class="advantage-bar">
            <div class="advantage-fill" id="advantage-bar" style="width: 50%"></div>
            <div class="advantage-text" id="advantage-label" style="left: 45%">50 - 50</div>
        </div>
        <div class="stats-grid">
            <div>
                <h3>主队数据</h3>
                <div class="metric">
                    <div class="metric-label">控球率</div>
                    <div class="metric-value" id="teamA-possession">50%</div>
                </div>
                <div class="metric">
                    <div class="metric-label">射门</div>
                    <div class="metric-value" id="teamA-shots">0</div>
                </div>
                <div class="metric">
                    <div class="metric-label">射正</div>
                    <div class="metric-value" id="teamA-shots-on-target">0</div>
                </div>
                <div class="metric">
                    <div class="metric-label">角球</div>
                    <div class="metric-value" id="teamA-corners">0</div>
                </div>
                <div class="metric">
                    <div class="metric-label">传球成功率</div>
                    <div class="metric-value" id="teamA-pass-acc">0%</div>
                </div>
            </div>
            <div>
                <h3>客队数据</h3>
                <div class="metric">
                    <div class="metric-label">控球率</div>
                    <div class="metric-value" id="teamB-possession">50%</div>
                </div>
                <div class="metric">
                    <div class="metric-label">射门</div>
                    <div class="metric-value" id="teamB-shots">0</div>
                </div>
                <div class="metric">
                    <div class="metric-label">射正</div>
                    <div class="metric-value" id="teamB-shots-on-target">0</div>
                </div>
                <div class="metric">
                    <div class="metric-label">角球</div>
                    <div class="metric-value" id="teamB-corners">0</div>
                </div>
                <div class="metric">
                    <div class="metric-label">传球成功率</div>
                    <div class="metric-value" id="teamB-pass-acc">0%</div>
                </div>
            </div>
        </div>
        <div id="analysis" style="background: #fff; padding: 20px; margin: 20px 0; border-radius: 8px;">
            <h3>实时分析</h3>
            <p id="analysis-text">正在分析比赛局势...</p>
        </div>
    </div>
    <script>
        // 初始化比赛数据
        let matchData = {
            teamA: '主队',
            teamB: '客队',
            advantageA: 50,
            advantageB: 50
        };
        // 更新UI
        function updateUI(data) {
            // 更新比分
            document.getElementById('teamA-score').textContent = data.details.scores.score ? 
                Math.round(data.details.scores.score * data.details.scores.score / 100) : 0;
            // 更新优势百分比
            const advantageA = Math.round(data.team_a);
            const advantageB = Math.round(data.team_b);
            document.getElementById('teamA-advantage').textContent = advantageA + '%';
            document.getElementById('teamB-advantage').textContent = advantageB + '%';
            // 更新优势条
            document.getElementById('advantage-bar').style.width = advantageA + '%';
            document.getElementById('advantage-label').textContent = advantageA + ' - ' + advantageB;
            // 更新优势标签
            document.getElementById('teamA-advantage').className = 'advantage-badge ' + 
                (advantageA > advantageB ? 'leading' : advantageA < advantageB ? 'trailing' : 'equal');
            document.getElementById('teamB-advantage').className = 'advantage-badge ' + 
                (advantageB > advantageA ? 'leading' : advantageB < advantageA ? 'trailing' : 'equal');
            // 更新详细数据
            if (data.details.metrics) {
                const metrics = data.details.metrics;
                const teams = data.details.teams;
                document.getElementById('teamA-shots').textContent = metrics.shots ? Math.round(metrics.shots) : 0;
                document.getElementById('teamB-shots').textContent = metrics.shots ? Math.round(100 - metrics.shots) : 0;
                // 控球率
                const possessionA = metrics.possession;
                document.getElementById('teamA-possession').textContent = possessionA.toFixed(1) + '%';
                document.getElementById('teamB-possession').textContent = (100 - possessionA).toFixed(1) + '%';
                // 射正
                const onTargetA = metrics.shots_on_target;
                document.getElementById('teamA-shots-on-target').textContent = Math.round(onTargetA / 100 * 10) + ' 次';
                document.getElementById('teamB-shots-on-target').textContent = Math.round((100 - onTargetA) / 100 * 10) + ' 次';
                // 分析文本
                updateAnalysis(advantageA, advantageB);
            }
        }
        // 生成分析文本
        function updateAnalysis(advantageA, advantageB) {
            const diff = advantageA - advantageB;
            let text = '';
            if (Math.abs(diff) < 5) {
                text = '比赛非常胶着,双方势均力敌,谁能坚持到最后仍难预料。';
            } else if (diff > 20) {
                text = '主队目前占据明显优势,进攻效率和整体表现都优于对手。';
            } else if (diff > 10) {
                text = '主队稍占上风,但客队仍有机会扭转局势。';
            } else if (diff < -20) {
                text = '客队目前占据明显优势,主队需要做出调整。';
            } else if (diff < -10) {
                text = '客队稍占上风,主队仍有反击的机会。';
            }
            document.getElementById('analysis-text').textContent = text;
        }
        // 定时更新数据(每3秒)
        function fetchMatchData() {
            fetch('get_match_data.php?match_id=1')
                .then(response => response.json())
                .then(data => {
                    if (data.success) {
                        updateUI(data.data);
                    }
                })
                .catch(error => console.error('Error:', error));
        }
        // 启动更新
        setInterval(fetchMatchData, 3000);
        fetchMatchData(); // 初始加载
    </script>
</body>
</html>

数据获取接口

<?php
// get_match_data.php
header('Content-Type: application/json');
require_once 'db_connection.php';
require_once 'AdvantageCalculator.php';
$pdo = getConnection();
$calculator = new AdvantageCalculator($pdo);
$matchId = $_GET['match_id'] ?? 1;
$advantage = $calculator->calculateAdvantage($matchId);
// 获取当前比分
$stmt = $pdo->prepare("SELECT team_name, score FROM team_stats WHERE match_id = ?");
$stmt->execute([$matchId]);
$scores = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode([
    'success' => true,
    'data' => $advantage,
    'scores' => $scores
]);

使用说明

  1. 初始化数据:创建比赛数据,插入初始统计
  2. 更新数据:通过调用 update_stats.php 接口更新统计数据
  3. 实时显示:前端通过轮询或 WebSocket 获取最新优势数据

这是一个完整的解决方案,可以根据需要调整权重和指标,如果您需要WebSocket实现或其他改进,请告诉我!

抱歉,评论功能暂时关闭!