#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 世界杯预测系统 - 后端API 端口: 16015 """ from flask import Flask, jsonify, request, send_from_directory from flask_cors import CORS import sqlite3 import json from datetime import datetime, timedelta import os app = Flask(__name__, static_folder='../frontend') CORS(app) DB_PATH = os.path.join(os.path.dirname(__file__), 'worldcup.db') def get_db(): """获取数据库连接""" conn = sqlite3.connect(DB_PATH) conn.row_factory = sqlite3.Row return conn def init_db(): """初始化数据库""" conn = get_db() cursor = conn.cursor() # 预测算法表 cursor.execute(''' CREATE TABLE IF NOT EXISTS algorithms ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE, description TEXT, accuracy REAL DEFAULT 0, total_predictions INTEGER DEFAULT 0, correct_predictions INTEGER DEFAULT 0, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, is_active INTEGER DEFAULT 1 ) ''') # 比赛表 cursor.execute(''' CREATE TABLE IF NOT EXISTS matches ( id INTEGER PRIMARY KEY AUTOINCREMENT, match_code TEXT NOT NULL UNIQUE, home_team TEXT NOT NULL, away_team TEXT NOT NULL, match_time TIMESTAMP, home_score INTEGER, away_score INTEGER, status TEXT DEFAULT 'upcoming', round_name TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ''') # 预测结果表 cursor.execute(''' CREATE TABLE IF NOT EXISTS predictions ( id INTEGER PRIMARY KEY AUTOINCREMENT, match_id INTEGER NOT NULL, algorithm_id INTEGER NOT NULL, predicted_home_score INTEGER, predicted_away_score INTEGER, predicted_winner TEXT, confidence REAL, predicted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, is_correct INTEGER, FOREIGN KEY (match_id) REFERENCES matches(id), FOREIGN KEY (algorithm_id) REFERENCES algorithms(id), UNIQUE(match_id, algorithm_id) ) ''') # 系统配置表 cursor.execute(''' CREATE TABLE IF NOT EXISTS system_config ( id INTEGER PRIMARY KEY AUTOINCREMENT, config_key TEXT NOT NULL UNIQUE, config_value TEXT, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ''') # 插入默认配置 default_configs = [ ('prediction_cycle_hours', '24'), ('last_prediction_time', datetime.now().isoformat()), ('world_cup_year', '2026') ] for key, value in default_configs: cursor.execute(''' INSERT OR IGNORE INTO system_config (config_key, config_value) VALUES (?, ?) ''', (key, value)) conn.commit() conn.close() def calculate_algorithm_stats(algorithm_id): """计算算法统计数据""" conn = get_db() cursor = conn.cursor() cursor.execute(''' SELECT COUNT(*) as total, SUM(CASE WHEN is_correct = 1 THEN 1 ELSE 0 END) as correct FROM predictions WHERE algorithm_id = ? AND is_correct IS NOT NULL ''', (algorithm_id,)) result = cursor.fetchone() total = result['total'] or 0 correct = result['correct'] or 0 accuracy = (correct / total * 100) if total > 0 else 0 cursor.execute(''' UPDATE algorithms SET total_predictions = ?, correct_predictions = ?, accuracy = ? WHERE id = ? ''', (total, correct, accuracy, algorithm_id)) conn.commit() conn.close() # ==================== 前端页面路由 ==================== @app.route('/') def index(): """主页""" return send_from_directory(app.static_folder, 'index.html') @app.route('/admin') def admin(): """管理后台""" return send_from_directory(app.static_folder, 'admin.html') @app.route('/css/') def css(filename): return send_from_directory(os.path.join(app.static_folder, 'css'), filename) @app.route('/js/') def js(filename): return send_from_directory(os.path.join(app.static_folder, 'js'), filename) # ==================== API路由 ==================== @app.route('/api/matches', methods=['GET']) def get_matches(): """获取所有比赛""" status = request.args.get('status', 'all') conn = get_db() cursor = conn.cursor() if status == 'upcoming': cursor.execute(''' SELECT * FROM matches WHERE status = 'upcoming' OR status IS NULL ORDER BY match_time ASC ''') elif status == 'finished': cursor.execute(''' SELECT * FROM matches WHERE status = 'finished' ORDER BY match_time DESC ''') else: cursor.execute('SELECT * FROM matches ORDER BY match_time DESC') matches = [dict(row) for row in cursor.fetchall()] conn.close() return jsonify({'success': True, 'data': matches}) @app.route('/api/matches/', methods=['GET']) def get_match(match_id): """获取单场比赛详情""" conn = get_db() cursor = conn.cursor() cursor.execute('SELECT * FROM matches WHERE id = ?', (match_id,)) match = cursor.fetchone() if not match: conn.close() return jsonify({'success': False, 'message': '比赛不存在'}), 404 # 获取该比赛的所有预测 cursor.execute(''' SELECT p.*, a.name as algorithm_name, a.accuracy as algorithm_accuracy FROM predictions p JOIN algorithms a ON p.algorithm_id = a.id WHERE p.match_id = ? ''', (match_id,)) predictions = [dict(row) for row in cursor.fetchall()] conn.close() return jsonify({ 'success': True, 'data': { 'match': dict(match), 'predictions': predictions } }) @app.route('/api/matches', methods=['POST']) def add_match(): """添加比赛""" data = request.json conn = get_db() cursor = conn.cursor() try: cursor.execute(''' INSERT INTO matches (match_code, home_team, away_team, match_time, round_name, status) VALUES (?, ?, ?, ?, ?, ?) ''', ( data['match_code'], data['home_team'], data['away_team'], data.get('match_time'), data.get('round_name', '小组赛'), data.get('status', 'upcoming') )) match_id = cursor.lastrowid conn.commit() conn.close() return jsonify({'success': True, 'data': {'id': match_id}}) except sqlite3.IntegrityError: conn.close() return jsonify({'success': False, 'message': '比赛代码已存在'}), 400 @app.route('/api/matches/', methods=['PUT']) def update_match(match_id): """更新比赛""" data = request.json conn = get_db() cursor = conn.cursor() cursor.execute('SELECT id FROM matches WHERE id = ?', (match_id,)) if not cursor.fetchone(): conn.close() return jsonify({'success': False, 'message': '比赛不存在'}), 404 update_fields = [] values = [] for field in ['home_team', 'away_team', 'match_time', 'home_score', 'away_score', 'status', 'round_name']: if field in data: update_fields.append(f'{field} = ?') values.append(data[field]) if update_fields: values.append(match_id) cursor.execute(f''' UPDATE matches SET {', '.join(update_fields)} WHERE id = ? ''', values) conn.commit() conn.close() return jsonify({'success': True}) @app.route('/api/matches/', methods=['DELETE']) def delete_match(match_id): """删除比赛""" conn = get_db() cursor = conn.cursor() cursor.execute('DELETE FROM predictions WHERE match_id = ?', (match_id,)) cursor.execute('DELETE FROM matches WHERE id = ?', (match_id,)) conn.commit() conn.close() return jsonify({'success': True}) @app.route('/api/algorithms', methods=['GET']) def get_algorithms(): """获取所有算法""" conn = get_db() cursor = conn.cursor() cursor.execute('SELECT * FROM algorithms ORDER BY accuracy DESC') algorithms = [dict(row) for row in cursor.fetchall()] conn.close() return jsonify({'success': True, 'data': algorithms}) @app.route('/api/algorithms/', methods=['GET']) def get_algorithm(algorithm_id): """获取单个算法详情""" conn = get_db() cursor = conn.cursor() cursor.execute('SELECT * FROM algorithms WHERE id = ?', (algorithm_id,)) algorithm = cursor.fetchone() if not algorithm: conn.close() return jsonify({'success': False, 'message': '算法不存在'}), 404 # 获取该算法的预测历史 cursor.execute(''' SELECT p.*, m.home_team, m.away_team, m.home_score, m.away_score, m.match_time FROM predictions p JOIN matches m ON p.match_id = m.id WHERE p.algorithm_id = ? ORDER BY m.match_time DESC LIMIT 50 ''', (algorithm_id,)) history = [dict(row) for row in cursor.fetchall()] conn.close() return jsonify({ 'success': True, 'data': { 'algorithm': dict(algorithm), 'history': history } }) @app.route('/api/algorithms', methods=['POST']) def add_algorithm(): """添加算法""" data = request.json conn = get_db() cursor = conn.cursor() try: cursor.execute(''' INSERT INTO algorithms (name, description) VALUES (?, ?) ''', (data['name'], data.get('description', ''))) algorithm_id = cursor.lastrowid conn.commit() conn.close() return jsonify({'success': True, 'data': {'id': algorithm_id}}) except sqlite3.IntegrityError: conn.close() return jsonify({'success': False, 'message': '算法名称已存在'}), 400 @app.route('/api/algorithms/', methods=['PUT']) def update_algorithm(algorithm_id): """更新算法""" data = request.json conn = get_db() cursor = conn.cursor() cursor.execute('SELECT id FROM algorithms WHERE id = ?', (algorithm_id,)) if not cursor.fetchone(): conn.close() return jsonify({'success': False, 'message': '算法不存在'}), 404 update_fields = [] values = [] for field in ['name', 'description', 'is_active']: if field in data: update_fields.append(f'{field} = ?') values.append(data[field]) if update_fields: values.append(algorithm_id) cursor.execute(f''' UPDATE algorithms SET {', '.join(update_fields)} WHERE id = ? ''', values) conn.commit() conn.close() return jsonify({'success': True}) @app.route('/api/algorithms/', methods=['DELETE']) def delete_algorithm(algorithm_id): """删除算法""" conn = get_db() cursor = conn.cursor() cursor.execute('DELETE FROM predictions WHERE algorithm_id = ?', (algorithm_id,)) cursor.execute('DELETE FROM algorithms WHERE id = ?', (algorithm_id,)) conn.commit() conn.close() return jsonify({'success': True}) @app.route('/api/predictions', methods=['POST']) def add_prediction(): """添加预测""" data = request.json conn = get_db() cursor = conn.cursor() try: cursor.execute(''' INSERT OR REPLACE INTO predictions (match_id, algorithm_id, predicted_home_score, predicted_away_score, predicted_winner, confidence) VALUES (?, ?, ?, ?, ?, ?) ''', ( data['match_id'], data['algorithm_id'], data.get('predicted_home_score'), data.get('predicted_away_score'), data.get('predicted_winner'), data.get('confidence', 0.5) )) conn.commit() conn.close() return jsonify({'success': True}) except Exception as e: conn.close() return jsonify({'success': False, 'message': str(e)}), 400 @app.route('/api/predictions/evaluate', methods=['POST']) def evaluate_predictions(): """评估预测结果""" conn = get_db() cursor = conn.cursor() # 获取所有已完成的比赛 cursor.execute(''' SELECT m.*, p.id as prediction_id, p.predicted_home_score, p.predicted_away_score, p.algorithm_id, p.predicted_winner FROM matches m JOIN predictions p ON m.id = p.match_id WHERE m.status = 'finished' AND m.home_score IS NOT NULL AND m.away_score IS NOT NULL AND p.is_correct IS NULL ''') predictions = cursor.fetchall() for pred in predictions: # 判断预测是否正确 is_correct = 0 if pred['predicted_home_score'] and pred['predicted_away_score']: # 比分预测 if pred['predicted_home_score'] == pred['home_score'] and pred['predicted_away_score'] == pred['away_score']: is_correct = 1 elif pred['predicted_winner']: # 胜负预测 actual_winner = 'draw' if pred['home_score'] == pred['away_score'] else ( pred['home_team'] if pred['home_score'] > pred['away_score'] else pred['away_team'] ) if pred['predicted_winner'] == actual_winner: is_correct = 1 cursor.execute(''' UPDATE predictions SET is_correct = ? WHERE id = ? ''', (is_correct, pred['prediction_id'])) # 更新算法统计 calculate_algorithm_stats(pred['algorithm_id']) conn.commit() conn.close() return jsonify({'success': True, 'evaluated': len(predictions)}) @app.route('/api/config', methods=['GET']) def get_config(): """获取系统配置""" conn = get_db() cursor = conn.cursor() cursor.execute('SELECT config_key, config_value FROM system_config') configs = {row['config_key']: row['config_value'] for row in cursor.fetchall()} conn.close() return jsonify({'success': True, 'data': configs}) @app.route('/api/config', methods=['POST']) def update_config(): """更新系统配置""" data = request.json conn = get_db() cursor = conn.cursor() for key, value in data.items(): cursor.execute(''' INSERT OR REPLACE INTO system_config (config_key, config_value, updated_at) VALUES (?, ?, ?) ''', (key, str(value), datetime.now().isoformat())) conn.commit() conn.close() return jsonify({'success': True}) @app.route('/api/stats/overview', methods=['GET']) def get_overview_stats(): """获取总体统计""" conn = get_db() cursor = conn.cursor() # 比赛统计 cursor.execute('SELECT COUNT(*) as total FROM matches') total_matches = cursor.fetchone()['total'] cursor.execute('SELECT COUNT(*) as total FROM matches WHERE status = "finished"') finished_matches = cursor.fetchone()['total'] # 算法统计 cursor.execute('SELECT COUNT(*) as total FROM algorithms WHERE is_active = 1') active_algorithms = cursor.fetchone()['total'] # 预测统计 cursor.execute('SELECT COUNT(*) as total FROM predictions') total_predictions = cursor.fetchone()['total'] cursor.execute('SELECT COUNT(*) as total FROM predictions WHERE is_correct = 1') correct_predictions = cursor.fetchone()['total'] overall_accuracy = (correct_predictions / total_predictions * 100) if total_predictions > 0 else 0 conn.close() return jsonify({ 'success': True, 'data': { 'total_matches': total_matches, 'finished_matches': finished_matches, 'upcoming_matches': total_matches - finished_matches, 'active_algorithms': active_algorithms, 'total_predictions': total_predictions, 'correct_predictions': correct_predictions, 'overall_accuracy': round(overall_accuracy, 2) } }) if __name__ == '__main__': init_db() app.run(host='0.0.0.0', port=16015, debug=False)