Files
ylt_diy/app.py
2026-07-30 10:59:44 +08:00

198 lines
6.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Flask 主应用
订单日报系统
"""
import os
from datetime import datetime
from flask import Flask, render_template, Response, request, jsonify, g
from flask_cors import CORS
from apscheduler.schedulers.background import BackgroundScheduler
import pytz
from lib.db import execute_query
from lib.report_generator import generate_daily_report
from lib.api import register_blueprints
from lib.db_init import init_database_tables, optimize_sum_data_indexes
from lib.logger import log_info, log_error, log_warning
from lib.api.download_api import cleanup_old_temp_zips
from lib.api.auth_api import get_current_user
# 创建 Flask 应用
app = Flask(__name__,
template_folder='templates',
static_folder='public')
CORS(app)
# 确保 public/reports 目录存在
os.makedirs(os.path.join('public', 'reports'), exist_ok=True)
# 注册 API 蓝图
register_blueprints(app)
# 应用启动时自动初始化数据库表
log_info('正在初始化数据库表...', 'app')
if init_database_tables():
log_info('数据库表初始化成功', 'app')
# 优化求和数据表索引
log_info('正在优化数据库索引...', 'app')
if optimize_sum_data_indexes():
log_info('数据库索引优化成功', 'app')
else:
log_warning('数据库索引优化失败,但不影响正常使用', 'app')
else:
log_error('数据库表初始化失败,请检查数据库连接', 'app')
# ==================== 登录验证中间件 ====================
# 不需要登录的路径白名单
WHITELIST_PATHS = [
'/api/auth/login',
'/api/auth/info',
'/public/',
'/static/',
'/favicon.ico',
'/'
]
@app.before_request
def check_login():
"""全局登录验证"""
path = request.path
# 白名单路径直接放行
for wp in WHITELIST_PATHS:
if path.startswith(wp):
return None
# 页面请求HTML直接放行由前端控制登录
if path == '/' or path.endswith('.html'):
return None
# 静态文件直接放行
if path.startswith('/public/') or path.startswith('/static/'):
return None
# API 请求需要验证登录
if path.startswith('/api/'):
# 先从 header 取 token再从 query 参数取(用于下载链接)
token = request.headers.get('Authorization', '')
if token.startswith('Bearer '):
token = token[7:]
if not token:
token = request.args.get('token', '')
user = None
if token:
from lib.api.auth_api import get_user_by_token
user = get_user_by_token(token)
if not user:
return jsonify({
'success': False,
'message': '未登录或登录已过期',
'code': 401
}), 401
g.current_user = user
return None
# ==================== 页面路由 ====================
@app.route('/')
def index():
"""主页"""
return render_template('index.html')
@app.route('/favicon.ico')
def favicon():
"""网站图标返回空响应避免404"""
return Response(status=204)
# ==================== 定时任务 ====================
def scheduled_job():
"""定时任务:每天 8:01 自动生成日报"""
log_info(f'开始执行定时任务...', 'scheduler')
try:
# 获取所有启用的配置
configs = execute_query(
'SELECT id, config_name FROM t_daily_report_config WHERE is_active = 1'
)
if not configs:
log_warning('没有启用的配置,跳过生成', 'scheduler')
return
log_info(f'找到 {len(configs)} 个启用的配置', 'scheduler')
# 为每个配置生成日报
success_count = 0
fail_count = 0
for config in configs:
log_info(f'正在生成:{config["config_name"]} (ID: {config["id"]})', 'scheduler')
result = generate_daily_report(config['id'])
if result['success']:
log_info(f' ✓ 成功:{result["total_orders"]} 条订单,总电量:{result.get("total_degree", 0)} kWh', 'scheduler')
success_count += 1
else:
log_error(f' ✗ 失败:{result["message"]}', 'scheduler')
fail_count += 1
log_info(f'定时任务执行完成,成功 {success_count} 个,失败 {fail_count}', 'scheduler')
except Exception as e:
log_error(f'定时任务执行失败:{e}', 'scheduler')
# ==================== 启动定时任务 ====================
scheduler = BackgroundScheduler(timezone='Asia/Shanghai')
scheduler.add_job(scheduled_job, 'cron', hour=8, minute=30)
scheduler.add_job(cleanup_old_temp_zips, 'interval', hours=1)
# 添加 Doris 表统计信息定时优化任务每天凌晨3点执行
def optimize_doris_tables():
"""定期优化 Doris 表统计信息"""
try:
log_info('开始执行 Doris 表统计信息优化...', 'scheduler')
from lib.doris_optimize import optimize_doris_table
tables = [
't_daily_report_sum_data',
't_daily_report_config',
't_daily_report_history',
]
for table in tables:
try:
optimize_doris_table(table)
log_info(f'{table} 统计信息优化完成', 'scheduler')
except Exception as e:
log_warning(f'{table} 统计信息优化失败: {e}', 'scheduler')
log_info('Doris 表统计信息优化完成', 'scheduler')
except Exception as e:
log_error(f'Doris 表统计信息优化失败: {e}', 'scheduler')
scheduler.add_job(optimize_doris_tables, 'cron', hour=3, minute=0)
scheduler.start()
log_info('定时任务已启动:每天 08:30 执行日报生成,每小时清理临时文件,每天 03:00 优化 Doris 统计信息', 'app')
# ==================== 启动应用 ====================
if __name__ == '__main__':
# 支持沙箱环境和本地环境
port = int(os.environ.get('DEPLOY_RUN_PORT', os.environ.get('PORT', 5000)))
log_info(f'启动服务器http://localhost:{port}', 'app')
log_info(f'数据库:{os.environ.get("DB_HOST", "haoslm2.xicp.net")}:{os.environ.get("DB_PORT", "10216")}', 'app')
app.run(host='0.0.0.0', port=port, debug=True)