Files
vps_web/models.py
ddrwode 5fe60fdd57 哈哈
2026-02-09 22:36:32 +08:00

164 lines
6.5 KiB
Python

# -*- coding: utf-8 -*-
"""数据库模型"""
from datetime import datetime
from werkzeug.security import generate_password_hash, check_password_hash
from extensions import db
class Provider(db.Model):
"""厂商(一个厂商对应多个配置)"""
__tablename__ = "providers"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), nullable=False, unique=True, index=True)
official_url = db.Column(db.String(512), nullable=True) # 厂商默认官网
plans = db.relationship("VPSPlan", backref="provider_rel", lazy="dynamic", foreign_keys="VPSPlan.provider_id")
class User(db.Model):
"""前台用户"""
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(32), nullable=False, unique=True, index=True)
password_hash = db.Column(db.String(255), nullable=False)
created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow, index=True)
last_login_at = db.Column(db.DateTime, nullable=True)
posts = db.relationship(
"ForumPost",
backref="author_rel",
lazy="dynamic",
cascade="all, delete-orphan",
foreign_keys="ForumPost.user_id",
)
comments = db.relationship(
"ForumComment",
backref="author_rel",
lazy="dynamic",
cascade="all, delete-orphan",
foreign_keys="ForumComment.user_id",
)
def set_password(self, raw_password):
self.password_hash = generate_password_hash(raw_password)
def check_password(self, raw_password):
if not self.password_hash:
return False
return check_password_hash(self.password_hash, raw_password)
class VPSPlan(db.Model):
"""云服务器配置/方案(属于某厂商)"""
__tablename__ = "vps_plans"
id = db.Column(db.Integer, primary_key=True)
provider_id = db.Column(db.Integer, db.ForeignKey("providers.id"), nullable=True, index=True) # 关联厂商
provider = db.Column(db.String(64), nullable=True, index=True) # 冗余显示名,与 provider_id 二选一兼容旧数据
region = db.Column(db.String(128), nullable=True, index=True) # 保留兼容,不再在表单中填写
name = db.Column(db.String(128), nullable=True) # 可选,不填则用配置项生成显示
vcpu = db.Column(db.Integer, nullable=True)
memory_gb = db.Column(db.Integer, nullable=True)
storage_gb = db.Column(db.Integer, nullable=True)
bandwidth_mbps = db.Column(db.Integer, nullable=True)
traffic = db.Column(db.String(64), nullable=True)
price_cny = db.Column(db.Float, nullable=True)
price_usd = db.Column(db.Float, nullable=True)
currency = db.Column(db.String(8), default="CNY")
official_url = db.Column(db.String(512), nullable=True) # 该配置详情页,可覆盖厂商默认
countries = db.Column(db.String(255), nullable=True, index=True)
price_histories = db.relationship(
"PriceHistory",
backref="plan_rel",
lazy="dynamic",
cascade="all, delete-orphan",
foreign_keys="PriceHistory.plan_id",
)
@property
def provider_name(self):
if self.provider_rel:
return self.provider_rel.name
return self.provider or ""
@property
def display_name(self):
"""无规格名称时用配置项生成,如 2核4G 80GB"""
if self.name and self.name.strip():
return self.name.strip()
parts = []
if self.vcpu is not None:
parts.append("{}".format(self.vcpu))
if self.memory_gb is not None:
parts.append("{}G".format(self.memory_gb))
if self.storage_gb is not None:
parts.append("{}GB".format(self.storage_gb))
if self.traffic and self.traffic.strip():
parts.append(self.traffic.strip())
return " ".join(parts) if parts else ""
def to_dict(self):
return {
"id": self.id,
"provider": self.provider_name,
"region": self.region or "",
"name": self.display_name,
"vcpu": self.vcpu,
"memory_gb": self.memory_gb,
"storage_gb": self.storage_gb,
"bandwidth_mbps": self.bandwidth_mbps,
"traffic": self.traffic or "",
"price_cny": float(self.price_cny) if self.price_cny is not None else None,
"price_usd": float(self.price_usd) if self.price_usd is not None else None,
"currency": self.currency or "CNY",
"official_url": self.official_url or (self.provider_rel.official_url if self.provider_rel else "") or "",
"countries": self.countries or "",
}
class PriceHistory(db.Model):
"""配置价格历史快照"""
__tablename__ = "price_histories"
id = db.Column(db.Integer, primary_key=True)
plan_id = db.Column(db.Integer, db.ForeignKey("vps_plans.id"), nullable=False, index=True)
price_cny = db.Column(db.Float, nullable=True)
price_usd = db.Column(db.Float, nullable=True)
currency = db.Column(db.String(8), nullable=False, default="CNY")
source = db.Column(db.String(32), nullable=True) # manual / import / bootstrap
captured_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow, index=True)
class ForumPost(db.Model):
"""论坛帖子"""
__tablename__ = "forum_posts"
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False, index=True)
title = db.Column(db.String(160), nullable=False, index=True)
content = db.Column(db.Text, nullable=False)
created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow, index=True)
updated_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow)
comments = db.relationship(
"ForumComment",
backref="post_rel",
lazy="dynamic",
cascade="all, delete-orphan",
foreign_keys="ForumComment.post_id",
)
class ForumComment(db.Model):
"""论坛评论"""
__tablename__ = "forum_comments"
id = db.Column(db.Integer, primary_key=True)
post_id = db.Column(db.Integer, db.ForeignKey("forum_posts.id"), nullable=False, index=True)
user_id = db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False, index=True)
content = db.Column(db.Text, nullable=False)
created_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow, index=True)
updated_at = db.Column(db.DateTime, nullable=False, default=datetime.utcnow, onupdate=datetime.utcnow)