php项目统计高球传中争顶成功率?

wen PHP项目 4

本文目录导读:

php项目统计高球传中争顶成功率?

  1. 第一步:数据库表结构设计(示例)
  2. 第二步:PHP 业务逻辑层(统计服务类)
  3. 第三步:控制器调用与API输出(以 Laravel 为例)
  4. 第四步:前端展示(Vue/React 示例片段)
  5. 第五步:进阶优化与注意事项

在PHP项目中统计“高球传中争顶成功率”,通常需要结合数据模型设计业务逻辑计算以及前端或接口展示

由于“成功率”是一个复合指标(成功次数 / 总争顶次数),核心在于如何定义“争顶成功”,以下是一个从数据库设计到PHP代码实现的全流程方案。


第一步:数据库表结构设计(示例)

假设你有球员、比赛和事件记录表,建议新建一张专门记录传中争顶事件的表。

-- 传中争顶事件记录表
CREATE TABLE `cross_duel_events` (
  `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
  `match_id` INT UNSIGNED NOT NULL COMMENT '比赛ID',
  `player_id` INT UNSIGNED NOT NULL COMMENT '球员ID',
  `team_id` INT UNSIGNED NOT NULL COMMENT '球队ID',
  `minute` TINYINT UNSIGNED DEFAULT NULL COMMENT '比赛分钟',
  -- 关键字段:本次争顶是否成功(1=成功,0=失败)
  `is_success` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '1=争顶成功(如头球攻门/头球摆渡给队友/解围成功), 0=失败',
  -- 附加属性:区分是进攻还是防守
  `duel_type` ENUM('attack', 'defense') NOT NULL DEFAULT 'attack' COMMENT '进攻型争顶或防守型解围',
  -- 时间戳
  `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  INDEX `idx_player_time` (`player_id`, `created_at`),
  INDEX `idx_match` (`match_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='高球传中争顶事件表';

第二步:PHP 业务逻辑层(统计服务类)

在PHP中,我们推荐使用 ServiceRepository 模式来处理统计逻辑。

<?php
namespace App\Services;
use App\Models\CrossDuelEvent; // 假设你用 Eloquent(Laravel)或自定义DB类
class CrossDuelStatsService
{
    /**
     * 计算传中争顶成功率
     *
     * @param int|null $playerId  球员ID(可选,若为空统计全队)
     * @param int|null $matchId   比赛ID(可选,若为空统计赛季)
     * @param string   $type      类型:'all' 全部, 'attack' 进攻, 'defense' 防守
     * @return array 包含总数、成功数、失败数、成功率
     */
    public function calculateSuccessRate(?int $playerId = null, ?int $matchId = null, string $type = 'all'): array
    {
        // 1. 构建查询条件
        $query = CrossDuelEvent::query();
        if ($playerId) {
            $query->where('player_id', $playerId);
        }
        if ($matchId) {
            $query->where('match_id', $matchId);
        }
        if ($type !== 'all') {
            $query->where('duel_type', $type);
        }
        // 2. 使用聚合函数一次性查出成功和失败数据
        // 注意:避免在PHP中遍历大量记录,直接在SQL层聚合
        $stats = (clone $query)
            ->selectRaw('COUNT(*) as total_count')
            ->selectRaw('SUM(CASE WHEN is_success = 1 THEN 1 ELSE 0 END) as success_count')
            ->selectRaw('SUM(CASE WHEN is_success = 0 THEN 1 ELSE 0 END) as fail_count')
            ->first();
        // 3. 计算成功率(避免除零错误)
        $total = (int)($stats->total_count ?? 0);
        $success = (int)($stats->success_count ?? 0);
        $fail = (int)($stats->fail_count ?? 0);
        $rate = ($total > 0) ? round(($success / $total) * 100, 2) : 0.00;
        return [
            'total'   => $total,
            'success' => $success,
            'fail'    => $fail,
            'rate'    => $rate, // 返回百分比数值,如 45.67
            'rate_str'=> $rate . '%'
        ];
    }
    /**
     * 获取球队或球员的“争顶最活跃”时间段分析(进阶功能)
     */
    public function getHotZones(int $teamId, int $matchId): array
    {
        // 按15分钟区间分组统计每段的争顶次数和成功率
        $segments = CrossDuelEvent::where('team_id', $teamId)
            ->where('match_id', $matchId)
            ->selectRaw('FLOOR(minute / 15) * 15 as segment_start')
            ->selectRaw('COUNT(*) as cnt')
            ->selectRaw('SUM(is_success) as success')
            ->groupBy('segment_start')
            ->orderBy('segment_start')
            ->get();
        return $segments->map(function ($item) {
            $rate = $item->cnt > 0 ? round(($item->success / $item->cnt) * 100, 1) : 0;
            return [
                'time_segment' => $item->segment_start . '-' . ($item->segment_start + 15) . 'min',
                'attempts' => $item->cnt,
                'success_rate' => $rate
            ];
        })->toArray();
    }
}

第三步:控制器调用与API输出(以 Laravel 为例)

<?php
namespace App\Http\Controllers\Api;
use App\Services\CrossDuelStatsService;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
class StatsController extends Controller
{
    protected $statsService;
    public function __construct(CrossDuelStatsService $statsService)
    {
        $this->statsService = $statsService;
    }
    /**
     * GET /api/stats/cross-duel
     */
    public function index(Request $request)
    {
        // 参数校验
        $playerId = $request->integer('player_id', null);
        $matchId  = $request->integer('match_id', null);
        $type     = $request->string('type', 'all');
        try {
            $result = $this->statsService->calculateSuccessRate($playerId, $matchId, $type);
            return response()->json([
                'code' => 0,
                'data' => $result,
                'msg'  => 'success'
            ]);
        } catch (\Throwable $e) {
            return response()->json([
                'code' => 500,
                'data' => null,
                'msg'  => '统计失败: ' . $e->getMessage()
            ], 500);
        }
    }
}

第四步:前端展示(Vue/React 示例片段)

// Vue 3 组合式API示例
const { data, loading } = useFetch('/api/stats/cross-duel?player_id=7&match_id=123').json();
// 模板中显示
<template>
  <div v-if="!loading">
    <h3>传中争顶成功率</h3>
    <p>总争顶次数:{{ data.total }}</p>
    <p>成功:{{ data.success }} / 失败:{{ data.fail }}</p>
    <div class="progress">
      <div 
        class="progress-bar" 
        :style="{ width: data.rate + '%' }"
      >{{ data.rate_str }}</div>
    </div>
    <!-- 热区图(时间段分析) -->
    <div v-if="hotZones">
      <h4>各时间段争顶活跃度</h4>
      <table>
        <tr v-for="zone in hotZones" :key="zone.time_segment">
          <td>{{ zone.time_segment }}</td>
          <td>{{ zone.attempts }}次</td>
          <td>成功率 {{ zone.success_rate }}%</td>
        </tr>
      </table>
    </div>
  </div>
</template>

第五步:进阶优化与注意事项

  1. 定义“成功”的边界:只统计“高球传中”引起的争顶,而非所有头球,建议在录入数据时增加一个cross_source字段,确保只有传中引出的头球才计入。
  2. 缓存策略:对于高频访问的统计接口(如球队总览),使用 Redis 缓存结果,设置TTL(如60秒),数据变化时主动失效。
    // 使用 Laravel Cache 示例
    $cacheKey = "cross_success_".$playerId.'_'.$matchId.'_'.$type;
    $result = Cache::remember($cacheKey, 60, function() use (...) {
        return $this->calculateSuccessRate(...);
    });
  3. 索引优化:确保 match_id, player_id, created_at 有联合索引,避免全表扫描。
  4. 数据录入校验:前端录入端应限制 is_success 必须是 0 或 1。

如果你需要实时比分直播基于视频的AI自动标记(如通过机器视觉识别争顶成功),那需要另外接入视频分析API,属于数据采集层面的问题,但最终统计逻辑依然可以复用上述PHP核心代码。

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