Files
pdf-translate-web/models.py
T
coder be1e01a8c6 初始提交: PDF翻译网站
功能:
- 上传PDF自动翻译成中文
- 不登录可用(次数限制)
- 登录后获得更多次数
- 会员等级制度(不同权益)
- 智能缓存(相同文件秒出结果)
- 原文译文对比查看
- 支持重新翻译(自定义要求)
- 翻译历史记录

技术栈: Flask + SQLAlchemy + Bootstrap5
2026-04-07 19:01:14 +08:00

199 lines
6.9 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 datetime import datetime
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
import hashlib
db = SQLAlchemy()
# ==================== 用户模型 ====================
class User(db.Model):
"""用户表"""
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
password_hash = db.Column(db.String(256), nullable=False)
# 用户类型: guest, free, vip_basic, vip_pro, vip_enterprise
user_type = db.Column(db.String(20), default='free')
# 会员信息
membership_expire = db.Column(db.DateTime, nullable=True) # 会员到期时间
# 使用统计
daily_count = db.Column(db.Integer, default=0) # 今日翻译次数
total_count = db.Column(db.Integer, default=0) # 总翻译次数
last_translate_date = db.Column(db.Date, nullable=True) # 最后翻译日期
# 时间戳
created_at = db.Column(db.DateTime, default=datetime.utcnow)
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
# 关系
translations = db.relationship('Translation', backref='user', lazy=True)
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)
def is_vip(self):
"""检查是否为付费会员"""
if self.user_type.startswith('vip'):
if self.membership_expire and self.membership_expire > datetime.utcnow():
return True
# 过期则降级为免费用户
self.user_type = 'free'
self.membership_expire = None
db.session.commit()
return False
def can_translate(self, pages, config):
"""检查是否可以翻译(次数、页数限制)"""
limits = config['USER_LIMITS'].get(self.user_type, config['USER_LIMITS']['free'])
# 检查页数限制
max_pages = limits['max_pages']
if max_pages > 0 and pages > max_pages:
return False, f"PDF页数超出限制(最大{max_pages}页)"
# 检查每日次数限制
today = datetime.utcnow().date()
if self.last_translate_date != today:
self.daily_count = 0
self.last_translate_date = today
daily_limit = limits['daily_translations']
if daily_limit > 0 and self.daily_count >= daily_limit:
return False, f"今日翻译次数已达上限({daily_limit}次)"
return True, "OK"
def increment_count(self):
"""增加翻译计数"""
today = datetime.utcnow().date()
if self.last_translate_date != today:
self.daily_count = 0
self.last_translate_date = today
self.daily_count += 1
self.total_count += 1
db.session.commit()
def to_dict(self):
return {
'id': self.id,
'username': self.username,
'email': self.email,
'user_type': self.user_type,
'is_vip': self.is_vip(),
'daily_count': self.daily_count,
'total_count': self.total_count,
}
# ==================== 翻译记录模型 ====================
class Translation(db.Model):
"""翻译记录表"""
__tablename__ = 'translations'
id = db.Column(db.Integer, primary_key=True)
# 用户关联
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True) # guest可为null
# 文件信息
file_hash = db.Column(db.String(64), nullable=False) # 文件MD5哈希
original_filename = db.Column(db.String(255), nullable=False)
file_size = db.Column(db.Integer, nullable=False)
page_count = db.Column(db.Integer, nullable=False)
# 翻译信息
source_language = db.Column(db.String(10), default='en')
target_language = db.Column(db.String(10), default='zh')
translate_params = db.Column(db.Text, nullable=True) # JSON格式的翻译参数
# 状态
status = db.Column(db.String(20), default='pending') # pending, processing, completed, failed
progress = db.Column(db.Integer, default=0) # 翻译进度 0-100
error_message = db.Column(db.Text, nullable=True)
# 输出
output_path = db.Column(db.String(255), nullable=True) # 翻译结果文件路径
# 时间戳
created_at = db.Column(db.DateTime, default=datetime.utcnow)
completed_at = db.Column(db.DateTime, nullable=True)
# 是否来自缓存
from_cache = db.Column(db.Boolean, default=False)
# 重译信息
retranslate_request = db.Column(db.Text, nullable=True) # 重译要求
parent_id = db.Column(db.Integer, db.ForeignKey('translations.id'), nullable=True) # 原翻译ID
def to_dict(self):
return {
'id': self.id,
'filename': self.original_filename,
'pages': self.page_count,
'status': self.status,
'progress': self.progress,
'from_cache': self.from_cache,
'created_at': self.created_at.isoformat() if self.created_at else None,
'completed_at': self.completed_at.isoformat() if self.completed_at else None,
}
# ==================== 翻译缓存模型 ====================
class TranslationCache(db.Model):
"""翻译缓存表"""
__tablename__ = 'translation_cache'
id = db.Column(db.Integer, primary_key=True)
# 文件哈希
file_hash = db.Column(db.String(64), unique=True, nullable=False)
# 缓存信息
cache_path = db.Column(db.String(255), nullable=False) # 缓存文件路径
page_count = db.Column(db.Integer, nullable=False)
# 统计
hit_count = db.Column(db.Integer, default=0) # 缓存命中次数
# 时间戳
created_at = db.Column(db.DateTime, default=datetime.utcnow)
expires_at = db.Column(db.DateTime, nullable=True)
def increment_hit(self):
self.hit_count += 1
db.session.commit()
@staticmethod
def compute_hash(file_content):
"""计算文件哈希"""
return hashlib.md5(file_content).hexdigest()
# ==================== 访客翻译记录 ====================
class GuestTranslation(db.Model):
"""访客翻译记录(基于IP或Session"""
__tablename__ = 'guest_translations'
id = db.Column(db.Integer, primary_key=True)
# 访客标识
session_id = db.Column(db.String(64), nullable=False) # Session ID
ip_address = db.Column(db.String(45), nullable=True)
# 统计
daily_count = db.Column(db.Integer, default=0)
last_translate_date = db.Column(db.Date, nullable=True)
created_at = db.Column(db.DateTime, default=datetime.utcnow)