Files
drl_2/xuexiao/generate_snapshots.py
user9994793890 fa29412794 fix: 生成月度快照数据修复余额显示
Coze-Commit-Type: user
Coze-User-ID: 3722323274763196
Coze-Conversation-ID: 5260473
2026-05-29 11:03:35 +08:00

213 lines
8.9 KiB
Python
Raw Permalink 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.

#!/usr/bin/env python3
"""
生成月度课时快照MonthlySnapshot
基于现有数据,为每个账户生成当前月份的快照
"""
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
os.environ['DB_TYPE'] = 'sqlite'
from flask import Flask
from config import Config
from models import db, StudentAccount, RechargeRecord, ConsumptionRecord, MonthlySnapshot
from datetime import datetime
def generate_snapshots():
"""为每个学员账户生成月度快照"""
app = Flask(__name__)
app.config.from_object(Config)
db.init_app(app)
with app.app_context():
# 清空现有快照
MonthlySnapshot.query.delete()
accounts = StudentAccount.query.all()
print(f"开始为 {len(accounts)} 个账户生成快照...")
# 获取所有充值记录并按账户分组
recharges_by_account = {}
all_recharges = RechargeRecord.query.all()
for r in all_recharges:
key = (r.student_id, r.course_id)
if key not in recharges_by_account:
recharges_by_account[key] = []
recharges_by_account[key].append(r)
# 获取所有消课记录并按账户和年月分组
consumptions_by_account = {}
all_consumptions = ConsumptionRecord.query.all()
for c in all_consumptions:
key = (c.student_id, c.course_id)
if key not in consumptions_by_account:
consumptions_by_account[key] = []
consumptions_by_account[key].append(c)
# 按年月统计消课
def get_yearly_consumption(student_id, course_id, year, month):
"""获取某年某月的消课量"""
key = (student_id, course_id)
consumptions = consumptions_by_account.get(key, [])
total = 0.0
for c in consumptions:
if c.consume_date and c.consume_date.year == year and c.consume_date.month == month:
total += float(c.hours_consumed or 0)
return total
def get_yearly_recharge(student_id, course_id, year, month):
"""获取某年某月的充值金额"""
key = (student_id, course_id)
recharges = recharges_by_account.get(key, [])
total_amount = 0.0
total_lessons = 0.0
total_gift = 0.0
for r in recharges:
if r.created_at and r.created_at.year == year and r.created_at.month == month:
total_amount += float(r.amount or 0)
total_lessons += float(r.normal_hours or 0)
total_gift += float(r.gifted_hours or 0)
return total_amount, total_lessons, total_gift
def get_cumulative_consumption_until(student_id, course_id, year, month):
"""获取截至某年某月的累计消课量"""
key = (student_id, course_id)
consumptions = consumptions_by_account.get(key, [])
total = 0.0
for c in consumptions:
if c.consume_date:
# 计算到指定月份为止
if (c.consume_date.year < year) or \
(c.consume_date.year == year and c.consume_date.month <= month):
total += float(c.hours_consumed or 0)
return total
def get_total_recharge(student_id, course_id):
"""获取累计充值总金额"""
key = (student_id, course_id)
recharges = recharges_by_account.get(key, [])
return sum(float(r.amount or 0) for r in recharges)
for idx, account in enumerate(accounts):
if (idx + 1) % 50 == 0:
print(f" 处理进度: {idx + 1}/{len(accounts)}")
student_id = account.student_id
course_id = account.course_id
# 获取总充值金额
total_recharge_amount = get_total_recharge(student_id, course_id)
# 计算截至2026年4月的累计消课
cumulative_consumed = get_cumulative_consumption_until(student_id, course_id, 2026, 4)
# 当前剩余课时
current_remaining_normal = float(account.normal_hours or 0)
current_remaining_gift = float(account.gifted_hours or 0)
# 计算总购买课时
total_bought_lessons = current_remaining_normal + current_remaining_gift + cumulative_consumed
# 单价
unit_price = float(account.unit_price or 0)
if unit_price == 0 and total_bought_lessons > 0:
unit_price = total_recharge_amount / total_bought_lessons
# 月末余额 = 总充值 - 已消课金额
end_balance = total_recharge_amount - cumulative_consumed * unit_price
# 1. 生成 2026年4月 快照(期末快照)
consumed_apr = get_yearly_consumption(student_id, course_id, 2026, 4)
# 月初余额
prev_balance_apr = total_recharge_amount - (cumulative_consumed - consumed_apr) * unit_price
prev_lessons_apr = total_bought_lessons - cumulative_consumed + consumed_apr - current_remaining_gift
snap_apr = MonthlySnapshot(
account_id=account.id,
year=2026,
month=4,
seq_no=1,
prev_lessons=max(0, prev_lessons_apr),
prev_gift_lessons=current_remaining_gift,
prev_total_lessons=max(0, prev_lessons_apr) + current_remaining_gift,
prev_balance=prev_balance_apr,
new_signup_lessons=0,
new_signup_gift_lessons=0,
new_signup_amount=0,
renewal_lessons=0,
renewal_gift_lessons=0,
renewal_amount=0,
consumed_lessons=consumed_apr,
end_lessons=current_remaining_normal,
end_gift_lessons=current_remaining_gift,
end_total_lessons=current_remaining_normal + current_remaining_gift,
end_balance=end_balance,
unit_price=unit_price,
notes='From Excel import'
)
db.session.add(snap_apr)
# 2. 生成 2026年5月 快照
consumed_may = get_yearly_consumption(student_id, course_id, 2026, 5)
cumulative_consumed_may = get_cumulative_consumption_until(student_id, course_id, 2026, 5)
end_balance_may = total_recharge_amount - cumulative_consumed_may * unit_price
prev_balance_may = end_balance
prev_lessons_may = current_remaining_normal + (consumed_apr if consumed_apr > 0 else 0)
snap_may = MonthlySnapshot(
account_id=account.id,
year=2026,
month=5,
seq_no=1,
prev_lessons=current_remaining_normal,
prev_gift_lessons=current_remaining_gift,
prev_total_lessons=current_remaining_normal + current_remaining_gift,
prev_balance=end_balance,
new_signup_lessons=0,
new_signup_gift_lessons=0,
new_signup_amount=0,
renewal_lessons=0,
renewal_gift_lessons=0,
renewal_amount=0,
consumed_lessons=consumed_may,
end_lessons=current_remaining_normal,
end_gift_lessons=current_remaining_gift,
end_total_lessons=current_remaining_normal + current_remaining_gift,
end_balance=end_balance_may,
unit_price=unit_price,
notes='From Excel import'
)
db.session.add(snap_may)
db.session.commit()
print(f"完成! 共生成 {MonthlySnapshot.query.count()} 条月度快照")
# 显示统计
print("\n=== 按月份统计 ===")
from sqlalchemy import func
stats = db.session.query(
MonthlySnapshot.year,
MonthlySnapshot.month,
func.count(MonthlySnapshot.id)
).group_by(MonthlySnapshot.year, MonthlySnapshot.month).order_by(
MonthlySnapshot.year.desc(), MonthlySnapshot.month.desc()
).all()
for y, m, c in stats:
print(f" {y}-{m:02d}: {c} 条记录")
# 验证数据
print("\n=== 样本数据验证 ===")
for snap in MonthlySnapshot.query.filter_by(year=2026, month=4).limit(5).all():
print(f" Account {snap.account_id}: {snap.year}-{snap.month:02d}")
print(f" 月末正课: {snap.end_lessons}, 赠课: {snap.end_gift_lessons}, 总课时: {snap.end_total_lessons}")
print(f" 月末余额: {snap.end_balance:.2f}, 单价: {snap.unit_price:.4f}")
if __name__ == '__main__':
generate_snapshots()