Files
drl_2/xuexiao/models.py
user9994793890 3468394c6c feat: 实现单价分离、班级转课、消课筛选和手动录入功能
Coze-Commit-Type: user
Coze-User-ID: 3722323274763196
Coze-Conversation-ID: 5260473
2026-05-29 10:40:41 +08:00

625 lines
28 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.

"""学生课程管理系统 - SQLAlchemy 数据模型"""
from datetime import datetime, date
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
db = SQLAlchemy()
class Model(db.Model):
"""ORM 基类:表名/列名加反引号,兼容 Doris 保留字roles、classes、date 等)"""
__abstract__ = True
__table_args__ = {'quote': True}
class Role(Model):
"""角色表"""
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), unique=True, nullable=False)
description = db.Column(db.String(200))
permissions = db.Column(db.Text)
remark = db.Column(db.Text)
created_at = db.Column(db.DateTime, default=datetime.now)
def to_dict(self):
return {'id': self.id, 'name': self.name, 'description': self.description,
'permissions': self.permissions, 'remark': self.remark,
'created_at': str(self.created_at)}
class User(Model):
"""用户表"""
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(50), unique=True, nullable=False)
password = db.Column(db.String(200), nullable=False)
real_name = db.Column(db.String(50), nullable=False)
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'), nullable=False)
phone = db.Column(db.String(20))
email = db.Column(db.String(100))
status = db.Column(db.SmallInteger, default=1)
remark = db.Column(db.String(200))
created_at = db.Column(db.DateTime, default=datetime.now)
updated_at = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now)
role = db.relationship('Role', backref='users')
def set_password(self, pwd):
self.password = generate_password_hash(pwd)
def check_password(self, pwd):
return check_password_hash(self.password, pwd)
@property
def is_authenticated(self):
return True
def has_permission(self, perm):
"""检查用户是否拥有指定权限,超级管理员角色拥有所有权限"""
if not self.role:
return False
if self.role.name == '超级管理员':
return True
perms_str = self.role.permissions or ''
if perms_str == 'all':
return True
return perm in perms_str.split(',')
def to_dict(self):
return {'id': self.id, 'username': self.username, 'real_name': self.real_name,
'role_id': self.role_id, 'phone': self.phone, 'email': self.email,
'status': self.status, 'created_at': str(self.created_at)}
class Teacher(Model):
"""老师表"""
__tablename__ = 'teachers'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
name = db.Column(db.String(50), nullable=False)
phone = db.Column(db.String(20))
specialty = db.Column(db.String(200))
status = db.Column(db.SmallInteger, default=1)
remark = db.Column(db.Text)
created_at = db.Column(db.DateTime, default=datetime.now)
user = db.relationship('User', backref='teacher_profile')
def to_dict(self):
return {'id': self.id, 'user_id': self.user_id, 'name': self.name,
'phone': self.phone, 'specialty': self.specialty,
'status': self.status, 'remark': self.remark,
'created_at': str(self.created_at)}
class Student(Model):
"""学员表"""
__tablename__ = 'students'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), nullable=False)
display_name = db.Column(db.String(50))
nickname = db.Column(db.String(50))
gender = db.Column(db.String(10))
phone = db.Column(db.String(20))
parent_phone = db.Column(db.String(20))
birthday = db.Column(db.Date)
address = db.Column(db.String(200))
remark = db.Column(db.Text)
status = db.Column(db.SmallInteger, default=1)
source = db.Column(db.String(50), default='新学员')
referrer_id = db.Column(db.Integer)
created_at = db.Column(db.DateTime, default=datetime.now)
updated_at = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now)
def to_dict(self):
return {'id': self.id, 'name': self.name,
'display_name': self.display_name, 'nickname': self.nickname,
'gender': self.gender,
'phone': self.phone, 'parent_phone': self.parent_phone,
'birthday': str(self.birthday) if self.birthday else None,
'address': self.address, 'remark': self.remark,
'status': self.status, 'source': self.source,
'referrer_id': self.referrer_id,
'created_at': str(self.created_at)}
class Course(Model):
"""课程表"""
__tablename__ = 'courses'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
course_code = db.Column(db.String(20))
type = db.Column(db.String(50), default='一对一')
level = db.Column(db.String(50))
description = db.Column(db.Text)
price_per_hour = db.Column(db.Numeric(10, 2), default=0)
total_hours = db.Column(db.Integer, default=0)
material_fee = db.Column(db.Numeric(10, 2), default=0)
remark = db.Column(db.Text)
status = db.Column(db.SmallInteger, default=1)
created_at = db.Column(db.DateTime, default=datetime.now)
def to_dict(self):
return {'id': self.id, 'name': self.name, 'type': self.type,
'level': self.level, 'description': self.description,
'price_per_hour': float(self.price_per_hour) if self.price_per_hour else 0,
'total_hours': self.total_hours,
'material_fee': float(self.material_fee or 0),
'remark': self.remark,
'status': self.status, 'created_at': str(self.created_at)}
class Class_(Model):
"""班级表"""
__tablename__ = 'classes'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
course_id = db.Column(db.Integer, db.ForeignKey('courses.id'), nullable=False)
teacher_id = db.Column(db.Integer, db.ForeignKey('teachers.id'), nullable=False)
start_date = db.Column(db.Date)
end_date = db.Column(db.Date)
schedule = db.Column(db.String(200))
max_students = db.Column(db.Integer, default=20)
current_students = db.Column(db.Integer, default=0)
remark = db.Column(db.Text)
status = db.Column(db.SmallInteger, default=1)
created_at = db.Column(db.DateTime, default=datetime.now)
course = db.relationship('Course', backref='classes')
teacher = db.relationship('Teacher', backref='classes')
def to_dict(self):
return {'id': self.id, 'name': self.name, 'course_id': self.course_id,
'teacher_id': self.teacher_id,
'start_date': str(self.start_date) if self.start_date else None,
'end_date': str(self.end_date) if self.end_date else None,
'schedule': self.schedule, 'max_students': self.max_students,
'current_students': self.current_students,
'remark': self.remark,
'status': self.status, 'created_at': str(self.created_at)}
class ClassStudent(Model):
"""班级学员关联表"""
__tablename__ = 'class_students'
id = db.Column(db.Integer, primary_key=True)
class_id = db.Column(db.Integer, db.ForeignKey('classes.id'), nullable=False)
student_id = db.Column(db.Integer, db.ForeignKey('students.id'), nullable=False)
join_date = db.Column(db.Date, nullable=False)
status = db.Column(db.SmallInteger, default=1)
created_at = db.Column(db.DateTime, default=datetime.now)
class_ = db.relationship('Class_', backref='student_relations')
student = db.relationship('Student', backref='class_relations')
def to_dict(self):
return {'id': self.id, 'class_id': self.class_id, 'student_id': self.student_id,
'join_date': str(self.join_date), 'status': self.status}
class StudentAccount(Model):
"""学员课时账户表"""
__tablename__ = 'student_accounts'
id = db.Column(db.Integer, primary_key=True)
student_id = db.Column(db.Integer, db.ForeignKey('students.id'), nullable=False)
course_id = db.Column(db.Integer, db.ForeignKey('courses.id'), nullable=False)
from_class_id = db.Column(db.Integer, db.ForeignKey('classes.id'))
from_teacher_id = db.Column(db.Integer, db.ForeignKey('teachers.id'))
normal_hours = db.Column(db.Numeric(10, 2), default=0)
gifted_hours = db.Column(db.Numeric(10, 2), default=0)
consumed_normal = db.Column(db.Numeric(10, 2), default=0)
consumed_gifted = db.Column(db.Numeric(10, 2), default=0)
normal_validity = db.Column(db.String(20), default='permanent')
normal_expiry_date = db.Column(db.Date)
gifted_validity = db.Column(db.String(20), default='permanent')
gifted_expiry_date = db.Column(db.Date)
is_stopped = db.Column(db.SmallInteger, default=0)
stop_end_date = db.Column(db.Date)
cumulative_amount = db.Column(db.Numeric(10, 2), default=0)
cumulative_start_date = db.Column(db.Date)
unit_price = db.Column(db.Numeric(10, 4), default=0)
original_price_per_lesson = db.Column(db.Numeric(10, 4))
account_status = db.Column(db.String(20), default='active')
version = db.Column(db.Integer, default=0)
created_at = db.Column(db.DateTime, default=datetime.now)
updated_at = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now)
student = db.relationship('Student', backref='accounts')
course = db.relationship('Course', backref='accounts')
from_class = db.relationship('Class_', foreign_keys=[from_class_id])
from_teacher = db.relationship('Teacher', foreign_keys=[from_teacher_id])
monthly_snapshots = db.relationship('MonthlySnapshot', backref='account', lazy='dynamic')
class_records = db.relationship('ClassRecord', backref='account', lazy='dynamic')
__table_args__ = (
db.UniqueConstraint('student_id', 'course_id'),
{'quote': True},
)
@property
def total_hours(self):
return float(self.normal_hours or 0) + float(self.gifted_hours or 0)
@property
def total_consumed(self):
return float(self.consumed_normal or 0) + float(self.consumed_gifted or 0)
def to_dict(self):
return {'id': self.id, 'student_id': self.student_id, 'course_id': self.course_id,
'normal_hours': float(self.normal_hours or 0),
'gifted_hours': float(self.gifted_hours or 0),
'consumed_normal': float(self.consumed_normal or 0),
'consumed_gifted': float(self.consumed_gifted or 0),
'total_hours': self.total_hours,
'is_stopped': self.is_stopped,
'cumulative_amount': float(self.cumulative_amount or 0)}
class RechargeActivity(Model):
"""充值优惠活动表"""
__tablename__ = 'recharge_activities'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), nullable=False)
trigger_amount = db.Column(db.Numeric(10, 2), nullable=False)
gifted_hours = db.Column(db.Numeric(10, 2), default=0)
gift_type = db.Column(db.String(50), default='课时')
is_cumulative = db.Column(db.SmallInteger, default=0)
cumulative_window = db.Column(db.Integer, default=0)
cumulative_clear = db.Column(db.SmallInteger, default=0)
allow_multi_trigger = db.Column(db.SmallInteger, default=0)
is_time_limited = db.Column(db.SmallInteger, default=0)
start_date = db.Column(db.Date)
end_date = db.Column(db.Date)
target_audience = db.Column(db.String(50), default='all')
can_stack = db.Column(db.SmallInteger, default=1)
gift_cap = db.Column(db.Numeric(10, 2), default=0)
group_discount = db.Column(db.SmallInteger, default=0)
group_min_people = db.Column(db.Integer, default=0)
group_extra_hours = db.Column(db.Numeric(10, 2), default=0)
referral_reward = db.Column(db.SmallInteger, default=0)
referral_reward_hours = db.Column(db.Numeric(10, 2), default=0)
reward_timing = db.Column(db.String(50), default='immediate')
status = db.Column(db.SmallInteger, default=1)
created_at = db.Column(db.DateTime, default=datetime.now)
def to_dict(self):
return {'id': self.id, 'name': self.name,
'trigger_amount': float(self.trigger_amount or 0),
'gifted_hours': float(self.gifted_hours or 0),
'gift_type': self.gift_type,
'is_cumulative': self.is_cumulative,
'cumulative_window': self.cumulative_window,
'allow_multi_trigger': self.allow_multi_trigger,
'is_time_limited': self.is_time_limited,
'start_date': str(self.start_date) if self.start_date else None,
'end_date': str(self.end_date) if self.end_date else None,
'target_audience': self.target_audience,
'can_stack': self.can_stack,
'gift_cap': float(self.gift_cap or 0),
'group_discount': self.group_discount,
'referral_reward': self.referral_reward,
'reward_timing': self.reward_timing,
'status': self.status}
class RechargeRecord(Model):
"""充值记录表"""
__tablename__ = 'recharge_records'
id = db.Column(db.Integer, primary_key=True)
student_id = db.Column(db.Integer, db.ForeignKey('students.id'), nullable=False)
course_id = db.Column(db.Integer, db.ForeignKey('courses.id'), nullable=False)
activity_id = db.Column(db.Integer, db.ForeignKey('recharge_activities.id'))
amount = db.Column(db.Numeric(10, 2), nullable=False)
normal_hours = db.Column(db.Numeric(10, 2), nullable=False)
gifted_hours = db.Column(db.Numeric(10, 2), default=0)
payment_method = db.Column(db.String(50))
operator_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
remark = db.Column(db.Text)
gift_clawed_back = db.Column(db.SmallInteger, default=0)
created_at = db.Column(db.DateTime, default=datetime.now)
student = db.relationship('Student', backref='recharge_records')
course = db.relationship('Course', backref='recharge_records')
activity = db.relationship('RechargeActivity', backref='records')
operator = db.relationship('User', backref='recharge_operations')
def to_dict(self):
return {'id': self.id, 'student_id': self.student_id, 'course_id': self.course_id,
'activity_id': self.activity_id,
'amount': float(self.amount or 0),
'normal_hours': float(self.normal_hours or 0),
'gifted_hours': float(self.gifted_hours or 0),
'payment_method': self.payment_method,
'operator_id': self.operator_id,
'remark': self.remark,
'created_at': str(self.created_at)}
class ConsumptionRecord(Model):
"""消课记录表"""
__tablename__ = 'consumption_records'
id = db.Column(db.Integer, primary_key=True)
student_id = db.Column(db.Integer, db.ForeignKey('students.id'), nullable=False)
class_id = db.Column(db.Integer, db.ForeignKey('classes.id'))
course_id = db.Column(db.Integer, db.ForeignKey('courses.id'), nullable=False)
hours_consumed = db.Column(db.Numeric(10, 2), nullable=False)
consume_type = db.Column(db.String(20), nullable=False)
normal_consumed = db.Column(db.Numeric(10, 2), default=0)
gifted_consumed = db.Column(db.Numeric(10, 2), default=0)
operator_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
consume_date = db.Column(db.Date, nullable=False)
is_makeup = db.Column(db.SmallInteger, default=0)
is_trial = db.Column(db.SmallInteger, default=0)
unit_price_at_consume = db.Column(db.Numeric(10, 4))
remark = db.Column(db.Text)
created_at = db.Column(db.DateTime, default=datetime.now)
student = db.relationship('Student', backref='consumption_records')
class_ = db.relationship('Class_', backref='consumption_records')
course = db.relationship('Course', backref='consumption_records')
operator = db.relationship('User', backref='consumption_operations')
def to_dict(self):
return {'id': self.id, 'student_id': self.student_id, 'class_id': self.class_id,
'course_id': self.course_id,
'hours_consumed': float(self.hours_consumed or 0),
'consume_type': self.consume_type,
'normal_consumed': float(self.normal_consumed or 0),
'gifted_consumed': float(self.gifted_consumed or 0),
'consume_date': str(self.consume_date),
'is_makeup': self.is_makeup,
'is_trial': self.is_trial,
'remark': self.remark,
'created_at': str(self.created_at)}
class RefundRecord(Model):
"""退费记录表"""
__tablename__ = 'refund_records'
id = db.Column(db.Integer, primary_key=True)
student_id = db.Column(db.Integer, db.ForeignKey('students.id'), nullable=False)
course_id = db.Column(db.Integer, db.ForeignKey('courses.id'), nullable=False)
refund_amount = db.Column(db.Numeric(10, 2), nullable=False)
remaining_normal_hours = db.Column(db.Numeric(10, 2))
remaining_gifted_hours = db.Column(db.Numeric(10, 2))
deduct_gifted_hours = db.Column(db.Numeric(10, 2), default=0)
deduct_gifted_amount = db.Column(db.Numeric(12, 2), default=0)
promo_clawback_hours = db.Column(db.Numeric(10, 2), default=0)
consumed_hours_value = db.Column(db.Numeric(10, 2), default=0)
material_fee_per_quarter = db.Column(db.Numeric(10, 2), default=250)
quarters = db.Column(db.Integer, default=0)
total_deduct = db.Column(db.Numeric(10, 2), default=0)
reason = db.Column(db.Text)
operator_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
status = db.Column(db.SmallInteger, default=1)
created_at = db.Column(db.DateTime, default=datetime.now)
student = db.relationship('Student', backref='refund_records')
course = db.relationship('Course', backref='refund_records')
operator = db.relationship('User', backref='refund_operations')
def to_dict(self):
return {'id': self.id, 'student_id': self.student_id, 'course_id': self.course_id,
'refund_amount': float(self.refund_amount or 0),
'remaining_normal_hours': float(self.remaining_normal_hours or 0),
'remaining_gifted_hours': float(self.remaining_gifted_hours or 0),
'deduct_gifted_hours': float(self.deduct_gifted_hours or 0),
'consumed_hours_value': float(self.consumed_hours_value or 0),
'material_fee_per_quarter': float(self.material_fee_per_quarter or 0),
'quarters': self.quarters,
'total_deduct': float(self.total_deduct or 0),
'reason': self.reason,
'status': self.status,
'created_at': str(self.created_at)}
class TransferRecord(Model):
"""转课记录表"""
__tablename__ = 'transfer_records'
id = db.Column(db.Integer, primary_key=True)
from_student_id = db.Column(db.Integer, db.ForeignKey('students.id'), nullable=False)
to_student_id = db.Column(db.Integer, db.ForeignKey('students.id'))
from_course_id = db.Column(db.Integer, db.ForeignKey('courses.id'), nullable=False)
to_course_id = db.Column(db.Integer, db.ForeignKey('courses.id'))
from_class_id = db.Column(db.Integer, db.ForeignKey('classes.id'))
to_class_id = db.Column(db.Integer, db.ForeignKey('classes.id'))
from_teacher_id = db.Column(db.Integer, db.ForeignKey('teachers.id'))
to_teacher_id = db.Column(db.Integer, db.ForeignKey('teachers.id'))
transfer_type = db.Column(db.String(20), nullable=False)
# 转课类型class_transfer(班级转课) / gift(转赠)
transfer_class_type = db.Column(db.String(20), default='class_transfer')
# 是否变更单价no_change(无单价变更) / price_changed(有单价变更)
price_change_type = db.Column(db.String(20), default='no_change')
transfer_hours = db.Column(db.Numeric(10, 2), nullable=False)
original_unit_price = db.Column(db.Numeric(10, 4))
new_unit_price = db.Column(db.Numeric(10, 4))
transfer_amount = db.Column(db.Numeric(10, 2))
price_change_reason = db.Column(db.Text)
effective_date = db.Column(db.Date)
operator_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
remark = db.Column(db.Text)
created_at = db.Column(db.DateTime, default=datetime.now)
from_student = db.relationship('Student', foreign_keys=[from_student_id], backref='transfers_out')
to_student = db.relationship('Student', foreign_keys=[to_student_id], backref='transfers_in')
from_course = db.relationship('Course', foreign_keys=[from_course_id])
to_course = db.relationship('Course', foreign_keys=[to_course_id])
from_class = db.relationship('Class_', foreign_keys=[from_class_id])
to_class = db.relationship('Class_', foreign_keys=[to_class_id])
from_teacher = db.relationship('Teacher', foreign_keys=[from_teacher_id])
to_teacher = db.relationship('Teacher', foreign_keys=[to_teacher_id])
operator = db.relationship('User', backref='transfer_operations')
def to_dict(self):
return {'id': self.id, 'from_student_id': self.from_student_id,
'to_student_id': self.to_student_id,
'from_course_id': self.from_course_id, 'to_course_id': self.to_course_id,
'from_class_id': self.from_class_id, 'to_class_id': self.to_class_id,
'from_teacher_id': self.from_teacher_id, 'to_teacher_id': self.to_teacher_id,
'transfer_type': self.transfer_type,
'transfer_class_type': self.transfer_class_type,
'price_change_type': self.price_change_type,
'transfer_hours': float(self.transfer_hours or 0),
'original_unit_price': float(self.original_unit_price or 0),
'new_unit_price': float(self.new_unit_price or 0),
'transfer_amount': float(self.transfer_amount or 0),
'price_change_reason': self.price_change_reason,
'effective_date': str(self.effective_date) if self.effective_date else None,
'remark': self.remark,
'created_at': str(self.created_at)}
class StopRecord(Model):
"""停课记录表"""
__tablename__ = 'stop_records'
id = db.Column(db.Integer, primary_key=True)
student_id = db.Column(db.Integer, db.ForeignKey('students.id'), nullable=False)
course_id = db.Column(db.Integer, db.ForeignKey('courses.id'), nullable=False)
start_date = db.Column(db.Date, nullable=False)
end_date = db.Column(db.Date, nullable=False)
reason = db.Column(db.Text)
operator_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
status = db.Column(db.SmallInteger, default=1)
created_at = db.Column(db.DateTime, default=datetime.now)
student = db.relationship('Student', backref='stop_records')
course = db.relationship('Course', backref='stop_records')
operator = db.relationship('User', backref='stop_operations')
def to_dict(self):
return {'id': self.id, 'student_id': self.student_id, 'course_id': self.course_id,
'start_date': str(self.start_date), 'end_date': str(self.end_date),
'reason': self.reason, 'status': self.status,
'created_at': str(self.created_at)}
class AttendanceRecord(Model):
"""考勤记录表"""
__tablename__ = 'attendance_records'
id = db.Column(db.Integer, primary_key=True)
student_id = db.Column(db.Integer, db.ForeignKey('students.id'), nullable=False)
class_id = db.Column(db.Integer, db.ForeignKey('classes.id'), nullable=False)
date = db.Column(db.Date, nullable=False)
status = db.Column(db.String(20), nullable=False)
remark = db.Column(db.Text)
created_at = db.Column(db.DateTime, default=datetime.now)
student = db.relationship('Student', backref='attendance_records')
class_ = db.relationship('Class_', backref='attendance_records')
def to_dict(self):
return {'id': self.id, 'student_id': self.student_id, 'class_id': self.class_id,
'date': str(self.date), 'status': self.status, 'remark': self.remark}
class OperationLog(Model):
"""操作日志表"""
__tablename__ = 'operation_logs'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
operation_type = db.Column(db.String(50), nullable=False)
operation_detail = db.Column(db.Text)
ip_address = db.Column(db.String(50))
created_at = db.Column(db.DateTime, default=datetime.now)
user = db.relationship('User', backref='operation_logs')
def to_dict(self):
return {'id': self.id, 'user_id': self.user_id,
'operation_type': self.operation_type,
'operation_detail': self.operation_detail,
'ip_address': self.ip_address,
'created_at': str(self.created_at)}
class Schedule(Model):
"""课程安排表"""
__tablename__ = 'schedules'
id = db.Column(db.Integer, primary_key=True)
class_id = db.Column(db.Integer, db.ForeignKey('classes.id'), nullable=False)
date = db.Column(db.Date, nullable=False)
start_time = db.Column(db.String(10), nullable=False)
end_time = db.Column(db.String(10), nullable=False)
teacher_id = db.Column(db.Integer, db.ForeignKey('teachers.id'), nullable=False)
topic = db.Column(db.String(200))
created_at = db.Column(db.DateTime, default=datetime.now)
class_ = db.relationship('Class_', backref='schedules')
teacher = db.relationship('Teacher', backref='schedules')
def to_dict(self):
return {'id': self.id, 'class_id': self.class_id,
'date': str(self.date), 'start_time': self.start_time,
'end_time': self.end_time, 'teacher_id': self.teacher_id,
'topic': self.topic}
# ========== 课时核对表Excel 流水账)==========
COURSE_CODES = ('scratch', 'python', 'c++', 'wedo')
class MonthlySnapshot(Model):
"""月度课时快照:每个学员课程账户每月一条"""
__tablename__ = 'monthly_snapshots'
id = db.Column(db.Integer, primary_key=True)
account_id = db.Column(db.Integer, db.ForeignKey('student_accounts.id'), nullable=False)
year = db.Column(db.SmallInteger, nullable=False)
month = db.Column(db.SmallInteger, nullable=False)
seq_no = db.Column(db.Integer)
prev_lessons = db.Column(db.Numeric(10, 2))
prev_gift_lessons = db.Column(db.Numeric(10, 2))
prev_total_lessons = db.Column(db.Numeric(10, 2))
prev_balance = db.Column(db.Numeric(12, 2))
new_signup_amount = db.Column(db.Numeric(12, 2))
new_signup_lessons = db.Column(db.Numeric(10, 2))
new_signup_gift_lessons = db.Column(db.Numeric(10, 2))
renewal_amount = db.Column(db.Numeric(12, 2))
renewal_lessons = db.Column(db.Numeric(10, 2))
renewal_gift_lessons = db.Column(db.Numeric(10, 2))
consumed_lessons = db.Column(db.Numeric(10, 2))
consumed_amount = db.Column(db.Numeric(12, 2))
refund_lessons = db.Column(db.Numeric(10, 2))
account_fee = db.Column(db.Numeric(12, 2))
refund_gift_lessons = db.Column(db.Numeric(10, 2))
refund_amount = db.Column(db.Numeric(12, 2))
end_lessons = db.Column(db.Numeric(10, 2))
end_gift_lessons = db.Column(db.Numeric(10, 2))
end_total_lessons = db.Column(db.Numeric(10, 2))
end_balance = db.Column(db.Numeric(12, 2))
unit_price = db.Column(db.Numeric(10, 4))
notes = db.Column(db.Text)
source_file = db.Column(db.String(200))
created_at = db.Column(db.DateTime, default=datetime.now)
__table_args__ = (
db.UniqueConstraint('account_id', 'year', 'month', name='uq_snapshot_account_month'),
{'quote': True},
)
@property
def period_label(self):
return f'{self.year}-{self.month:02d}'
class ClassRecord(Model):
"""上课记录:从 Excel「上课情况」列解析"""
__tablename__ = 'class_records'
id = db.Column(db.Integer, primary_key=True)
account_id = db.Column(db.Integer, db.ForeignKey('student_accounts.id'), nullable=False)
year = db.Column(db.SmallInteger, nullable=False)
month = db.Column(db.SmallInteger, nullable=False)
class_date = db.Column(db.Date, nullable=False)
lessons_consumed = db.Column(db.Numeric(10, 2), default=2)
raw_text = db.Column(db.String(50))
created_at = db.Column(db.DateTime, default=datetime.now)
__table_args__ = (
db.UniqueConstraint(
'account_id', 'year', 'month', 'class_date', 'lessons_consumed',
name='uq_class_record_day',
),
{'quote': True},
)