465 lines
20 KiB
Python
465 lines
20 KiB
Python
"""模块:CRB 数据模型
|
||
|
||
3CRB(Currency/Resource/Banking)货币经济系统,分两类表:
|
||
|
||
1. **MC 插件共享表**(managed=False):由 Minecraft 服务器 CME 经济插件
|
||
直接读写,Django 仅作 ORM 映射。表名为 snake_case、列名为 snake_case、
|
||
主键为 INT 自增、user_uuid 为 BINARY(16)(MC 玩家 UUID)。
|
||
Django 通过 ``db_column`` 映射,Python 属性保持 PascalCase。
|
||
|
||
2. **Django 独有表**(managed=True):游戏内企业注册、定期储值、
|
||
GVSDS 用户↔MC 玩家映射。表名与列名均为 PascalCase,由 Django 迁移管理。
|
||
|
||
所有 ORM 字段与方法遵循帕斯卡命名法(首字母大写、缩写大写、单单词小写)。
|
||
MC 插件表因外部 schema 约束,通过 ``db_column`` 保持 DB 侧 snake_case。"""
|
||
|
||
from gvsds.model_base import QModel
|
||
import datetime
|
||
from django.db import models
|
||
from gvsds.apps.core.models import UUIDField
|
||
|
||
|
||
# ============================================================================
|
||
# MC 插件共享表(managed=False)
|
||
# 由 Minecraft 服务器 CME 经济插件读写,Django 仅作 ORM 映射,不可迁移
|
||
# ============================================================================
|
||
|
||
class CmeCurrencies(QModel):
|
||
"""CME 币种表——MC 插件定义的币种
|
||
|
||
映射真实表 ``cme_currencies``(snake_case)。
|
||
PK 为 INT 自增 ``id``,与 MC 插件 schema 一致。
|
||
"""
|
||
|
||
CurrencyID = models.AutoField(primary_key=True, db_column='id', verbose_name='币种ID')
|
||
CurrencyName = models.CharField(max_length=255, unique=True, db_column='name', verbose_name='币种名称')
|
||
CurrencySymbol = models.CharField(max_length=255, db_column='symbol', verbose_name='币种符号')
|
||
Tradable = models.IntegerField(default=1, db_column='tradable', verbose_name='是否可交易')
|
||
CreatedAt = models.DateTimeField(auto_now_add=True, db_column='created_at', null=True, blank=True, verbose_name='创建时间')
|
||
|
||
class Meta:
|
||
db_table = 'cme_currencies'
|
||
app_label = 'crb'
|
||
managed = False
|
||
verbose_name = 'CME币种'
|
||
verbose_name_plural = 'CME币种'
|
||
|
||
def __str__(self):
|
||
return self.CurrencyName
|
||
|
||
|
||
class CmeBalances(QModel):
|
||
"""CME 钱包余额表——玩家货币余额
|
||
|
||
映射真实表 ``cme_balances``(snake_case)。
|
||
``UserUUID`` 存 MC 玩家 UUID(BINARY(16)),不是 GVSDS UserUUID。
|
||
GVSDS UserUUID ↔ MC PlayerUUID 通过 ``CmeWalletGVSDSMapping`` 映射。
|
||
"""
|
||
|
||
BalanceID = models.AutoField(primary_key=True, db_column='id', verbose_name='余额ID')
|
||
UserUUID = UUIDField(db_column='user_uuid', verbose_name='MC玩家UUID')
|
||
Currency = models.ForeignKey(
|
||
CmeCurrencies, on_delete=models.DO_NOTHING,
|
||
db_column='currency_id', related_name='+',
|
||
verbose_name='币种',
|
||
)
|
||
Balance = models.FloatField(default=0, db_column='balance', verbose_name='余额')
|
||
CreatedAt = models.DateTimeField(auto_now_add=True, db_column='created_at', null=True, blank=True, verbose_name='创建时间')
|
||
UpdatedAt = models.DateTimeField(auto_now=True, db_column='updated_at', null=True, blank=True, verbose_name='更新时间')
|
||
|
||
class Meta:
|
||
db_table = 'cme_balances'
|
||
app_label = 'crb'
|
||
managed = False
|
||
verbose_name = 'CME钱包余额'
|
||
verbose_name_plural = 'CME钱包余额'
|
||
unique_together = [('UserUUID', 'Currency')]
|
||
|
||
def __str__(self):
|
||
return f'{self.UserUUID}:{self.Balance}'
|
||
|
||
|
||
class CmeTransactions(QModel):
|
||
"""CME 交易流水表——所有币种转账记录
|
||
|
||
映射真实表 ``cme_transactions``(snake_case)。
|
||
``type`` 列在 Python 中用 ``TransactionType`` 属性访问(``type`` 是保留字)。
|
||
"""
|
||
|
||
TransactionID = models.AutoField(primary_key=True, db_column='id', verbose_name='交易ID')
|
||
FromUUID = UUIDField(db_column='from_uuid', null=True, blank=True, verbose_name='付款方MC玩家UUID')
|
||
ToUUID = UUIDField(db_column='to_uuid', null=True, blank=True, verbose_name='收款方MC玩家UUID')
|
||
Currency = models.ForeignKey(
|
||
CmeCurrencies, on_delete=models.DO_NOTHING,
|
||
db_column='currency_id', related_name='+',
|
||
verbose_name='币种',
|
||
)
|
||
Amount = models.FloatField(db_column='amount', verbose_name='金额')
|
||
TransactionType = models.CharField(max_length=255, db_column='type', verbose_name='交易类型')
|
||
Description = models.CharField(max_length=255, null=True, blank=True, db_column='description', verbose_name='描述')
|
||
CreatedAt = models.DateTimeField(auto_now_add=True, db_column='created_at', null=True, blank=True, verbose_name='创建时间')
|
||
|
||
class Meta:
|
||
db_table = 'cme_transactions'
|
||
app_label = 'crb'
|
||
managed = False
|
||
verbose_name = 'CME交易流水'
|
||
verbose_name_plural = 'CME交易流水'
|
||
ordering = ['-CreatedAt']
|
||
|
||
def __str__(self):
|
||
return f'{self.TransactionType}:{self.Amount}'
|
||
|
||
|
||
class CmePaymentRequests(QModel):
|
||
"""CME 付款请求表——玩家间付款请求
|
||
|
||
映射真实表 ``cme_payment_requests``(snake_case)。
|
||
"""
|
||
|
||
RequestID = models.AutoField(primary_key=True, db_column='id', verbose_name='请求ID')
|
||
FromUUID = UUIDField(db_column='from_uuid', verbose_name='付款方MC玩家UUID')
|
||
ToUUID = UUIDField(db_column='to_uuid', verbose_name='收款方MC玩家UUID')
|
||
Currency = models.ForeignKey(
|
||
CmeCurrencies, on_delete=models.DO_NOTHING,
|
||
db_column='currency_id', related_name='+',
|
||
verbose_name='币种',
|
||
)
|
||
Amount = models.FloatField(db_column='amount', verbose_name='金额')
|
||
Status = models.CharField(max_length=255, default='pending', db_column='status', verbose_name='状态')
|
||
CreatedAt = models.DateTimeField(auto_now_add=True, db_column='created_at', null=True, blank=True, verbose_name='创建时间')
|
||
|
||
class Meta:
|
||
db_table = 'cme_payment_requests'
|
||
app_label = 'crb'
|
||
managed = False
|
||
verbose_name = 'CME付款请求'
|
||
verbose_name_plural = 'CME付款请求'
|
||
ordering = ['-CreatedAt']
|
||
|
||
def __str__(self):
|
||
return f'{self.Status}:{self.Amount}'
|
||
|
||
|
||
class CmePlayerSettings(QModel):
|
||
"""CME 玩家设置表——MC 玩家语言等偏好
|
||
|
||
映射真实表 ``cme_player_settings``(snake_case)。
|
||
"""
|
||
|
||
SettingID = models.AutoField(primary_key=True, db_column='id', verbose_name='设置ID')
|
||
UserUUID = UUIDField(db_column='user_uuid', verbose_name='MC玩家UUID')
|
||
Language = models.CharField(max_length=50, default='en_us', db_column='language', verbose_name='语言')
|
||
CreatedAt = models.DateTimeField(auto_now_add=True, db_column='created_at', null=True, blank=True, verbose_name='创建时间')
|
||
UpdatedAt = models.DateTimeField(auto_now=True, db_column='updated_at', null=True, blank=True, verbose_name='更新时间')
|
||
|
||
class Meta:
|
||
db_table = 'cme_player_settings'
|
||
app_label = 'crb'
|
||
managed = False
|
||
verbose_name = 'CME玩家设置'
|
||
verbose_name_plural = 'CME玩家设置'
|
||
|
||
def __str__(self):
|
||
return f'{self.UserUUID}:{self.Language}'
|
||
|
||
|
||
class CmeExchangeRates(QModel):
|
||
"""CME 汇率表——币种间兑换汇率
|
||
|
||
映射真实表 ``cme_exchange_rates``(snake_case)。
|
||
"""
|
||
|
||
RateID = models.AutoField(primary_key=True, db_column='id', verbose_name='汇率ID')
|
||
CurrencyName = models.CharField(max_length=255, unique=True, db_column='currency_name', verbose_name='币种名称')
|
||
Rate = models.FloatField(default=1, db_column='rate', verbose_name='汇率')
|
||
CreatedAt = models.DateTimeField(auto_now_add=True, db_column='created_at', null=True, blank=True, verbose_name='创建时间')
|
||
UpdatedAt = models.DateTimeField(auto_now=True, db_column='updated_at', null=True, blank=True, verbose_name='更新时间')
|
||
|
||
class Meta:
|
||
db_table = 'cme_exchange_rates'
|
||
app_label = 'crb'
|
||
managed = False
|
||
verbose_name = 'CME汇率'
|
||
verbose_name_plural = 'CME汇率'
|
||
|
||
def __str__(self):
|
||
return f'{self.CurrencyName}:{self.Rate}'
|
||
|
||
|
||
# ============================================================================
|
||
# Django 独有表(managed=True)
|
||
# 游戏内企业注册、定期储值、GVSDS↔MC 玩家映射,由 Django 迁移管理
|
||
# ============================================================================
|
||
|
||
class CmeStoreValue(QModel):
|
||
"""CME 储值表——定期存款记录(GVSDS 业务,非 MC 插件)
|
||
|
||
玩家将游戏币存入定期,按游戏日计息,到期取现。
|
||
业务逻辑见旧 flask 的 Cash.api(计算游戏天数与利息)。
|
||
"""
|
||
|
||
StoreValueUUID = UUIDField(primary_key=True, verbose_name='储值UUID')
|
||
UserUUID = UUIDField(null=True, blank=True, verbose_name='用户UUID')
|
||
PlayerUUID = UUIDField(null=True, blank=True, verbose_name='玩家UUID')
|
||
PlayerName = models.TextField(null=True, blank=True, verbose_name='玩家名称')
|
||
StoreValueCreateDateTime = models.DateTimeField(null=True, blank=True, verbose_name='储值创建时间')
|
||
StoreValueType = models.IntegerField(null=True, blank=True, verbose_name='储值类型')
|
||
StoreValueMin = models.BigIntegerField(null=True, blank=True, verbose_name='储值最小值')
|
||
StoreValueDayInterestRate = models.SmallIntegerField(null=True, blank=True, verbose_name='日利率')
|
||
StoreValueAmount = models.BigIntegerField(null=True, blank=True, verbose_name='储值金额')
|
||
StoreValueExpirationGameDate = models.IntegerField(null=True, blank=True, verbose_name='到期游戏日期')
|
||
|
||
class Meta:
|
||
db_table = 'storevalue'
|
||
app_label = 'crb'
|
||
verbose_name = 'CME储值'
|
||
verbose_name_plural = 'CME储值'
|
||
|
||
|
||
class CmeWalletGVSDSMapping(QModel):
|
||
"""GVSDS 钱包映射表——GVSDS 用户与 MC 玩家关联
|
||
|
||
``UserUUID`` 为 GVSDS UserUUID(BINARY(16)),
|
||
``PlayerUUID`` 为 MC 玩家 UUID 字符串形式(varchar(36))。
|
||
用于 Web 端用户查询其 MC 钱包余额。
|
||
"""
|
||
|
||
MappingID = models.BigAutoField(primary_key=True, verbose_name='映射ID')
|
||
UserUUID = UUIDField(verbose_name='GVSDS用户UUID')
|
||
PlayerUUID = models.CharField(max_length=36, verbose_name='MC玩家UUID字符串')
|
||
|
||
class Meta:
|
||
db_table = 'cmewalletgvsds'
|
||
app_label = 'crb'
|
||
verbose_name = 'GVSDS钱包映射'
|
||
verbose_name_plural = 'GVSDS钱包映射'
|
||
unique_together = [('UserUUID', 'PlayerUUID')]
|
||
|
||
|
||
class Enterprise(QModel):
|
||
"""企业表——游戏内企业注册信息(GVSDS 业务,非 MC 插件)
|
||
|
||
注:旧表有拼写错误 ``EnterpriseAddres``(少 s),已修正为 ``EnterpriseAddress``。
|
||
"""
|
||
|
||
EnterpriseUUID = UUIDField(primary_key=True, verbose_name='企业UUID')
|
||
EnterpriseCreateCertificationCode = UUIDField(null=True, blank=True, verbose_name='企业认证码')
|
||
EnterpriseFatherUUID = UUIDField(null=True, blank=True, verbose_name='上级企业UUID')
|
||
EnterpriseName = models.TextField(null=True, blank=True, verbose_name='企业名称')
|
||
EnterpriseType = models.IntegerField(null=True, blank=True, verbose_name='企业类型')
|
||
EnterpriseStatus = models.IntegerField(null=True, blank=True, verbose_name='企业状态')
|
||
EnterpriseCreateTime = models.DateTimeField(null=True, blank=True, verbose_name='企业创建时间')
|
||
EnterpriseRegisteredCapital = models.FloatField(null=True, blank=True, verbose_name='注册资本')
|
||
EnterpriseCapitalGC = models.TextField(null=True, blank=True, verbose_name='资本GC')
|
||
EnterpriseCapital = models.FloatField(null=True, blank=True, verbose_name='资本')
|
||
EnterpriseTelephone = models.TextField(null=True, blank=True, verbose_name='企业电话')
|
||
EnterpriseEmail = models.TextField(null=True, blank=True, verbose_name='企业邮箱')
|
||
EnterpriseWebsite = models.TextField(null=True, blank=True, verbose_name='企业网站')
|
||
EnterpriseAddress = models.TextField(null=True, blank=True, verbose_name='企业地址')
|
||
EnterpriseDesc = models.TextField(null=True, blank=True, verbose_name='企业描述')
|
||
EnterpriseAdministrativeDivisions = models.TextField(null=True, blank=True, verbose_name='行政区划')
|
||
EnterpriseBusinessPeriod = models.BigIntegerField(null=True, blank=True, verbose_name='营业期限')
|
||
EnterpriseApprovalDate = models.DateTimeField(null=True, blank=True, verbose_name='审批日期')
|
||
EnterpriseBusinessScope = models.JSONField(null=True, blank=True, verbose_name='经营范围')
|
||
|
||
class Meta:
|
||
db_table = 'enterprise'
|
||
app_label = 'crb'
|
||
verbose_name = '企业'
|
||
verbose_name_plural = '企业'
|
||
|
||
def __str__(self):
|
||
return self.EnterpriseName or str(self.EnterpriseUUID)
|
||
|
||
|
||
class EnterpriseUser(QModel):
|
||
"""企业用户关联表——游戏内企业成员(GVSDS 业务,非 MC 插件)"""
|
||
|
||
EnterpriseSMTMUUID = UUIDField(primary_key=True, verbose_name='企业用户UUID')
|
||
EnterpriseUUID = models.ForeignKey(
|
||
Enterprise, on_delete=models.CASCADE,
|
||
to_field='EnterpriseUUID',
|
||
related_name='+', null=True, blank=True,
|
||
verbose_name='企业',
|
||
)
|
||
UserUUID = UUIDField(null=True, blank=True, verbose_name='用户UUID')
|
||
EnterpriseUserType = models.IntegerField(null=True, blank=True, verbose_name='企业用户类型')
|
||
|
||
class Meta:
|
||
db_table = 'enterpriseuser'
|
||
app_label = 'crb'
|
||
verbose_name = '企业用户'
|
||
verbose_name_plural = '企业用户'
|
||
|
||
|
||
# ============================================================================
|
||
# 商店系统(Django 独有表,managed=True)
|
||
# 淘宝/拼多多风格的游戏内商店:商品/购物车/订单/订单项
|
||
# 全部走 minecraft 库(crb app 路由),GC 虚拟币支付
|
||
# ============================================================================
|
||
|
||
# 订单状态枚举(与旧 flask 数字兼容,简化版状态机)
|
||
# 0=待付款 → 1=已付款(待发货) → 2=已发货(待收货) → 3=已完成
|
||
# 0/1 → 4=已取消
|
||
# 1/2 → 5=退款中 → 6=已退款
|
||
SHOP_ORDER_PENDING = 0
|
||
SHOP_ORDER_PAID = 1
|
||
SHOP_ORDER_SHIPPED = 2
|
||
SHOP_ORDER_COMPLETED = 3
|
||
SHOP_ORDER_CANCELLED = 4
|
||
SHOP_ORDER_REFUNDING = 5
|
||
SHOP_ORDER_REFUNDED = 6
|
||
|
||
SHOP_ORDER_STATUS_CHOICES = [
|
||
(SHOP_ORDER_PENDING, '待付款'),
|
||
(SHOP_ORDER_PAID, '已付款'),
|
||
(SHOP_ORDER_SHIPPED, '已发货'),
|
||
(SHOP_ORDER_COMPLETED, '已完成'),
|
||
(SHOP_ORDER_CANCELLED, '已取消'),
|
||
(SHOP_ORDER_REFUNDING, '退款中'),
|
||
(SHOP_ORDER_REFUNDED, '已退款'),
|
||
]
|
||
|
||
# 商品状态
|
||
SHOP_PRODUCT_OFFLINE = 0 # 下架
|
||
SHOP_PRODUCT_ONLINE = 1 # 在售
|
||
SHOP_PRODUCT_OUT_OF_STOCK = 2 # 缺货
|
||
|
||
|
||
class ShopProduct(QModel):
|
||
"""商店商品表——游戏内商品定义(GVSDS 业务,非 MC 插件)
|
||
|
||
设计说明:
|
||
- 单一规格(无 SPU/SKU 多规格),一个商品一条记录
|
||
- 价格以 GC 计价(CurrencyName='GC')
|
||
- 图片走 FileAsset 系统的 SHA1 引用,存 ShopProductImage 列为 SHA1 列表
|
||
- Stock=-1 表示无限库存
|
||
"""
|
||
|
||
ProductUUID = UUIDField(primary_key=True, verbose_name='商品UUID')
|
||
SellerUserUUID = UUIDField(verbose_name='卖家GVSDS用户UUID')
|
||
SellerPlayerName = models.CharField(max_length=64, null=True, blank=True, verbose_name='卖家玩家名')
|
||
ProductName = models.CharField(max_length=128, verbose_name='商品名称')
|
||
ProductDesc = models.TextField(null=True, blank=True, verbose_name='商品描述')
|
||
ProductPrice = models.FloatField(default=0, verbose_name='商品价格(GC)')
|
||
Stock = models.BigIntegerField(default=-1, verbose_name='库存(-1=无限)')
|
||
SoldCount = models.BigIntegerField(default=0, verbose_name='已售数量')
|
||
ProductImages = models.JSONField(default=list, blank=True, verbose_name='商品图片SHA1列表')
|
||
ProductTags = models.JSONField(default=list, blank=True, verbose_name='商品标签')
|
||
ProductCategory = models.CharField(max_length=64, null=True, blank=True, verbose_name='商品分类')
|
||
ProductStatus = models.SmallIntegerField(default=SHOP_PRODUCT_ONLINE, verbose_name='商品状态')
|
||
CreateTime = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||
UpdateTime = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||
|
||
class Meta:
|
||
db_table = 'shop_product'
|
||
app_label = 'crb'
|
||
verbose_name = '商店商品'
|
||
verbose_name_plural = '商店商品'
|
||
ordering = ['-CreateTime']
|
||
|
||
def __str__(self):
|
||
return self.ProductName
|
||
|
||
|
||
class ShopCartItem(QModel):
|
||
"""购物车项——用户加入购物车的商品(GVSDS 业务)
|
||
|
||
UserUUID 为 GVSDS UserUUID(不是 MC 玩家 UUID)。
|
||
"""
|
||
|
||
CartItemUUID = UUIDField(primary_key=True, verbose_name='购物车项UUID')
|
||
UserUUID = UUIDField(verbose_name='GVSDS用户UUID')
|
||
Product = models.ForeignKey(
|
||
ShopProduct, on_delete=models.CASCADE,
|
||
to_field='ProductUUID',
|
||
related_name='+',
|
||
verbose_name='商品',
|
||
)
|
||
Quantity = models.BigIntegerField(default=1, verbose_name='数量')
|
||
CreateTime = models.DateTimeField(auto_now_add=True, verbose_name='加入时间')
|
||
UpdateTime = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||
|
||
class Meta:
|
||
db_table = 'shop_cart_item'
|
||
app_label = 'crb'
|
||
verbose_name = '购物车项'
|
||
verbose_name_plural = '购物车项'
|
||
# 同一用户同一商品唯一(避免重复加购)
|
||
unique_together = [('UserUUID', 'Product')]
|
||
|
||
|
||
class ShopOrder(QModel):
|
||
"""商店订单表——订单主表(GVSDS 业务,非 MC 插件)
|
||
|
||
状态机:
|
||
PENDING(0) → PAID(1) → SHIPPED(2) → COMPLETED(3)
|
||
PENDING(0) / PAID(1) → CANCELLED(4)
|
||
PAID(1) / SHIPPED(2) → REFUNDING(5) → REFUNDED(6)
|
||
|
||
BuyerUserUUID 为 GVSDS UserUUID,BuyerPlayerUUID 为 MC 玩家 UUID(BINARY16)。
|
||
订单号 OrderNO 格式:S + YYYYMMDDHHMMSS + 6位随机数。
|
||
"""
|
||
|
||
OrderUUID = UUIDField(primary_key=True, verbose_name='订单UUID')
|
||
OrderNO = models.CharField(max_length=64, unique=True, verbose_name='订单编号')
|
||
BuyerUserUUID = UUIDField(verbose_name='买家GVSDS用户UUID')
|
||
BuyerPlayerUUID = UUIDField(verbose_name='买家MC玩家UUID')
|
||
BuyerPlayerName = models.CharField(max_length=64, null=True, blank=True, verbose_name='买家玩家名')
|
||
TotalAmount = models.FloatField(default=0, verbose_name='订单总金额(GC)')
|
||
OrderStatus = models.SmallIntegerField(
|
||
default=SHOP_ORDER_PENDING, choices=SHOP_ORDER_STATUS_CHOICES,
|
||
verbose_name='订单状态',
|
||
)
|
||
Remark = models.CharField(max_length=512, null=True, blank=True, verbose_name='订单备注')
|
||
PayTime = models.DateTimeField(null=True, blank=True, verbose_name='付款时间')
|
||
ShipTime = models.DateTimeField(null=True, blank=True, verbose_name='发货时间')
|
||
CompleteTime = models.DateTimeField(null=True, blank=True, verbose_name='完成时间')
|
||
CancelTime = models.DateTimeField(null=True, blank=True, verbose_name='取消时间')
|
||
RefundTime = models.DateTimeField(null=True, blank=True, verbose_name='退款时间')
|
||
RefundReason = models.CharField(max_length=512, null=True, blank=True, verbose_name='退款原因')
|
||
CreateTime = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
|
||
UpdateTime = models.DateTimeField(auto_now=True, verbose_name='更新时间')
|
||
|
||
class Meta:
|
||
db_table = 'shop_order'
|
||
app_label = 'crb'
|
||
verbose_name = '商店订单'
|
||
verbose_name_plural = '商店订单'
|
||
ordering = ['-CreateTime']
|
||
|
||
def __str__(self):
|
||
return self.OrderNO
|
||
|
||
|
||
class ShopOrderItem(QModel):
|
||
"""商店订单项——订单的商品明细(含商品快照)
|
||
|
||
商品快照:下单时冻结 ProductName/Price/Image,避免商品后续修改影响历史订单。
|
||
"""
|
||
|
||
OrderItemUUID = UUIDField(primary_key=True, verbose_name='订单项UUID')
|
||
Order = models.ForeignKey(
|
||
ShopOrder, on_delete=models.CASCADE,
|
||
to_field='OrderUUID',
|
||
related_name='items',
|
||
verbose_name='订单',
|
||
)
|
||
Product = models.ForeignKey(
|
||
ShopProduct, on_delete=models.SET_NULL,
|
||
to_field='ProductUUID', null=True, blank=True,
|
||
related_name='+',
|
||
verbose_name='商品',
|
||
)
|
||
ProductSnapshot = models.JSONField(verbose_name='商品快照')
|
||
Quantity = models.BigIntegerField(default=1, verbose_name='购买数量')
|
||
UnitPrice = models.FloatField(default=0, verbose_name='单价(GC)')
|
||
Subtotal = models.FloatField(default=0, verbose_name='小计(GC)')
|
||
|
||
class Meta:
|
||
db_table = 'shop_order_item'
|
||
app_label = 'crb'
|
||
verbose_name = '商店订单项'
|
||
verbose_name_plural = '商店订单项'
|