2026-03-23 11:08:52 +08:00
|
|
|
|
# 数据库模型 - 使用字段编码
|
2026-03-25 10:58:00 +08:00
|
|
|
|
from sqlalchemy import Column, String, Float, Boolean, DateTime, Integer, Text, ForeignKey, Date, UniqueConstraint
|
2026-03-23 11:08:52 +08:00
|
|
|
|
from sqlalchemy.orm import relationship
|
|
|
|
|
|
from sqlalchemy.sql import func
|
|
|
|
|
|
from app.core.database import Base
|
|
|
|
|
|
import uuid
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def generate_uuid():
|
|
|
|
|
|
"""生成 UUID 字符串"""
|
|
|
|
|
|
return str(uuid.uuid4())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class User(Base):
|
|
|
|
|
|
__tablename__ = "users"
|
|
|
|
|
|
|
|
|
|
|
|
# f99 系统字段
|
|
|
|
|
|
f99_90_id = Column(String(36), primary_key=True, default=generate_uuid)
|
|
|
|
|
|
user_code = Column(String(10), unique=True, nullable=True, index=True) # 用户编码
|
|
|
|
|
|
f99_91_user_id = Column(String(36), unique=True, nullable=False, index=True)
|
|
|
|
|
|
f01_01_name = Column(String(255), unique=True, nullable=False, index=True) # username
|
|
|
|
|
|
email = Column(String(255), unique=True, nullable=True, index=True)
|
|
|
|
|
|
phone = Column(String(50), nullable=True)
|
|
|
|
|
|
avatar = Column(String(500), nullable=True)
|
|
|
|
|
|
address = Column(String(500), nullable=True)
|
|
|
|
|
|
bio = Column(Text, nullable=True)
|
|
|
|
|
|
password = Column(String(255), nullable=False)
|
|
|
|
|
|
role = Column(String(50), default="user")
|
2026-03-24 22:52:37 +08:00
|
|
|
|
# 时间字段
|
2026-03-23 11:08:52 +08:00
|
|
|
|
f99_92_created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
|
|
f99_93_updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
2026-03-24 22:52:37 +08:00
|
|
|
|
# 新增字段
|
|
|
|
|
|
f99_94_level = Column(String(20), default="青铜") # 会员等级
|
|
|
|
|
|
f99_95_ai_count = Column(Integer, default=0) # ai识别次数
|
|
|
|
|
|
f99_96_search_count = Column(Integer, default=0) # 寻号使用次数
|
|
|
|
|
|
f99_97_collection_count = Column(Integer, default=0) # 藏品数量
|
|
|
|
|
|
f01_06_phone_verified = Column(Boolean, default=False) # 手机号已核验
|
|
|
|
|
|
f99_98_login_count = Column(Integer, default=0) # 登录次数
|
|
|
|
|
|
f99_99_last_login = Column(DateTime(timezone=True), nullable=True) # 最后登录时间
|
|
|
|
|
|
f01_07_gender = Column(String(10), nullable=True) # 性别
|
|
|
|
|
|
f01_08_birthday = Column(Date, nullable=True) # 生日
|
|
|
|
|
|
f01_09_region = Column(String(100), nullable=True) # 地区
|
|
|
|
|
|
f01_10_realname_verified = Column(Boolean, default=False) # 实名认证
|
|
|
|
|
|
f99_100_points = Column(Integer, default=0) # 积分
|
|
|
|
|
|
f01_11_balance = Column(Float, default=0) # 余额
|
|
|
|
|
|
f01_12_total_amount = Column(Float, default=0) # 累计金额
|
|
|
|
|
|
f01_13_invite_code = Column(String(20), nullable=True) # 邀请码
|
2026-03-23 11:08:52 +08:00
|
|
|
|
|
|
|
|
|
|
collections = relationship("Collection", back_populates="user", cascade="all, delete-orphan")
|
|
|
|
|
|
operations = relationship("Operation", back_populates="user", cascade="all, delete-orphan")
|
|
|
|
|
|
custom_fields = relationship("CustomField", back_populates="user", cascade="all, delete-orphan")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Collection(Base):
|
|
|
|
|
|
__tablename__ = "collections"
|
|
|
|
|
|
|
|
|
|
|
|
# f99 系统字段
|
|
|
|
|
|
f99_90_id = Column(String(36), primary_key=True, default=generate_uuid)
|
|
|
|
|
|
f99_91_user_id = Column(String(36), ForeignKey("users.f99_90_id", ondelete="CASCADE"), nullable=False, index=True)
|
|
|
|
|
|
f99_92_created_at = Column(DateTime(timezone=True), server_default=func.now(), index=True)
|
|
|
|
|
|
f99_93_updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
|
|
|
|
|
|
|
|
|
|
|
# f01 基本信息
|
|
|
|
|
|
f01_01_name = Column(String(255), nullable=False)
|
|
|
|
|
|
f01_02_code = Column(String(50), nullable=True, index=True)
|
|
|
|
|
|
f01_03_category = Column(String(100), nullable=False, index=True)
|
|
|
|
|
|
f01_04_status = Column(String(50), default="in_collection", index=True)
|
|
|
|
|
|
f01_05_remark = Column(Text, nullable=True)
|
|
|
|
|
|
|
|
|
|
|
|
# f02 详细字段
|
|
|
|
|
|
f02_10_prefix_serial = Column(String(50), nullable=True, index=True)
|
|
|
|
|
|
f02_11_version = Column(String(100), nullable=True, index=True)
|
|
|
|
|
|
f02_12_packaging = Column(String(100), nullable=True, index=True)
|
|
|
|
|
|
f02_13_rarity = Column(String(50), nullable=True, index=True) # 珍惜度
|
|
|
|
|
|
f02_14_number_category = Column(String(20), nullable=True, index=True) # 号码分类
|
|
|
|
|
|
|
|
|
|
|
|
# f03 评级信息
|
|
|
|
|
|
f03_20_is_graded = Column(Boolean, default=False, index=True)
|
|
|
|
|
|
f03_21_grading_company = Column(String(100), nullable=True, index=True)
|
|
|
|
|
|
f03_22_grading_score = Column(String(20), nullable=True, index=True)
|
|
|
|
|
|
f03_23_three_star = Column(Boolean, default=False, index=True)
|
|
|
|
|
|
|
|
|
|
|
|
# f04 特殊信息
|
|
|
|
|
|
f04_30_special_mark = Column(String(200), nullable=True, index=True)
|
|
|
|
|
|
f04_31_serial_feature = Column(String(100), nullable=True, index=True)
|
|
|
|
|
|
f04_32_issuer = Column(String(100), nullable=True, index=True)
|
|
|
|
|
|
f04_33_issue_year = Column(String(20), nullable=True, index=True)
|
|
|
|
|
|
f04_34_material = Column(String(50), nullable=True)
|
|
|
|
|
|
f04_35_denomination = Column(String(20), nullable=True)
|
|
|
|
|
|
f04_36_issue_quantity = Column(String(50), nullable=True)
|
|
|
|
|
|
|
|
|
|
|
|
# f05 价格信息
|
|
|
|
|
|
f05_40_cost_price = Column(Float, nullable=True, index=True)
|
|
|
|
|
|
f05_41_target_price = Column(Float, nullable=True, index=True)
|
|
|
|
|
|
f05_42_goal_price = Column(Float, nullable=True)
|
|
|
|
|
|
f05_43_repair_fee = Column(Float, nullable=True)
|
|
|
|
|
|
f05_44_grading_fee = Column(Float, nullable=True)
|
|
|
|
|
|
|
|
|
|
|
|
# f06 其他信息
|
|
|
|
|
|
f06_50_purpose = Column(String(100), nullable=True)
|
|
|
|
|
|
|
|
|
|
|
|
user = relationship("User", back_populates="collections")
|
|
|
|
|
|
images = relationship("CollectionImage", back_populates="collection", cascade="all, delete-orphan")
|
|
|
|
|
|
operations = relationship("Operation", back_populates="collection", cascade="all, delete-orphan")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CollectionImage(Base):
|
|
|
|
|
|
__tablename__ = "collection_images"
|
|
|
|
|
|
|
|
|
|
|
|
id = Column(String(36), primary_key=True, default=generate_uuid)
|
|
|
|
|
|
collection_id = Column(String(36), ForeignKey("collections.f99_90_id", ondelete="CASCADE"), nullable=False, index=True)
|
|
|
|
|
|
filename = Column(String(255), nullable=False)
|
|
|
|
|
|
original_name = Column(String(255), nullable=True)
|
|
|
|
|
|
path = Column(String(500), nullable=True)
|
|
|
|
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
|
|
|
|
|
|
|
|
collection = relationship("Collection", back_populates="images")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Operation(Base):
|
|
|
|
|
|
__tablename__ = "operations"
|
|
|
|
|
|
|
|
|
|
|
|
f99_90_id = Column(String(36), primary_key=True, default=generate_uuid)
|
|
|
|
|
|
f99_91_user_id = Column(String(36), ForeignKey("collections.f99_90_id", ondelete="CASCADE"), nullable=False, index=True)
|
|
|
|
|
|
f99_92_user_id = Column(String(36), ForeignKey("users.f99_90_id", ondelete="CASCADE"), nullable=False, index=True)
|
|
|
|
|
|
type = Column(String(50), nullable=False, index=True)
|
|
|
|
|
|
price = Column(Float, nullable=True)
|
|
|
|
|
|
note = Column(Text, nullable=True)
|
|
|
|
|
|
f99_93_created_at = Column(DateTime(timezone=True), server_default=func.now(), index=True)
|
|
|
|
|
|
|
|
|
|
|
|
collection = relationship("Collection", back_populates="operations")
|
|
|
|
|
|
user = relationship("User", back_populates="operations")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class CustomField(Base):
|
|
|
|
|
|
__tablename__ = "custom_fields"
|
|
|
|
|
|
|
|
|
|
|
|
id = Column(String(36), primary_key=True, default=generate_uuid)
|
|
|
|
|
|
user_id = Column(String(36), ForeignKey("users.f99_90_id", ondelete="CASCADE"), nullable=False, index=True)
|
|
|
|
|
|
name = Column(String(100), nullable=False)
|
|
|
|
|
|
field_type = Column(String(50), default="text")
|
|
|
|
|
|
options = Column(Text, nullable=True)
|
|
|
|
|
|
required = Column(Boolean, default=False)
|
|
|
|
|
|
visible = Column(Boolean, default=True)
|
|
|
|
|
|
sort_order = Column(Integer, default=0)
|
|
|
|
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
|
|
|
|
|
|
|
|
user = relationship("User", back_populates="custom_fields")
|
2026-03-25 10:58:00 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 资讯模型
|
|
|
|
|
|
class Information(Base):
|
|
|
|
|
|
__tablename__ = "information"
|
|
|
|
|
|
|
|
|
|
|
|
id = Column(String(36), primary_key=True, default=generate_uuid)
|
|
|
|
|
|
user_id = Column(String(36), ForeignKey("users.f99_90_id", ondelete="CASCADE"), nullable=False, index=True)
|
|
|
|
|
|
|
|
|
|
|
|
# 信息类型: seek-寻配号, deal-成交数据, publish-发布
|
|
|
|
|
|
info_type = Column(String(20), nullable=False, index=True)
|
|
|
|
|
|
|
|
|
|
|
|
# 标题
|
|
|
|
|
|
title = Column(String(255), nullable=False)
|
|
|
|
|
|
|
|
|
|
|
|
# 内容描述
|
|
|
|
|
|
content = Column(Text, nullable=True)
|
|
|
|
|
|
|
|
|
|
|
|
# 关联藏品ID
|
|
|
|
|
|
collection_id = Column(String(36), ForeignKey("collections.f99_90_id", ondelete="SET NULL"), nullable=True)
|
|
|
|
|
|
|
|
|
|
|
|
# 期望条件 (寻配号用)
|
|
|
|
|
|
expect_category = Column(String(100), nullable=True) # 期望类别
|
|
|
|
|
|
expect_version = Column(String(100), nullable=True) # 期望版别
|
|
|
|
|
|
expect_packaging = Column(String(100), nullable=True) # 期望包装
|
|
|
|
|
|
expect_number = Column(String(50), nullable=True) # 期望号码
|
|
|
|
|
|
expect_price_min = Column(Float, nullable=True) # 期望价格区间
|
|
|
|
|
|
expect_price_max = Column(Float, nullable=True)
|
|
|
|
|
|
|
|
|
|
|
|
# 成交价格 (成交数据用)
|
|
|
|
|
|
deal_price = Column(Float, nullable=True)
|
|
|
|
|
|
deal_date = Column(Date, nullable=True)
|
|
|
|
|
|
|
|
|
|
|
|
# 状态: active-有效, closed-已关闭, expired-已过期
|
|
|
|
|
|
status = Column(String(20), default="active", index=True)
|
|
|
|
|
|
|
2026-03-28 08:24:08 +08:00
|
|
|
|
# 匹配状态: pending-尚未匹配, matched-已经匹配
|
|
|
|
|
|
is_matched = Column(String(20), default="pending", index=True)
|
|
|
|
|
|
|
|
|
|
|
|
# 匹配的用户ID(当用户愿意交换联系方式时)
|
|
|
|
|
|
matched_user_id = Column(String(36), ForeignKey("users.f99_90_id", ondelete="SET NULL"), nullable=True)
|
|
|
|
|
|
|
2026-03-25 10:58:00 +08:00
|
|
|
|
# 浏览/联系次数
|
|
|
|
|
|
view_count = Column(Integer, default=0)
|
|
|
|
|
|
contact_count = Column(Integer, default=0)
|
|
|
|
|
|
|
|
|
|
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now(), index=True)
|
|
|
|
|
|
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
|
|
|
|
|
|
|
|
|
|
|
user = relationship("User")
|
|
|
|
|
|
collection = relationship("Collection")
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-03-28 08:24:08 +08:00
|
|
|
|
# 资讯评论/留言
|
|
|
|
|
|
class InformationComment(Base):
|
|
|
|
|
|
__tablename__ = "information_comments"
|
|
|
|
|
|
|
|
|
|
|
|
id = Column(String(36), primary_key=True, default=generate_uuid)
|
|
|
|
|
|
information_id = Column(String(36), ForeignKey("information.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
|
|
|
|
user_id = Column(String(36), ForeignKey("users.f99_90_id", ondelete="CASCADE"), nullable=False, index=True)
|
|
|
|
|
|
content = Column(Text, nullable=False)
|
|
|
|
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
|
|
|
|
|
|
|
|
user = relationship("User")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 资讯联系方式查看记录
|
|
|
|
|
|
class InformationContactView(Base):
|
|
|
|
|
|
__tablename__ = "information_contact_views"
|
|
|
|
|
|
|
|
|
|
|
|
id = Column(String(36), primary_key=True, default=generate_uuid)
|
|
|
|
|
|
information_id = Column(String(36), ForeignKey("information.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
|
|
|
|
viewer_id = Column(String(36), ForeignKey("users.f99_90_id", ondelete="CASCADE"), nullable=False, index=True)
|
|
|
|
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
|
|
|
|
|
|
|
|
viewer = relationship("User")
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-03-25 10:58:00 +08:00
|
|
|
|
# 资讯关联用户 (收藏/点赞)
|
|
|
|
|
|
class InformationLike(Base):
|
|
|
|
|
|
__tablename__ = "information_likes"
|
|
|
|
|
|
|
|
|
|
|
|
id = Column(String(36), primary_key=True, default=generate_uuid)
|
|
|
|
|
|
information_id = Column(String(36), ForeignKey("information.id", ondelete="CASCADE"), nullable=False, index=True)
|
|
|
|
|
|
user_id = Column(String(36), ForeignKey("users.f99_90_id", ondelete="CASCADE"), nullable=False, index=True)
|
|
|
|
|
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
|
|
|
|
|
|
|
|
__table_args__ = (
|
|
|
|
|
|
UniqueConstraint('information_id', 'user_id', name='uq_information_user'),
|
|
|
|
|
|
)
|
2026-03-26 14:50:26 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 角色常量
|
|
|
|
|
|
class UserRole:
|
|
|
|
|
|
ADMIN = "admin" # 管理员:全部权限
|
|
|
|
|
|
EDITOR = "editor" # 信息员:可发布信息、管理资讯
|
|
|
|
|
|
USER = "user" # 普通用户:基本功能
|
|
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
|
def get_role_name(cls, role):
|
|
|
|
|
|
names = {
|
|
|
|
|
|
cls.ADMIN: "管理员",
|
|
|
|
|
|
cls.EDITOR: "信息员",
|
|
|
|
|
|
cls.USER: "用户"
|
|
|
|
|
|
}
|
|
|
|
|
|
return names.get(role, "用户")
|