本文目录导读:

我来帮你设计一个PHP项目来统计落叶球射门成功率,落叶球(Knuckleball)是一种特殊的射门技术,需要特定的统计维度。
项目架构设计
// 项目目录结构
/
├── config/
│ └── database.php // 数据库配置
├── includes/
│ ├── Database.php // 数据库连接类
│ ├── ShootingStats.php // 射门统计核心类
│ └── Validator.php // 数据验证类
├── api/
│ ├── record_shot.php // 记录射门数据
│ ├── get_stats.php // 获取统计数据
│ └── get_analysis.php // 获取详细分析
├── admin/
│ ├── dashboard.php // 管理后台
│ └── stats_view.php // 统计展示页面
└── sql/
└── schema.sql // 数据库结构
数据库结构
-- sql/schema.sql
CREATE DATABASE IF NOT EXISTS football_stats;
USE football_stats;
-- 球员表
CREATE TABLE players (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL,
position VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 射门事件表
CREATE TABLE shot_events (
id INT PRIMARY KEY AUTO_INCREMENT,
player_id INT,
match_id VARCHAR(50),
shot_time DATETIME,
shot_type ENUM('knuckleball', 'normal', 'curve', 'drive') DEFAULT 'normal',
distance_meters DECIMAL(4,1),
shot_angle DECIMAL(4,1),
ball_velocity_kmh INT,
shot_accuracy ENUM('target', 'off_target', 'blocked', 'saved'),
is_goal BOOLEAN DEFAULT 0,
weather_condition ENUM('sunny', 'rainy', 'windy', 'cloudy'),
field_condition ENUM('dry', 'wet', 'artificial', 'natural'),
opponent_strength ENUM('weak', 'medium', 'strong'),
foot_used ENUM('right', 'left'),
spin_rate_rpm INT DEFAULT 0,
trajectory_swing VARCHAR(50), // 落叶球摆动幅度
notes TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (player_id) REFERENCES players(id)
);
-- 训练记录表
CREATE TABLE training_sessions (
id INT PRIMARY KEY AUTO_INCREMENT,
player_id INT,
session_date DATE,
total_shots INT,
successful_shots INT,
goal_conversions INT,
average_speed INT,
notes TEXT,
FOREIGN KEY (player_id) REFERENCES players(id)
);
-- 统计汇总表
CREATE TABLE monthly_stats (
id INT PRIMARY KEY AUTO_INCREMENT,
player_id INT,
stat_month DATE,
total_shots INT,
on_target INT,
goals INT,
knuckleball_success_rate DECIMAL(5,2),
avg_velocity INT,
FOREIGN KEY (player_id) REFERENCES players(id)
);
核心统计类
<?php
// includes/ShootingStats.php
class ShootingStats {
private $db;
private $playerId;
public function __construct($db, $playerId = null) {
$this->db = $db;
$this->playerId = $playerId;
}
/**
* 记录一次射门
*/
public function recordShot($data) {
$query = "INSERT INTO shot_events
(player_id, match_id, shot_time, shot_type, distance_meters,
shot_angle, ball_velocity_kmh, shot_accuracy, is_goal,
weather_condition, field_condition, opponent_strength,
foot_used, spin_rate_rpm, trajectory_swing, notes)
VALUES
(:player_id, :match_id, :shot_time, :shot_type, :distance_meters,
:shot_angle, :ball_velocity_kmh, :shot_accuracy, :is_goal,
:weather_condition, :field_condition, :opponent_strength,
:foot_used, :spin_rate_rpm, :trajectory_swing, :notes)";
$stmt = $this->db->prepare($query);
return $stmt->execute($data);
}
/**
* 计算落叶球成功率
*/
public function calculateKnuckleballSuccessRate($playerId = null, $dateRange = null) {
$conditions = [];
$params = [];
if ($playerId) {
$conditions[] = "player_id = :player_id";
$params[':player_id'] = $playerId;
}
if ($dateRange) {
$conditions[] = "shot_time BETWEEN :start_date AND :end_date";
$params[':start_date'] = $dateRange['start'];
$params[':end_date'] = $dateRange['end'];
}
$whereClause = $conditions ? "WHERE " . implode(" AND ", $conditions) : "";
$query = "SELECT
COUNT(*) as total_shots,
SUM(CASE WHEN is_goal = 1 THEN 1 ELSE 0 END) as goals,
SUM(CASE WHEN shot_accuracy = 'target' THEN 1 ELSE 0 END) as on_target,
AVG(CASE WHEN is_goal = 1 THEN 100.0 ELSE 0 END) as success_rate
FROM shot_events
WHERE shot_type = 'knuckleball'
$whereClause";
$stmt = $this->db->prepare($query);
$stmt->execute($params);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
/**
* 计算综合射门分析
*/
public function getKnuckleballAnalysis() {
$query = "SELECT
player_id,
COUNT(*) as total_attempts,
SUM(is_goal) as total_goals,
ROUND((SUM(is_goal) / COUNT(*)) * 100, 2) as success_rate,
ROUND(AVG(ball_velocity_kmh), 2) as avg_speed,
ROUND(AVG(spin_rate_rpm), 2) as avg_spin_rate,
ROUND(AVG(distance_meters), 2) as avg_distance,
MAX(distance_meters) as longest_goal_distance
FROM shot_events
WHERE shot_type = 'knuckleball'
GROUP BY player_id
ORDER BY success_rate DESC";
$stmt = $this->db->prepare($query);
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
/**
* 分析不同环境下的成功率
*/
public function analyzeEnvironmentFactors() {
$query = "SELECT
weather_condition,
field_condition,
COUNT(*) as total_shots,
SUM(is_goal) as goals,
ROUND((SUM(is_goal) / COUNT(*)) * 100, 2) as success_rate
FROM shot_events
WHERE shot_type = 'knuckleball'
GROUP BY weather_condition, field_condition";
$stmt = $this->db->prepare($query);
$stmt->execute();
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
/**
* 球员能力趋势分析
*/
public function getPerformanceTrend($playerId, $months = 6) {
$query = "SELECT
DATE_FORMAT(shot_time, '%Y-%m') as month,
COUNT(*) as monthly_shots,
SUM(is_goal) as monthly_goals,
ROUND((SUM(is_goal) / COUNT(*)) * 100, 2) as monthly_success_rate
FROM shot_events
WHERE player_id = :player_id
AND shot_type = 'knuckleball'
AND shot_time >= DATE_SUB(NOW(), INTERVAL $months MONTH)
GROUP BY DATE_FORMAT(shot_time, '%Y-%m')
ORDER BY month";
$stmt = $this->db->prepare($query);
$stmt->execute([':player_id' => $playerId]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
/**
* 优化建议分析
*/
public function getOptimizationSuggestions($playerId) {
$suggestions = [];
// 分析最佳射门距离
$distanceAnalysis = $this->analyzeDistancePerformance();
// 分析最佳脚使用
$footAnalysis = $this->analyzeFootPerformance($playerId);
// 分析对手强度影响
$opponentAnalysis = $this->analyzeOpponentStrength($playerId);
return [
'distance_recommendation' => $distanceAnalysis['recommended_distance'],
'foot_recommendation' => $footAnalysis['preferred_foot'],
'opponent_strategy' => $opponentAnalysis,
'suggestions' => $suggestions
];
}
private function analyzeDistancePerformance() {
$query = "SELECT
CASE
WHEN distance_meters < 20 THEN 'close'
WHEN distance_meters BETWEEN 20 AND 30 THEN 'medium'
WHEN distance_meters BETWEEN 30 AND 40 THEN 'long'
ELSE 'very_long'
END as distance_category,
COUNT(*) as shots,
SUM(is_goal) as goals,
ROUND((SUM(is_goal) / COUNT(*)) * 100, 2) as success_rate
FROM shot_events
WHERE shot_type = 'knuckleball'
GROUP BY distance_category
ORDER BY success_rate DESC";
$stmt = $this->db->prepare($query);
$stmt->execute();
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
return [
'recommended_distance' => $results[0]['distance_category'] ?? 'unknown',
'details' => $results
];
}
}
API接口实现
<?php
// api/record_shot.php
require_once '../includes/Database.php';
require_once '../includes/ShootingStats.php';
header('Content-Type: application/json');
// 验证请求方法
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode(['error' => 'Method Not Allowed']);
exit;
}
// 获取请求数据
$input = json_decode(file_get_contents('php://input'), true);
// 验证数据
$requiredFields = ['player_id', 'shot_time', 'shot_accuracy'];
foreach ($requiredFields as $field) {
if (!isset($input[$field])) {
http_response_code(400);
echo json_encode(['error' => "Missing required field: $field"]);
exit;
}
}
// 设置默认值
$input['shot_type'] = $input['shot_type'] ?? 'knuckleball';
$input['is_goal'] = ($input['shot_accuracy'] === 'goal') ? 1 : 0;
// 初始化类
$db = Database::getConnection();
$stats = new ShootingStats($db);
try {
$result = $stats->recordShot($input);
if ($result) {
echo json_encode([
'success' => true,
'message' => 'Shot recorded successfully'
]);
} else {
http_response_code(500);
echo json_encode(['error' => 'Failed to record shot']);
}
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['error' => $e->getMessage()]);
}
管理员统计页面
<?php
// admin/stats_view.php
require_once '../includes/Database.php';
require_once '../includes/ShootingStats.php';
$db = Database::getConnection();
$stats = new ShootingStats($db);
// 获取总体统计
$overallStats = $stats->calculateKnuckleballSuccessRate();
$playerStats = $stats->getKnuckleballAnalysis();
$environmentFactors = $stats->analyzeEnvironmentFactors();
// 计算综合评分
$overallRate = $overallStats['success_rate'] ?? 0;
$totalShots = $overallStats['total_shots'] ?? 0;
$goalRate = $overallStats['goals'] ?? 0;
// 评分标准示例
$score = 0;
$score += $overallRate * 0.4; // 成功率权重
$score += min(($totalShots / 50) * 20, 20); // 样本量评分
$score += ($goalRate / 20) * 20; // 进球数评分
$score += min($overallStats['on_target'] / 100 * 20, 20); // 命中率
// 显示页面
?>
<!DOCTYPE html>
<html>
<head>落叶球射门统计</title>
<style>
.stats-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
padding: 20px;
}
.stat-card {
background: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.progress-bar {
background: #e0e0e0;
border-radius: 4px;
height: 20px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #4CAF50, #8BC34A);
transition: width 0.3s ease;
}
table {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #4CAF50;
color: white;
}
tr:nth-child(even) {
background-color: #f2f2f2;
}
.score-meter {
text-align: center;
font-size: 2em;
font-weight: bold;
}
</style>
</head>
<body>
<div class="stats-container">
<!-- 综合评分卡片 -->
<div class="stat-card">
<h3>球员综合能力评分</h3>
<div class="score-meter">
<?php echo number_format($score, 1); ?> / 100
</div>
<div class="progress-bar">
<div class="progress-fill" style="width: <?php echo min($score, 100); ?>%"></div>
</div>
<p>
<strong>整体成功率:</strong> <?php echo number_format($overallRate, 2); ?>%<br>
<strong>总射门次数:</strong> <?php echo $totalShots; ?><br>
<strong>总进球数:</strong> <?php echo $goalRate; ?>
</p>
</div>
<!-- 球员排行榜 -->
<div class="stat-card">
<h3>球员落叶球成功率排行榜</h3>
<table>
<thead>
<tr>
<th>球员</th>
<th>尝试次数</th>
<th>成功率</th>
<th>平均速度(km/h)</th>
</tr>
</thead>
<tbody>
<?php foreach ($playerStats as $player): ?>
<tr>
<td><?php echo htmlspecialchars($player['player_id']); ?></td>
<td><?php echo $player['total_attempts']; ?></td>
<td><?php echo number_format($player['success_rate'], 2); ?>%</td>
<td><?php echo $player['avg_speed']; ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- 环境因素分析 -->
<div class="stat-card">
<h3>环境因素对成功率的影响</h3>
<table>
<thead>
<tr>
<th>天气</th>
<th>场地</th>
<th>射门次数</th>
<th>成功率</th>
</tr>
</thead>
<tbody>
<?php foreach ($environmentFactors as $factor): ?>
<tr>
<td><?php echo $factor['weather_condition']; ?></td>
<td><?php echo $factor['field_condition']; ?></td>
<td><?php echo $factor['total_shots']; ?></td>
<td><?php echo number_format($factor['success_rate'], 2); ?>%</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<!-- 趋势图表(简化版) -->
<div class="stat-card">
<h3>近期表现趋势</h3>
<?php
// 获取示例球员数据(假设球员ID为1)
$trends = $stats->getPerformanceTrend(1);
?>
<canvas id="trendChart"></canvas>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
const ctx = document.getElementById('trendChart');
const months = <?php echo json_encode(array_column($trends, 'month')); ?>;
const successRates = <?php echo json_encode(array_column($trends, 'monthly_success_rate')); ?>;
new Chart(ctx, {
type: 'line',
data: {
labels: months,
datasets: [{
label: '月度成功率 (%)',
data: successRates,
borderColor: 'rgb(75, 192, 192)',
tension: 0.1
}]
},
options: {
responsive: true,
maintainAspectRatio: false
}
});
</script>
</div>
<!-- 优化建议 -->
<div class="stat-card">
<h3>技术优化建议</h3>
<?php
$suggestions = $stats->getOptimizationSuggestions(1);
?>
<h4>最佳射门距离</h4>
<p><?php echo htmlspecialchars($suggestions['distance_recommendation']); ?></p>
<h4>脚部偏好</h4>
<p><?php echo htmlspecialchars($suggestions['foot_recommendation']); ?></p>
<h4>对手策略分析</h4>
<pre><?php echo json_encode($suggestions['opponent_strategy'], JSON_PRETTY_PRINT); ?></pre>
</div>
</div>
<script>
// 添加实时数据刷新功能
function refreshStats() {
fetch('../api/get_stats.php')
.then(response => response.json())
.then(data => {
// 更新页面数据
console.log('Stats updated:', data);
// 这里可以添加更新DOM的代码
});
}
// 每30秒自动刷新一次
setInterval(refreshStats, 30000);
</script>
</body>
</html>
使用说明
数据录入示例
// 记录一次成功的落叶球射门
$shotData = [
'player_id' => 1,
'match_id' => 'MATCH-2023-001',
'shot_time' => '2023-09-15 89:30:00',
'shot_type' => 'knuckleball',
'distance_meters' => 25.5,
'shot_angle' => 30.0,
'ball_velocity_kmh' => 98,
'shot_accuracy' => 'goal',
'is_goal' => 1,
'weather_condition' => 'sunny',
'field_condition' => 'dry',
'opponent_strength' => 'strong',
'foot_used' => 'right',
'spin_rate_rpm' => 0, // 落叶球低旋转
'trajectory_swing' => 'high_swing',
'notes' => '完美落叶球,守门员脱手'
];
核心功能特性
- 多维度统计:距离、角度、速度、旋转率等
- 环境因素:天气、场地、对手强度
- 趋势分析:月度/赛季变化趋势
- 球员对比:多球员横向比较
- 智能建议:基于数据分析的优化建议
- 实时监控:通过API实时更新数据
部署建议
- 数据库优化:为高频查询字段建立索引
- 缓存机制:使用Redis缓存热点数据
- 数据可视化:可集成Highcharts或ECharts
- 移动端支持:开发移动端API
- 权限控制:管理员/教练/球员分级权限
这个系统可以根据实际需求进行调整和扩展,例如添加视频分析集成、实时球探报告等功能。