"""模块:订单 SAAS 数据模型 SAAS 级订单模板与订单实例——支持等级定价、可选绑定支付网关。 设计原则: 1. 模板驱动:通过 OrderTemplate 定义订单结构,子服务基于模板快速创建订单 2. 等级定价:OrderLevel 定义不同等级的价格/权益,订单可关联等级 3. 支付可选:订单可选绑定 PaymentGatewayConfig,不绑定则不涉及支付 4. 不可变快照:订单创建后,模板变更不影响已有订单 """ import datetime from django.db import models from gvsdsdk.model_base import QModel from gvsdsdk.models import UUIDField # === 订单等级表 === class OrderLevel(QModel): """订单等级表——定义订单模板的定价阶梯 例如:VIP 月卡、VIP 季卡、SVIP 年卡等。 每个等级属于一个订单模板,有独立的价格和有效期。 """ LevelUUID = UUIDField(primary_key=True, verbose_name='等级UUID') Template = models.ForeignKey( 'OrderTemplate', on_delete=models.CASCADE, related_name='Levels', verbose_name='订单模板', ) LevelCode = models.CharField(max_length=64, verbose_name='等级编码') LevelName = models.CharField(max_length=128, verbose_name='等级名称') LevelDescription = models.CharField(max_length=512, default='', verbose_name='等级描述') Price = models.DecimalField(max_digits=18, decimal_places=4, default=0, verbose_name='价格') OriginalPrice = models.DecimalField(max_digits=18, decimal_places=4, default=0, verbose_name='原价') Currency = models.CharField(max_length=10, default='CNY', verbose_name='币种') DurationDays = models.IntegerField(default=0, verbose_name='有效天数') DurationMonths = models.IntegerField(default=0, verbose_name='有效月数') LevelSort = models.SmallIntegerField(default=0, verbose_name='排序') LevelStatus = models.SmallIntegerField(default=1, verbose_name='等级状态') LevelMetadata = models.JSONField(default=dict, verbose_name='等级元数据') CreateTime = models.DateTimeField(default=datetime.datetime.utcnow, verbose_name='创建时间') UpdateTime = models.DateTimeField(null=True, blank=True, verbose_name='更新时间') class Meta: db_table = 'OrderLevel' app_label = 'gvsdsdk' verbose_name = '订单等级' verbose_name_plural = '订单等级' managed = False unique_together = [('Template', 'LevelCode')] ordering = ['LevelSort'] indexes = [ models.Index(fields=['Template', 'LevelStatus'], name='idx_ordlvl_template_status'), ] def __str__(self): return f'{self.LevelName}({self.LevelCode})' @property def IsActive(self): return self.LevelStatus == 1 def GetDuration(self): """获取有效时长(天)""" return self.DurationDays + self.DurationMonths * 30 # === 订单模板表 === class OrderTemplate(QModel): """订单模板表——定义订单的结构与配置 每个模板属于一个租户(TenantUUID),可选关联一个公司(CompanyUUID)。 模板定义了订单的基本信息、支付配置、等级设置等。 子服务通过模板快速创建标准化的订单。 """ TPL_SERVICE = 'service' TPL_GOODS = 'goods' TPL_MEMBERSHIP = 'membership' TPL_RECHARGE = 'recharge' TPL_CUSTOM = 'custom' TPL_TYPE_CHOICES = [ (TPL_SERVICE, '服务'), (TPL_GOODS, '商品'), (TPL_MEMBERSHIP, '会员'), (TPL_RECHARGE, '充值'), (TPL_CUSTOM, '自定义'), ] TPL_DRAFT = 0 TPL_ACTIVE = 1 TPL_ARCHIVED = 2 TPL_STATUS_CHOICES = [ (TPL_DRAFT, '草稿'), (TPL_ACTIVE, '启用'), (TPL_ARCHIVED, '归档'), ] TemplateUUID = UUIDField(primary_key=True, verbose_name='模板UUID') TenantUUID = UUIDField(verbose_name='租户UUID') CompanyUUID = UUIDField(null=True, blank=True, verbose_name='公司UUID') TemplateCode = models.CharField(max_length=64, verbose_name='模板编码') TemplateName = models.CharField(max_length=256, verbose_name='模板名称') TemplateDescription = models.CharField(max_length=512, default='', verbose_name='模板描述') TemplateType = models.CharField(max_length=32, choices=TPL_TYPE_CHOICES, verbose_name='模板类型') TemplateStatus = models.SmallIntegerField( default=TPL_DRAFT, choices=TPL_STATUS_CHOICES, verbose_name='模板状态', ) # 可选绑定支付网关 PaymentConfig = models.ForeignKey( 'payment.PaymentGatewayConfig', on_delete=models.SET_NULL, null=True, blank=True, related_name='order_templates', verbose_name='支付网关配置', ) # 订单级配置 RequirePayment = models.BooleanField(default=True, verbose_name='需要支付') AllowRefund = models.BooleanField(default=False, verbose_name='允许退款') RefundPolicy = models.JSONField(default=dict, verbose_name='退款策略') AutoConfirm = models.BooleanField(default=False, verbose_name='自动确认') ConfirmTimeoutSeconds = models.IntegerField(default=0, verbose_name='确认超时秒数') # 模板元数据(扩展字段) TemplateMetadata = models.JSONField(default=dict, verbose_name='模板元数据') TemplateSort = models.SmallIntegerField(default=0, verbose_name='排序') CreateTime = models.DateTimeField(default=datetime.datetime.utcnow, verbose_name='创建时间') UpdateTime = models.DateTimeField(null=True, blank=True, verbose_name='更新时间') class Meta: db_table = 'OrderTemplate' app_label = 'gvsdsdk' verbose_name = '订单模板' verbose_name_plural = '订单模板' managed = False unique_together = [('TenantUUID', 'TemplateCode')] ordering = ['TemplateSort', '-CreateTime'] indexes = [ models.Index(fields=['TenantUUID', 'TemplateStatus'], name='idx_ordtpl_tenant_status'), models.Index(fields=['TemplateType'], name='idx_ordtpl_type'), ] def __str__(self): return f'{self.TemplateName}({self.TemplateCode})' @property def IsActive(self): return self.TemplateStatus == self.TPL_ACTIVE def GetLevels(self): """获取模板的所有等级""" return self.Levels.filter(LevelStatus=1).order_by('LevelSort') def GetItems(self): """获取模板的所有行项目定义""" return self.Items.filter(ItemStatus=1).order_by('ItemSort') # === 订单模板项表 === class OrderTemplateItem(QModel): """订单模板项表——定义模板中的行项目 每个行项目代表订单中的一个条目,如商品、服务项等。 创建订单时,模板项会被复制到订单项中。 """ ITEM_MANDATORY = 0 ITEM_OPTIONAL = 1 ITEM_OPTIONALITY_CHOICES = [ (ITEM_MANDATORY, '必选'), (ITEM_OPTIONAL, '可选'), ] ItemUUID = UUIDField(primary_key=True, verbose_name='模板项UUID') Template = models.ForeignKey( OrderTemplate, on_delete=models.CASCADE, related_name='Items', verbose_name='订单模板', ) ItemCode = models.CharField(max_length=64, verbose_name='项目编码') ItemName = models.CharField(max_length=256, verbose_name='项目名称') ItemDescription = models.CharField(max_length=512, default='', verbose_name='项目描述') ItemType = models.CharField(max_length=32, default='default', verbose_name='项目类型') UnitPrice = models.DecimalField(max_digits=18, decimal_places=4, default=0, verbose_name='单价') Currency = models.CharField(max_length=10, default='CNY', verbose_name='币种') DefaultQuantity = models.IntegerField(default=1, verbose_name='默认数量') MinQuantity = models.IntegerField(default=1, verbose_name='最小数量') MaxQuantity = models.IntegerField(default=999, verbose_name='最大数量') Optionality = models.SmallIntegerField( default=ITEM_MANDATORY, choices=ITEM_OPTIONALITY_CHOICES, verbose_name='是否可选', ) ItemSort = models.SmallIntegerField(default=0, verbose_name='排序') ItemStatus = models.SmallIntegerField(default=1, verbose_name='项目状态') ItemMetadata = models.JSONField(default=dict, verbose_name='项目元数据') CreateTime = models.DateTimeField(default=datetime.datetime.utcnow, verbose_name='创建时间') class Meta: db_table = 'OrderTemplateItem' app_label = 'gvsdsdk' verbose_name = '订单模板项' verbose_name_plural = '订单模板项' managed = False unique_together = [('Template', 'ItemCode')] ordering = ['ItemSort'] indexes = [ models.Index(fields=['Template', 'ItemStatus'], name='idx_ordtplitem_template_status'), ] def __str__(self): return f'{self.ItemName}({self.ItemCode})' # === 订单表 === class Order(QModel): """订单表——SAAS 级统一订单记录 每个订单基于一个模板创建,包含订单基本信息、等级、支付状态等。 子服务通过此表管理业务订单生命周期。 """ ORD_PENDING = 0 ORD_CONFIRMED = 1 ORD_PAID = 2 ORD_PROCESSING = 3 ORD_COMPLETED = 4 ORD_CANCELLED = 5 ORD_REFUNDING = 6 ORD_REFUNDED = 7 ORD_CLOSED = 8 ORD_STATUS_CHOICES = [ (ORD_PENDING, '待确认'), (ORD_CONFIRMED, '已确认'), (ORD_PAID, '已支付'), (ORD_PROCESSING, '处理中'), (ORD_COMPLETED, '已完成'), (ORD_CANCELLED, '已取消'), (ORD_REFUNDING, '退款中'), (ORD_REFUNDED, '已退款'), (ORD_CLOSED, '已关闭'), ] OrderUUID = UUIDField(primary_key=True, verbose_name='订单UUID') TenantUUID = UUIDField(verbose_name='租户UUID') CompanyUUID = UUIDField(null=True, blank=True, verbose_name='公司UUID') OrderNO = models.CharField(max_length=64, unique=True, verbose_name='订单编号') # 来源模板 Template = models.ForeignKey( OrderTemplate, on_delete=models.SET_NULL, null=True, related_name='orders', verbose_name='订单模板', ) # 等级 Level = models.ForeignKey( OrderLevel, on_delete=models.SET_NULL, null=True, blank=True, related_name='orders', verbose_name='订单等级', ) # 订单主体 OrderTitle = models.CharField(max_length=256, verbose_name='订单标题') OrderDescription = models.CharField(max_length=1024, default='', verbose_name='订单描述') OrderType = models.CharField(max_length=32, verbose_name='订单类型') # 用户 BuyerUserUUID = UUIDField(verbose_name='买家用户UUID') SellerUserUUID = UUIDField(null=True, blank=True, verbose_name='卖家用户UUID') # 金额 TotalAmount = models.DecimalField(max_digits=18, decimal_places=4, default=0, verbose_name='总金额') DiscountAmount = models.DecimalField(max_digits=18, decimal_places=4, default=0, verbose_name='优惠金额') PayAmount = models.DecimalField(max_digits=18, decimal_places=4, default=0, verbose_name='实付金额') Currency = models.CharField(max_length=10, default='CNY', verbose_name='币种') # 状态 OrderStatus = models.SmallIntegerField( default=ORD_PENDING, choices=ORD_STATUS_CHOICES, verbose_name='订单状态', ) # 支付关联(可选) PaymentTransaction = models.ForeignKey( 'payment.PaymentTransaction', on_delete=models.SET_NULL, null=True, blank=True, related_name='orders', verbose_name='支付交易', ) PaymentConfig = models.ForeignKey( 'payment.PaymentGatewayConfig', on_delete=models.SET_NULL, null=True, blank=True, related_name='paid_orders', verbose_name='支付网关配置', ) # 时间 ConfirmTime = models.DateTimeField(null=True, blank=True, verbose_name='确认时间') PayTime = 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='取消时间') ExpireTime = models.DateTimeField(null=True, blank=True, verbose_name='过期时间') # 快照(创建时冻结模板信息) TemplateSnapshot = models.JSONField(default=dict, verbose_name='模板快照') LevelSnapshot = models.JSONField(default=dict, verbose_name='等级快照') # 扩展 OrderMetadata = models.JSONField(default=dict, verbose_name='订单元数据') Remark = models.CharField(max_length=512, default='', verbose_name='备注') CreateTime = models.DateTimeField(default=datetime.datetime.utcnow, verbose_name='创建时间') UpdateTime = models.DateTimeField(null=True, blank=True, verbose_name='更新时间') class Meta: db_table = 'Order' app_label = 'gvsdsdk' verbose_name = '订单' verbose_name_plural = '订单' managed = False ordering = ['-CreateTime'] indexes = [ models.Index(fields=['TenantUUID', 'OrderStatus', 'CreateTime'], name='idx_order_tenant_status_time'), models.Index(fields=['BuyerUserUUID'], name='idx_order_buyer'), models.Index(fields=['OrderNO'], name='idx_order_no'), models.Index(fields=['OrderType'], name='idx_order_type'), ] def __str__(self): return self.OrderNO @property def IsPaid(self): return self.OrderStatus >= self.ORD_PAID @property def IsCompleted(self): return self.OrderStatus == self.ORD_COMPLETED @property def IsCancelled(self): return self.OrderStatus == self.ORD_CANCELLED @property def IsTerminal(self): return self.OrderStatus in ( self.ORD_COMPLETED, self.ORD_CANCELLED, self.ORD_REFUNDED, self.ORD_CLOSED, ) def Confirm(self): """确认订单""" self.OrderStatus = self.ORD_CONFIRMED self.ConfirmTime = datetime.datetime.utcnow() self.save(update_fields=['OrderStatus', 'ConfirmTime', 'UpdateTime']) def MarkPaid(self, payment_transaction=None): """标记为已支付""" self.OrderStatus = self.ORD_PAID self.PayTime = datetime.datetime.utcnow() if payment_transaction: self.PaymentTransaction = payment_transaction self.save(update_fields=['OrderStatus', 'PayTime', 'PaymentTransaction', 'UpdateTime']) def StartProcessing(self): """开始处理""" self.OrderStatus = self.ORD_PROCESSING self.save(update_fields=['OrderStatus', 'UpdateTime']) def Complete(self): """完成订单""" self.OrderStatus = self.ORD_COMPLETED self.CompleteTime = datetime.datetime.utcnow() self.save(update_fields=['OrderStatus', 'CompleteTime', 'UpdateTime']) def Cancel(self, reason=''): """取消订单""" self.OrderStatus = self.ORD_CANCELLED self.CancelTime = datetime.datetime.utcnow() if reason: self.Remark = reason self.save(update_fields=['OrderStatus', 'CancelTime', 'Remark', 'UpdateTime']) def StartRefund(self): """开始退款""" self.OrderStatus = self.ORD_REFUNDING self.save(update_fields=['OrderStatus', 'UpdateTime']) def RefundDone(self): """退款完成""" self.OrderStatus = self.ORD_REFUNDED self.save(update_fields=['OrderStatus', 'UpdateTime']) def Close(self): """关闭订单""" self.OrderStatus = self.ORD_CLOSED self.save(update_fields=['OrderStatus', 'UpdateTime']) def GetItems(self): """获取订单所有行项目""" return self.Items.all().order_by('ItemSort') # === 订单项表 === class OrderItem(QModel): """订单项表——订单中的行项目 创建订单时从模板项复制生成,记录实际购买的商品/服务。 """ ItemUUID = UUIDField(primary_key=True, verbose_name='订单项UUID') Order = models.ForeignKey( Order, on_delete=models.CASCADE, related_name='Items', verbose_name='订单', ) # 来源模板项(可选,手动创建的订单可能没有模板项) TemplateItem = models.ForeignKey( OrderTemplateItem, on_delete=models.SET_NULL, null=True, blank=True, related_name='order_items', verbose_name='模板项', ) ItemCode = models.CharField(max_length=64, verbose_name='项目编码') ItemName = models.CharField(max_length=256, verbose_name='项目名称') ItemDescription = models.CharField(max_length=512, default='', verbose_name='项目描述') ItemType = models.CharField(max_length=32, default='default', verbose_name='项目类型') UnitPrice = models.DecimalField(max_digits=18, decimal_places=4, default=0, verbose_name='单价') Quantity = models.IntegerField(default=1, verbose_name='数量') Subtotal = models.DecimalField(max_digits=18, decimal_places=4, default=0, verbose_name='小计') Currency = models.CharField(max_length=10, default='CNY', verbose_name='币种') ItemSort = models.SmallIntegerField(default=0, verbose_name='排序') ItemStatus = models.SmallIntegerField(default=1, verbose_name='项目状态') ItemMetadata = models.JSONField(default=dict, verbose_name='项目元数据') CreateTime = models.DateTimeField(default=datetime.datetime.utcnow, verbose_name='创建时间') class Meta: db_table = 'OrderItem' app_label = 'gvsdsdk' verbose_name = '订单项' verbose_name_plural = '订单项' managed = False ordering = ['ItemSort'] indexes = [ models.Index(fields=['Order'], name='idx_orditem_order'), ] def __str__(self): return f'{self.ItemName}x{self.Quantity}' def CalcSubtotal(self): """计算小计""" self.Subtotal = self.UnitPrice * self.Quantity return self.Subtotal