"""学生课程管理系统 - Flask主应用入口""" import os from flask import Flask from config import Config, write_engine from models import db def create_app(): """应用工厂函数""" app = Flask(__name__) app.config.from_object(Config) app.config['TEMPLATES_AUTO_RELOAD'] = True # 初始化数据库 db.init_app(app) # 仅当存在有效的 write_engine(非 None)时才复用 if write_engine is not None: if app in db._app_engines: old_engine = db._app_engines[app].get(None) if old_engine is not None and old_engine is not write_engine: old_engine.dispose() db._app_engines[app][None] = write_engine # 注入模板上下文变量 @app.context_processor def inject_globals(): from flask import session from datetime import date from models import User, ConsumptionRecord, Student from sqlalchemy import func user_id = session.get('user_id') user = db.session.get(User, user_id) if user_id else None panel_progress = 0 if user: month_start = date.today().replace(day=1) month_h = db.session.query( func.coalesce(func.sum(ConsumptionRecord.hours_consumed), 0) ).filter(ConsumptionRecord.consume_date >= month_start).scalar() or 0 students = Student.query.filter_by(status=1).count() or 1 target = max(students * 4, 20) panel_progress = min(100, int(float(month_h) / target * 100)) return {'current_user': user, 'panel_progress': panel_progress} # 注册路由(直接注册到app,不使用蓝图) with app.app_context(): from routes import register_all_routes register_all_routes(app) # 建表 + 自动迁移(Doris / MySQL / SQLite) from db_bootstrap import bootstrap_database bootstrap_database() _init_default_data() _sync_account_original_prices() return app def _sync_account_original_prices(): """未录原价时:课程表单价 → 最新月度快照单价""" from models import db, StudentAccount, Course, MonthlySnapshot from sqlalchemy import func try: accounts = StudentAccount.query.filter( db.or_( StudentAccount.original_price_per_lesson.is_(None), StudentAccount.original_price_per_lesson == 0, ) ).all() n = 0 for acc in accounts: if acc.original_price_per_lesson and float(acc.original_price_per_lesson or 0) > 0: continue crs = db.session.get(Course, acc.course_id) if crs and float(crs.price_per_hour or 0) > 0: acc.original_price_per_lesson = crs.price_per_hour n += 1 continue row = ( db.session.query(MonthlySnapshot.unit_price) .filter( MonthlySnapshot.account_id == acc.id, MonthlySnapshot.unit_price > 0, ) .order_by( MonthlySnapshot.year.desc(), MonthlySnapshot.month.desc(), ) .first() ) if row and float(row[0] or 0) > 0: acc.original_price_per_lesson = row[0] n += 1 if n: db.session.commit() except Exception: db.session.rollback() def _init_default_data(): """初始化默认数据: 确保admin用户和管理员角色存在""" from models import User, Role from utils import PERMISSIONS # §八 默认角色层级 default_roles = [ ('超级管理员', 'all', '全部权限'), ('校区管理员', ','.join(PERMISSIONS.keys()), '本校区全部业务'), ( '财务', 'recharge_view,recharge_add,recharge_export,recharge_activity_view,' 'recharge_activity_add,recharge_activity_edit,refund_view,refund_add,' 'refund_export,statistics_view,statistics_export,log_view', '充值退费与报表', ), ( '授课老师', 'student_view,course_view,class_view,class_add,attendance_view,' 'attendance_add,consumption_view,consumption_add,schedule_view', '消课考勤与班级', ), ] for name, perms, remark in default_roles: if not Role.query.filter_by(name=name).first(): db.session.add(Role(name=name, permissions=perms, remark=remark)) db.session.commit() # 校区管理员:补齐后续新增的权限项(如课时核对表) campus = Role.query.filter_by(name='校区管理员').first() if campus and campus.permissions != 'all': all_keys = sorted(PERMISSIONS.keys()) campus.permissions = ','.join(all_keys) db.session.commit() admin_role = Role.query.filter_by(name='超级管理员').first() if not admin_role: admin_role = Role(name='超级管理员', permissions='all', remark='系统默认管理员角色') db.session.add(admin_role) db.session.commit() admin_role = Role.query.filter_by(name='超级管理员').first() # 确保admin用户存在且密码正确 admin = User.query.filter_by(username='admin').first() default_password = 'admin123' if not admin: admin = User( username='admin', real_name='系统管理员', role_id=admin_role.id, status=1, remark='默认管理员' ) admin.set_password(default_password) db.session.add(admin) db.session.commit() elif not admin.check_password(default_password): admin.set_password(default_password) db.session.commit() app = create_app() if __name__ == '__main__': port = int(os.environ.get('DEPLOY_RUN_PORT', 5000)) app.run(host='0.0.0.0', port=port, debug=True, use_reloader=True)