ai_v/models.py
24024 0da71bc439 ```
feat(admin): 添加积分发放管理和邀请奖励功能

- 新增积分发放相关模型 PointsGrant 和 InviteReward
- 实现管理员积分发放接口(单个、批量、全员发放)
- 添加积分发放记录查询和统计功能
- 集成邀请奖励机制,在用户充值时自动发放邀请奖励
- 在用户注册流程中集成邀请码功能
- 扩展用户信息返回积分和创建时间字段
- 添加前端邀请码处理和邀请统计功能
```
2026-01-23 21:46:08 +08:00

248 lines
10 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.

from extensions import db
from datetime import datetime, timedelta, timezone
from werkzeug.security import generate_password_hash, check_password_hash
# 定义时区常量
UTC_TZ = timezone.utc
BEIJING_TZ = timezone(timedelta(hours=8))
def get_bj_now():
"""获取当前北京时间(无时区信息的 naive datetime"""
return datetime.now(BEIJING_TZ).replace(tzinfo=None)
def to_bj_time(dt):
"""返回北京时间
注意:为统一时间处理,现在所有时间存储都直接使用北京时间。
此函数主要用于兼容性,直接返回输入时间。
"""
if not dt:
return None
# 直接返回(数据库存储的已经是北京时间)
return dt if dt.tzinfo is None else dt.replace(tzinfo=None)
# 角色与权限的多对多关联表
role_permissions = db.Table('role_permissions',
db.Column('role_id', db.Integer, db.ForeignKey('roles.id'), primary_key=True),
db.Column('permission_id', db.Integer, db.ForeignKey('permissions.id'), primary_key=True)
)
class Permission(db.Model):
__tablename__ = 'permissions'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), unique=True, nullable=False) # 如: 'view_logs', 'manage_users'
description = db.Column(db.String(100))
class Role(db.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(100))
# 角色拥有的权限
permissions = db.relationship('Permission', secondary=role_permissions,
backref=db.backref('roles', lazy='dynamic'),
order_by='Permission.id')
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
phone = db.Column(db.String(20), unique=True, nullable=False)
password_hash = db.Column(db.String(255), nullable=False)
api_key = db.Column(db.String(255)) # 存储用户的 API Key
points = db.Column(db.Integer, default=2) # 账户积分默认赠送2次试用
has_used_points = db.Column(db.Boolean, default=False) # 是否使用过积分
is_banned = db.Column(db.Boolean, default=False) # 账号是否被封禁
# 关联角色 ID
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
created_at = db.Column(db.DateTime, default=get_bj_now)
# 邀请系统字段
invite_code = db.Column(db.String(10), unique=True) # 用户专属邀请码
invited_by = db.Column(db.Integer, db.ForeignKey('users.id')) # 邀请人ID
@property
def created_at_bj(self):
return to_bj_time(self.created_at)
# 关系映射
role = db.relationship('Role', backref=db.backref('users', lazy='dynamic'))
def has_permission(self, perm_name):
"""动态检查用户是否拥有某项权限"""
if not self.role:
return False
# 获取用户拥有的所有权限名称
perms = [p.name for p in self.role.permissions]
# 核心修复:如果是超级管理员(拥有 manage_system则豁免所有具体权限检查
if 'manage_system' in perms:
return True
return perm_name in perms
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
class SystemDict(db.Model):
"""通用字典管理系统"""
__tablename__ = 'system_dicts'
id = db.Column(db.Integer, primary_key=True)
dict_type = db.Column(db.String(50), nullable=False) # 如: 'ai_model', 'aspect_ratio', 'prompt_tpl'
label = db.Column(db.String(100), nullable=False) # 显示名称
value = db.Column(db.Text, nullable=False) # 存储值或提示词内容
cost = db.Column(db.Integer, default=0) # 消耗积分 (仅针对 ai_model 有效)
is_active = db.Column(db.Boolean, default=True)
sort_order = db.Column(db.Integer, default=0) # 排序权重
class GenerationRecord(db.Model):
"""AI 生成记录"""
__tablename__ = 'generation_records'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
prompt = db.Column(db.Text)
model = db.Column(db.String(100))
cost = db.Column(db.Integer, default=0) # 消耗积分
# 存储生成的图片 URL 列表 (JSON 字符串)
image_urls = db.Column(db.Text)
created_at = db.Column(db.DateTime, default=get_bj_now)
@property
def created_at_bj(self):
return to_bj_time(self.created_at)
user = db.relationship('User', backref=db.backref('records', lazy='dynamic', order_by='GenerationRecord.created_at.desc()'))
# 用户已读通知关联表
notification_reads = db.Table('notification_reads',
db.Column('user_id', db.Integer, db.ForeignKey('users.id'), primary_key=True),
db.Column('notification_id', db.Integer, db.ForeignKey('system_notifications.id'), primary_key=True)
)
class SystemNotification(db.Model):
"""系统全局通知"""
__tablename__ = 'system_notifications'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(200), nullable=False)
content = db.Column(db.Text, nullable=False)
is_active = db.Column(db.Boolean, default=True)
created_at = db.Column(db.DateTime, default=get_bj_now)
@property
def created_at_bj(self):
return to_bj_time(self.created_at)
# 哪些用户已读
read_by_users = db.relationship('User', secondary=notification_reads, backref=db.backref('read_notifications', lazy='dynamic'))
class Order(db.Model):
"""订单模型"""
__tablename__ = 'orders'
id = db.Column(db.Integer, primary_key=True)
out_trade_no = db.Column(db.String(64), unique=True, nullable=False) # 系统订单号
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
amount = db.Column(db.Numeric(10, 2), nullable=False) # 金额
points = db.Column(db.Integer, nullable=False) # 购买的积分
status = db.Column(db.String(20), default='PENDING') # PENDING, PAID, CANCELLED
trade_no = db.Column(db.String(64)) # 支付宝交易号
created_at = db.Column(db.DateTime, default=get_bj_now)
paid_at = db.Column(db.DateTime)
@property
def created_at_bj(self):
return to_bj_time(self.created_at)
@property
def paid_at_bj(self):
return to_bj_time(self.paid_at)
user = db.relationship('User', backref=db.backref('orders', lazy='dynamic', order_by='Order.created_at.desc()'))
class SystemLog(db.Model):
"""系统精细化日志记录"""
__tablename__ = 'system_logs'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True) # 可能没有登录用户
level = db.Column(db.String(20), nullable=False) # INFO, WARNING, ERROR, DEBUG
module = db.Column(db.String(50)) # 模块名
message = db.Column(db.Text, nullable=False) # 日志内容
extra = db.Column(db.Text) # 额外信息的 JSON 字符串
# 请求上下文信息
ip = db.Column(db.String(50))
path = db.Column(db.String(255))
method = db.Column(db.String(10))
user_agent = db.Column(db.String(255))
created_at = db.Column(db.DateTime, default=get_bj_now)
@property
def created_at_bj(self):
return to_bj_time(self.created_at)
user = db.relationship('User', backref=db.backref('logs', lazy='dynamic', order_by='SystemLog.created_at.desc()'))
class SavedPrompt(db.Model):
"""用户收藏的提示词"""
__tablename__ = 'saved_prompts'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
title = db.Column(db.String(100), nullable=False)
prompt = db.Column(db.Text, nullable=False)
created_at = db.Column(db.DateTime, default=get_bj_now)
@property
def created_at_bj(self):
return to_bj_time(self.created_at)
user = db.relationship('User', backref=db.backref('saved_prompts', lazy='dynamic', order_by='SavedPrompt.created_at.desc()'))
class PointsGrant(db.Model):
"""积分发放记录(管理员手动发放)"""
__tablename__ = 'points_grants'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
points = db.Column(db.Integer, nullable=False) # 发放积分数
reason = db.Column(db.String(200)) # 发放原因
admin_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) # 操作管理员
created_at = db.Column(db.DateTime, default=get_bj_now)
@property
def created_at_bj(self):
return to_bj_time(self.created_at)
user = db.relationship('User', foreign_keys=[user_id], backref=db.backref('received_grants', lazy='dynamic'))
admin = db.relationship('User', foreign_keys=[admin_id])
class InviteReward(db.Model):
"""邀请奖励记录(被邀请人充值时,邀请人获得的奖励)"""
__tablename__ = 'invite_rewards'
id = db.Column(db.Integer, primary_key=True)
inviter_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) # 邀请人ID
invitee_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) # 被邀请人ID
order_id = db.Column(db.Integer, db.ForeignKey('orders.id'), nullable=False) # 触发奖励的订单
reward_points = db.Column(db.Integer, nullable=False) # 奖励积分数
recharge_count = db.Column(db.Integer, nullable=False) # 这是被邀请人的第几次充值1-3
created_at = db.Column(db.DateTime, default=get_bj_now)
@property
def created_at_bj(self):
return to_bj_time(self.created_at)
inviter = db.relationship('User', foreign_keys=[inviter_id], backref=db.backref('invite_rewards', lazy='dynamic'))
invitee = db.relationship('User', foreign_keys=[invitee_id])
order = db.relationship('Order', backref=db.backref('invite_reward', uselist=False))