Files
X25020501_card_book/core/db/models.py

324 lines
14 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.

# -*- coding = utf-8 -*-
# @Time : 2025/2/13 下午12:17
# @File : models.py
# @Software : PyCharm
# @Author : xingc
# @Desc :
import enum
import uuid
from datetime import datetime, date, timedelta
from typing import List
import ujson
from sqlalchemy import (
Column, Integer, String, Text, Date,
ForeignKey, DECIMAL, CheckConstraint,
UniqueConstraint, Index, Sequence, JSON, Enum, Boolean, UUID, Table
)
from sqlalchemy.orm import (
DeclarativeBase, Mapped, mapped_column, relationship
)
from sqlalchemy.sql import func
from sqlalchemy.ext.asyncio import AsyncAttrs
from core.base import schema as base_schema
class BaseModel(DeclarativeBase, AsyncAttrs):
id: Mapped[int] = mapped_column(
Integer, primary_key=True, autoincrement=True, comment='自增id'
)
def dict(self, exclude: set = None):
data = {}
for column in self.__table__.columns:
if exclude is not None and column.name in exclude:
continue
value = getattr(self, column.name)
if isinstance(value, datetime):
data[column.name] = value.strftime('%Y-%m-%d %H:%M:%S')
elif isinstance(value, enum.Enum):
data[column.name] = value.value
else:
data[column.name] = value
return data
def json(self, exclude: set = None):
return ujson.dumps(self.dict(exclude=exclude))
class DateModel:
created_at: Mapped[datetime] = mapped_column(server_default=func.current_timestamp(), comment='创建时间')
updated_at: Mapped[datetime] = mapped_column(server_default=func.current_timestamp(),
onupdate=func.current_timestamp(), comment='更新时间')
def set_default_vip_expire_date() -> date:
expire_date = (datetime.now() - timedelta(days=1))
return expire_date.date()
class User(BaseModel, DateModel):
__tablename__ = 'system_users'
__table_args__ = (
Index('idx_wechat_openid', 'wechat_openid'),
UniqueConstraint('user_id', name='uniq_user_id'),
{'comment': '用户表'}
)
id = None
user_id: Mapped[int] = mapped_column(
Integer, Sequence('non_primary_id_seq', start=100000, increment=1), primary_key=True, autoincrement=True,
comment='用户id'
)
nickname: Mapped[str] = mapped_column(String(20), comment='昵称')
gender: Mapped[int] = mapped_column(Integer, nullable=True, comment='性别')
avatar_url: Mapped[str] = mapped_column(String(500), nullable=True, comment='头像链接')
wechat_openid: Mapped[str] = mapped_column(String(100), comment='微信openid')
wechat_union_id: Mapped[str] = mapped_column(String(100), nullable=True, comment='微信unionid')
phone: Mapped[str] = mapped_column(String(11), nullable=True, comment='手机号码')
vip_expire_date: Mapped[date] = mapped_column(Date, default=set_default_vip_expire_date, comment='会员到期时间')
points: Mapped[int] = mapped_column(Integer, default=0, comment='会员积分')
collections: Mapped[list['UserCollection']] = relationship(
back_populates='user',
cascade='all, delete-orphan'
)
# 关注者
following: Mapped[list['UserFollows']] = relationship(
'UserFollows',
foreign_keys='UserFollows.follower_id',
cascade='all, delete-orphan'
)
# 定义被关注关系
followers: Mapped[list['UserFollows']] = relationship(
'UserFollows',
foreign_keys='UserFollows.followed_id',
cascade='all, delete-orphan'
)
chats: Mapped[List['Chat']] = relationship(secondary='system_chat_participant', back_populates='users')
messages: Mapped[List['Message']] = relationship(back_populates='user')
chat_participants: Mapped[List['ChatParticipant']] = relationship(back_populates='user')
def __str__(self):
return f'{self.user_id}'
def is_vip(self) -> bool:
return self.vip_expire_date >= datetime.now().date()
class UserFollows(BaseModel, DateModel):
__tablename__ = 'system_user_followers'
__table_args__ = (
{'comment': '用户关注关系表'}
)
follower_id: Mapped[int] = mapped_column(
Integer, ForeignKey('system_users.user_id', ondelete='CASCADE'), comment='关注者的用户id'
)
followed_id: Mapped[int] = mapped_column(
Integer, ForeignKey('system_users.user_id', ondelete='CASCADE'), comment='被关注者的用户id'
)
follower = relationship('User', foreign_keys=[follower_id], back_populates='following')
followed = relationship('User', foreign_keys=[followed_id], back_populates='followers')
def __repr__(self):
return f'<UserFollows(follower_id={self.follower_id}, followed_id={self.followed_id})>'
class Card(BaseModel, DateModel):
__tablename__ = 'system_cards'
__table_args__ = (
Index('idx_cert_number_category_source', 'cert_number', 'category', 'source'),
{'comment': '卡片表'}
)
cert_number: Mapped[str] = mapped_column(String(50), comment='评级卡号')
card_year: Mapped[str] = mapped_column(String(50), nullable=True, comment='卡片年份')
card_brand: Mapped[str] = mapped_column(String(255), nullable=True, comment='卡片品牌')
card_score: Mapped[float] = mapped_column(DECIMAL(10, 1), nullable=True, comment='卡片评分')
auto_score: Mapped[float] = mapped_column(DECIMAL(10, 1), nullable=True, comment='签字评分')
card_name: Mapped[str] = mapped_column(String(225), nullable=True, comment='卡片名称')
card_number: Mapped[str] = mapped_column(String(225), nullable=True, comment='卡片编号')
card_type: Mapped[str] = mapped_column(String(20), nullable=True, comment='卡片类型')
card_picture: Mapped[dict] = mapped_column(JSON, default=lambda: dict(), comment='卡片正、反图片(本地)')
card_spare_picture: Mapped[dict] = mapped_column(JSON, default=lambda: dict(), comment='卡片正、反图片(远程)')
create_user_id: Mapped[int] = mapped_column(Integer, nullable=True, comment='创建者')
perfect_user_id: Mapped[int] = mapped_column(Integer, nullable=True, comment='完善者')
category: Mapped[base_schema.CardCategoryType] = mapped_column(
Enum(base_schema.CardCategoryType, inherit_schema=True),
nullable=True, server_default=base_schema.CardCategoryType.OTHER.value, comment='卡片分类'
)
source: Mapped[base_schema.PlatformSourceType] = mapped_column(
Enum(base_schema.PlatformSourceType, inherit_schema=True),
comment='来源PSA、BGS、CGC、GBTC、CCG、BCTC、CIC :采集 、OTHER自定义'
)
diy_source: Mapped[str] = mapped_column(String(50), nullable=True, comment='自定义品牌名')
collections: Mapped[list['UserCollection']] = relationship(
back_populates='card',
cascade='all, delete-orphan'
)
class UserCollection(BaseModel, DateModel):
__tablename__ = 'system_user_collections'
__table_args__ = (
UniqueConstraint('user_id', 'card_id', name='uniq_user_card'),
CheckConstraint('purchase_price >= 0', name='check_purchase_price'),
CheckConstraint('display_value >= 0', name='check_display_price'),
{'comment': '卡片收藏关联表'}
)
user_id: Mapped[int] = mapped_column(
Integer, ForeignKey('system_users.user_id', ondelete='CASCADE'),
nullable=False
)
card_id: Mapped[int] = mapped_column(
Integer, ForeignKey('system_cards.id', ondelete='CASCADE'),
nullable=False
)
purchase_price: Mapped[float] = mapped_column(
DECIMAL(10, 2), default=0
)
display_price: Mapped[float] = mapped_column(
DECIMAL(10, 2), default=0
)
displayed: Mapped[bool] = mapped_column(
Boolean, default=False
)
user: Mapped['User'] = relationship(back_populates='collections')
card: Mapped['Card'] = relationship(back_populates='collections')
class SearchCardCache(BaseModel, DateModel):
__tablename__ = 'system_cache_cards'
__table_args__ = (
Index('idx_cert_and_card_number_source', 'cert_number', 'source'),
{'comment': '搜索卡片信息缓存表'}
)
cert_number: Mapped[str] = mapped_column(String(50), nullable=False, comment='评级卡号')
data: Mapped[dict] = mapped_column(JSON, comment='标准数据')
raw_data: Mapped[dict] = mapped_column(JSON, comment='原始数据')
source: Mapped[base_schema.SupportPlatformType] = mapped_column(
Enum(base_schema.SupportPlatformType, inherit_schema=True),
comment='数据来源PSA、BGS、CGC、GBTC、CCG、BCTC、CIC'
)
class Product(BaseModel, DateModel):
__tablename__ = 'system_products'
__table_args__ = {'comment': '产品信息表'}
name: Mapped[str] = mapped_column(String(20), comment='产品名')
category: Mapped[base_schema.ProductCategory] = mapped_column(
Enum(base_schema.ProductCategory, inherit_schema=True),
comment='商品分类, vip-会员服务 points-积分服务 gift-礼物'
)
price: Mapped[int] = mapped_column(Integer, comment='价格,不带小数点 单位为分')
action: Mapped[base_schema.ProductAction] = mapped_column(
Enum(base_schema.ProductAction, inherit_schema=True),
comment='付款后的动作会员续期vip_renewal 积分recharge_points'
)
number: Mapped[int] = mapped_column(Integer, comment='执行动作的增量值')
gift_id: Mapped[int] = mapped_column(Integer, nullable=True, comment='礼物产品id')
description: Mapped[str] = mapped_column(Text, nullable=True, comment='商品描述')
class Order(BaseModel, DateModel):
__tablename__ = 'system_orders'
__table_args__ = (
Index('idx_order_on_order_id', 'order_id'),
{'comment': '产品订单表'}
)
order_id: Mapped[str] = mapped_column(String(32), comment='订单id')
transaction_id: Mapped[str] = mapped_column(String(32), nullable=True, comment='微信订单id')
user_id: Mapped[int] = mapped_column(Integer, comment='用户id')
product_id: Mapped[int] = mapped_column(Integer, comment='产品id')
total_fee: Mapped[int] = mapped_column(Integer, nullable=True, comment='发起的总金额,单位为分')
notify_total_fee: Mapped[int] = mapped_column(Integer, nullable=True, comment='回调成功支付的总金额,单位为分')
pay_url: Mapped[str] = mapped_column(String(255), nullable=True, comment='支付地址')
status: Mapped[base_schema.OrderStatusType] = mapped_column(
Enum(base_schema.OrderStatusType, inherit_schema=True),
server_default=base_schema.OrderStatusType.INIT.value,
comment='订单状态init初始化 fail失败 ok完成 wait payment待支付 paid已支付 cancel. 取消'
)
ip_address: Mapped[str] = mapped_column(String(255), nullable=True, comment='操作ip')
class Chat(BaseModel, DateModel):
__tablename__ = 'system_chats'
__table_args__ = (
Index('idx_chat_on_guid', 'guid'),
{'comment': '聊天表'}
)
guid: Mapped[uuid.UUID] = mapped_column(String(36), nullable=True, default=lambda: str(uuid.uuid4()))
users: Mapped[List['User']] = relationship(secondary='system_chat_participant', back_populates='chats')
messages: Mapped[List['Message']] = relationship(back_populates='chat', cascade='all,delete')
is_deleted: Mapped[bool] = mapped_column(Boolean, default=False)
chat_participants: Mapped[List['ChatParticipant']] = relationship(back_populates='chat', cascade='all,delete')
class Message(BaseModel, DateModel):
__tablename__ = 'system_messages'
__table_args__ = (
Index('idx_message_on_user_id', 'user_id'),
Index('idx_message_on_chat_id', 'chat_id'),
Index('idx_message_on_user_id_chat_id', 'chat_id', 'user_id'),
{'comment': '聊天消息表'}
)
guid: Mapped[uuid.UUID] = mapped_column(String(36), nullable=True, default=lambda: str(uuid.uuid4()))
user_id: Mapped[int] = mapped_column(ForeignKey('system_users.user_id'))
chat_id: Mapped[int] = mapped_column(ForeignKey('system_chats.id'))
message_type: Mapped[str] = mapped_column(
Enum(base_schema.MessageType, inherit_schema=True), default=base_schema.MessageType.TEXT
)
content: Mapped[str] = mapped_column(String(5000), nullable=True, comment='文本消息')
chat: Mapped['Chat'] = relationship(back_populates='messages')
user: Mapped['User'] = relationship(back_populates='messages')
def __str__(self):
return f'{self.content}'
class PointsTransaction(BaseModel, DateModel):
__tablename__ = 'system_points_transaction'
__table_args__ = (
{'comment': '积分消费记录表'}
)
user_id: Mapped[int] = mapped_column(Integer, comment='用户id')
transaction_type: Mapped[int] = mapped_column(Integer, comment='交易类型1-充值 2-消费 3-赠送 4-过期 5-系统调整')
product_id: Mapped[int] = mapped_column(Integer, nullable=True, comment='关联产品id')
points: Mapped[int] = mapped_column(Integer, comment='积分数量(正数表示增加,负数表示减少)')
balance: Mapped[int] = mapped_column(Integer, comment='交易后余额')
status: Mapped[int] = mapped_column(Integer, default=1, comment='状态0-无效 1-有效 2-已撤销')
description: Mapped[str] = mapped_column(String(255), nullable=True, comment='交易描述')
class ChatParticipant(BaseModel):
__tablename__ = 'system_chat_participant'
__table_args__ = (
{'comment': '聊天参与者关联表'}
)
id: None = None
user_id: Mapped[int] = mapped_column(Integer, ForeignKey('system_users.user_id'), primary_key=True)
chat_id: Mapped[int] = mapped_column(Integer, ForeignKey('system_chats.id'), primary_key=True)
is_deleted: Mapped[bool] = mapped_column(Boolean, default=False)
last_read_message_id: Mapped[int] = mapped_column(default=0, nullable=True)
last_del_message_id: Mapped[int] = mapped_column(default=0, nullable=True)
chat: Mapped['Chat'] = relationship(back_populates='chat_participants')
user: Mapped['User'] = relationship(back_populates='chat_participants')