Files
Django/products/models.py
T
XingQueandCursor dc4c0ad091 feat: 转盘按商品重设计(库存、控奖、后台免填商品编号)
转盘即商品自动建绑;奖池支持库存;控奖指定用户必中;xq/xzj 普通点单路径不变。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-22 20:39:59 +08:00

507 lines
23 KiB
Python

from django.db import models
from gvsdsdk.model_base import QModel
from django.utils import timezone
class Shangpin(QModel):
"""商品表 - 100-1000条数据"""
id = models.AutoField(primary_key=True, verbose_name='商品ID')
biaoqian = models.CharField(max_length=45,null=True, blank=True, verbose_name='商品标题') # 15汉字
jiage = models.DecimalField(max_digits=10, decimal_places=2, default=0.00, verbose_name='商品价格')
kucun = models.PositiveIntegerField(default=0, verbose_name='商品库存')
leixing_id = models.PositiveIntegerField(null=True, blank=True,verbose_name='商品类型ID') # 直接存数字
zhuanqu_id = models.PositiveIntegerField(null=True, blank=True,verbose_name='商品专区ID') # 直接存数字
zhenshi_xiaoliang = models.PositiveIntegerField(default=0, verbose_name='商品真实销量')
duiwai_xiaoliang = models.PositiveIntegerField(default=0, verbose_name='商品对外销量')
jieshao = models.TextField(null=True, blank=True, verbose_name='商品介绍')
# 新增字段
xiadan_xuzhi = models.TextField(null=True, blank=True, verbose_name='下单须知') # 支持500字,可换行
guize_tupian = models.CharField(max_length=500, null=True, blank=True, verbose_name='规则图片URL')
yaoqiuleixing = models.PositiveIntegerField(null=True, blank=True, verbose_name='抢单要求类型')
huiyuan_id = models.CharField(max_length=6, null=True, blank=True, verbose_name='会员ID')
yongjin = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True,verbose_name='佣金要求')
# 🔥 新增:是否开启打手额外分成
kaioi_ewai_dashou_fencheng = models.BooleanField(default=False, verbose_name='是否开启打手额外分成')
# 🔥 新增:打手额外分成金额(开启时使用此值,否则按利率计算)
ewai_dashou_fencheng = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True,verbose_name='打手额外分成金额')
# 原有图片URL
tupian_url = models.CharField(max_length=500, null=True, blank=True, verbose_name='商品图片URL')
CreateTime = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
UpdateTime = models.DateTimeField(auto_now=True, verbose_name='更改时间')
paixu = models.IntegerField(default=0, verbose_name='排序权重')
shenhezhuangtai = models.IntegerField(default=1, verbose_name='审核状态', help_text='1=正常, 2=审核专用')
dianpu = models.ForeignKey(
'shop.Dianpu', # 🔥 跨应用完整路径
on_delete=models.CASCADE,
null=True,
blank=True,
db_index=True,
verbose_name='所属店铺'
)
dianpu_leixing = models.ForeignKey(
'shop.ShangpinLeixingDianpu',
on_delete=models.SET_NULL,
null=True,
blank=True,
db_index=True,
verbose_name='店铺商品类型'
)
shangjia_zhuangtai = models.BooleanField(default=True, verbose_name='是否上架')
fengjin_zhuangtai = models.BooleanField(default=True, verbose_name='是否封禁')
shenhe_zhuangtai = models.BooleanField(default=False, verbose_name='平台审核状态',help_text='True=审核通过,False=未审核')
club_id = models.CharField(
max_length=16, default='xq', db_index=True, verbose_name='所属俱乐部',
help_text='公共商品按俱乐部隔离;历史数据默认星阙 xq',
)
# 转盘购票商品标记:True 时走转盘支付,不走普通点单进抢池;默认 False,xq/xzj 不受影响
shi_zhuanpan = models.BooleanField(
default=False, db_index=True, verbose_name='是否转盘购票商品',
)
class Meta:
db_table = 'shangpin'
verbose_name = '商品'
verbose_name_plural = '商品'
# 不要索引!数据量太小
def __str__(self):
return f"商品{self.id}:{self.biaoqian}"
class ShangpinLeixing(QModel):
"""商品类型表 - <10条数据"""
id = models.AutoField(primary_key=True, verbose_name='类型ID')
yaoqiuleixing = models.PositiveIntegerField(null=True, blank=True, verbose_name='抢单要求类型')
huiyuan_id = models.CharField(max_length=6, null=True, blank=True, verbose_name='会员ID')
yongjin = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, verbose_name='佣金要求')
jieshao = models.CharField(max_length=45, null=True, blank=True, verbose_name='类型介绍') # 15汉字
tupian_url = models.CharField(max_length=500, null=True, blank=True, verbose_name='类型图片URL') # 用CharField
paixu = models.IntegerField(default=0, verbose_name='排序权重')
shenhezhuangtai = models.IntegerField(default=1, verbose_name='审核状态', help_text='1=正常, 2=审核专用')
bankuai = models.ForeignKey(
'rank.Bankuai',
on_delete=models.SET_NULL,
null=True,
blank=True,
verbose_name='所属板块'
)
class Meta:
db_table = 'shangpin_leixing'
verbose_name = '商品类型'
verbose_name_plural = '商品类型'
def __str__(self):
return f"{self.jieshao}"
class ShangpinZhuanqu(QModel):
"""商品专区表 - <50条数据"""
id = models.AutoField(primary_key=True, verbose_name='专区ID')
mingzi = models.CharField(max_length=45, null=True, blank=True, verbose_name='专区名称') # 15汉字
leixing_id = models.PositiveIntegerField(verbose_name='类型ID') # 直接存数字,不关联
paixu = models.IntegerField(default=0, verbose_name='排序权重')
shenhezhuangtai = models.IntegerField(default=1, verbose_name='审核状态', help_text='1=正常, 2=审核专用')
dianpu = models.ForeignKey(
'shop.Dianpu', # 🔥 跨应用完整路径
on_delete=models.CASCADE,
null=True,
blank=True,
db_index=True,
verbose_name='所属店铺'
)
dianpu_leixing = models.ForeignKey(
'shop.ShangpinLeixingDianpu',
on_delete=models.SET_NULL,
null=True,
blank=True,
db_index=True,
verbose_name='店铺商品类型'
)
shangjia_zhuangtai = models.BooleanField(default=True, verbose_name='是否上架')
fengjin_zhuangtai = models.BooleanField(default=True, verbose_name='是否封禁')
club_id = models.CharField(
max_length=16, default='xq', db_index=True, verbose_name='所属俱乐部',
help_text='公共专区按俱乐部隔离;历史数据默认星阙 xq',
)
# UFO 等点单端专区横滑图标(相对 OSS 路径或完整 URL);xq 可不填
tupian_url = models.CharField(
max_length=500, null=True, blank=True, default='',
verbose_name='专区图标URL',
)
class Meta:
db_table = 'shangpin_zhuanqu'
verbose_name = '商品专区'
verbose_name_plural = '商品专区'
def __str__(self):
return f"{self.mingzi}"
class Huiyuan(QModel):
huiyuan_id = models.CharField(max_length=6, unique=True, verbose_name='会员ID')
jieshao = models.CharField(max_length=30, verbose_name='会员介绍')
jtjieshao = models.TextField(null=True, blank=True, verbose_name='会员规则介绍')
jiage = models.DecimalField(max_digits=10, decimal_places=2, default=0.00, verbose_name='会员价格')
guanshifc = models.DecimalField(max_digits=10, decimal_places=2, default=0.00, verbose_name='管事分成')
zuzhangfc = models.DecimalField(max_digits=10, decimal_places=2, default=0.00, verbose_name='组长分成')
goumai_cishu = models.PositiveIntegerField(default=0, verbose_name='购买次数')
CreateTime = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
UpdateTime = models.DateTimeField(auto_now=True, verbose_name='更新时间')
bankuai = models.ForeignKey(
'rank.Bankuai',
on_delete=models.SET_NULL,
null=True,
blank=True,
verbose_name='所属板块'
)
is_bundle = models.BooleanField(default=False, verbose_name='是否为总会员(包含式)')
class Meta:
db_table = 'huiyuan'
verbose_name = '会员'
verbose_name_plural = '会员'
def __str__(self):
return f"会员{self.huiyuan_id}:{self.jieshao}"
class Huiyuangoumai(QModel):
id = models.AutoField(primary_key=True, verbose_name='购买记录ID')
huiyuan_id = models.CharField(max_length=6, db_index=True, verbose_name='会员ID')
yonghu_id = models.CharField(max_length=32, db_index=True, verbose_name='用户ID')
huiyuan_zhuangtai = models.PositiveIntegerField(default=1, verbose_name='会员状态')
jieshao = models.CharField(max_length=30,null=True,verbose_name='会员介绍')
daoqi_time = models.DateTimeField(verbose_name='会员到期时间')
CreateTime = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
UpdateTime = models.DateTimeField(auto_now=True, verbose_name='更新时间')
club_id = models.CharField(
max_length=16, default='xq', db_index=True, verbose_name='所属俱乐部',
)
is_trial = models.BooleanField(default=False, verbose_name='当前是否为体验权益')
has_used_trial = models.BooleanField(default=False, verbose_name='是否已购买过体验版')
class Meta:
db_table = 'huiyuangoumai'
verbose_name = '用户购买会员'
verbose_name_plural = '用户购买会员'
# 添加这个联合唯一约束
unique_together = [['yonghu_id', 'huiyuan_id']]
# 顺便优化索引(可选的,建议加)
indexes = [
models.Index(fields=['yonghu_id']),
models.Index(fields=['huiyuan_id']),
models.Index(fields=['yonghu_id', 'huiyuan_id']), # 复合索引加速查询
]
def __str__(self):
return f"用户{self.yonghu_id}-会员{self.huiyuan_id}"
def jiance_shifou_daoqi(self):
# 使用Django的timezone,它会自动处理时区
now = timezone.now()
if now > self.daoqi_time:
# 过期了
if self.huiyuan_zhuangtai != 0:
self.huiyuan_zhuangtai = 0
self.save()
return True
else:
# 没过期
if self.huiyuan_zhuangtai != 1:
self.huiyuan_zhuangtai = 1
self.save()
return False
class Czjilu(QModel):
dingdan_id = models.CharField(max_length=32, unique=True, db_index=True, verbose_name='订单ID')
huiyuan_id = models.CharField(max_length=6, null=True, blank=True, verbose_name='会员ID')
zhuangtai = models.IntegerField(null=True, blank=True, verbose_name='订单状态')
yonghuid = models.CharField(max_length=7, db_index=True, verbose_name='用户ID')
jine = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='订单金额')
leixing = models.IntegerField(
null=True, blank=True,
verbose_name='充值类型,1:会员,2:押金,3:积分,4:商家充值,5:考核,6:罚款缴纳',
)
shuoming = models.CharField(max_length=100, null=True, blank=True, verbose_name='订单说明')
club_id = models.CharField(
max_length=16, default='xq', db_index=True, verbose_name='所属俱乐部',
)
is_trial = models.BooleanField(default=False, verbose_name='是否体验会员单')
purchase_days = models.PositiveIntegerField(
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 = 'czjilu'
verbose_name = '充值记录订单'
verbose_name_plural = '充值记录订单'
# 为高频查询字段添加索引
indexes = [
models.Index(fields=['dingdan_id']),
models.Index(fields=['yonghuid']),
]
class Gsfenhong(QModel):
dingdan_id = models.CharField(max_length=32, unique=True, verbose_name='订单ID')
guanshi = models.CharField(max_length=7, db_index=True, verbose_name='管事ID')
dashouid = models.CharField(max_length=7, db_index=True,verbose_name='打手ID')
shuoming = models.CharField(max_length=100, null=True, blank=True, verbose_name='订单说明')
fenhong = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='订单分红金额')
avatar = models.CharField(max_length=500, null=True, blank=True, verbose_name='打手头像')
nicheng = models.CharField(max_length=50, null=True, verbose_name='打手昵称')
CreateTime = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
UpdateTime = models.DateTimeField(auto_now=True, verbose_name='更新时间')
zuzhang_id = models.CharField(max_length=7, null=True, blank=True, db_index=True, verbose_name='组长ID')
zuzhang_fenhong = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True,verbose_name='组长分红金额')
fenhong_leixing = models.PositiveSmallIntegerField(
default=1,
choices=[(1, '会员分红'), (2, '押金分红'), (3, '商家订单分红')],
verbose_name='分红类型'
)
huiyuan_id = models.CharField(max_length=6, null=True, blank=True, verbose_name='会员ID')
club_id = models.CharField(
max_length=16, default='xq', db_index=True, verbose_name='所属俱乐部',
)
is_trial = models.BooleanField(default=False, verbose_name='是否体验会员分红')
class Meta:
db_table = 'gsfenhong'
verbose_name = '管事分红记录'
verbose_name_plural = '管事分红记录'
# 为高频查询字段添加索引
indexes = [
models.Index(fields=['guanshi']),
models.Index(fields=['dashouid']),
]
class DuociFenhong(QModel):
"""
多次分红配置表
用于配置不同次数下管事和组长的分红金额
"""
huiyuan = models.CharField(max_length=10, verbose_name='会员ID')
yonghuid = models.CharField(max_length=7, db_index=True, verbose_name='管事ID')
club_id = models.CharField(max_length=16, default='xq', db_index=True, verbose_name='所属俱乐部')
cishu = models.PositiveIntegerField(verbose_name='分红次数')
guanshi_fenhong = models.DecimalField(max_digits=10, decimal_places=2, default=0.00, verbose_name='管事分红金额')
zuzhang_fenhong = models.DecimalField(max_digits=10, decimal_places=2, default=0.00, verbose_name='组长分红金额')
CreateTime = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
UpdateTime = models.DateTimeField(auto_now=True, verbose_name='更新时间')
class Meta:
db_table = 'duoci_fenhong'
verbose_name = '多次分红配置'
verbose_name_plural = '多次分红配置'
unique_together = ('club_id', 'huiyuan', 'yonghuid', 'cishu')
indexes = [
models.Index(fields=['yonghuid']),
]
def __str__(self):
return f"会员{self.huiyuan_id} 用户{self.yonghuid} 次数{self.cishu}"
class ShangpinMeiriTongji(QModel):
"""
商品每日统计表
按商品 + 日期维度记录点单、成交、退款及收益数据
日期精确到天,冗余年月日字段便于分组查询
"""
shangpin = models.ForeignKey(
'products.Shangpin',
on_delete=models.CASCADE,
db_index=True,
verbose_name='商品ID'
)
riqi = models.DateField(db_index=True, verbose_name='统计日期')
nian = models.PositiveSmallIntegerField(verbose_name='年份')
yue = models.PositiveSmallIntegerField(verbose_name='月份')
ri = models.PositiveSmallIntegerField(verbose_name='日')
# 点单统计(下单即累加)
diandan_zongliang = models.PositiveIntegerField(default=0, verbose_name='点单总量')
diandan_zonge = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='点单总额')
# 成交统计(订单完成时累加)
chengjiao_zongliang = models.PositiveIntegerField(default=0, verbose_name='成交总量')
chengjiao_zonge = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='成交总额')
# 退款统计
tuikuan_zongliang = models.PositiveIntegerField(default=0, verbose_name='退款总量')
tuikuan_zonge = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='退款总额')
# 收益统计(新增)
pingtai_shouyi = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='平台收益')
dianpu_zongshouyi = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='店铺总收益')
CreateTime = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
UpdateTime = models.DateTimeField(auto_now=True, verbose_name='更新时间')
class Meta:
db_table = 'shangpin_meiri_tongji'
verbose_name = '商品每日统计'
verbose_name_plural = verbose_name
# 每天每个商品唯一记录
unique_together = [['shangpin', 'riqi']]
indexes = [
models.Index(fields=['shangpin', 'riqi']),
models.Index(fields=['nian', 'yue', 'ri']),
]
def __str__(self):
return f"{self.shangpin.biaoqian} - {self.riqi}"
def clean(self):
"""保存前自动填充年月日冗余字段"""
if self.riqi:
self.nian = self.riqi.year
self.yue = self.riqi.month
self.ri = self.riqi.day
super().clean()
def save(self, *args, **kwargs):
self.clean()
super().save(*args, **kwargs)
# ===========================================================================
# 转盘单(UFO 等俱乐部可选能力;未配置活动则接口无数据,不影响 xq/xzj)
# ===========================================================================
class ZhuanpanHuodong(QModel):
"""转盘活动:与转盘商品 1:1;购票价格取关联商品 jiage。"""
id = models.AutoField(primary_key=True)
club_id = models.CharField(max_length=16, db_index=True, verbose_name='俱乐部')
mingzi = models.CharField(max_length=64, default='幸运转盘', verbose_name='活动名')
shangpin_id = models.PositiveIntegerField(
db_index=True, verbose_name='转盘商品ID',
help_text='对应 Shangpin.id,且 shi_zhuanpan=True;后台创建商品时自动绑定',
)
qiyong = models.BooleanField(default=True, db_index=True, verbose_name='是否启用')
beizhu = models.CharField(max_length=200, blank=True, default='', verbose_name='备注')
CreateTime = models.DateTimeField(auto_now_add=True)
UpdateTime = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'zhuanpan_huodong'
verbose_name = '转盘活动'
verbose_name_plural = verbose_name
indexes = [models.Index(fields=['club_id', 'qiyong'])]
class ZhuanpanJiangpin(QModel):
"""转盘奖品格:权重越高越易中;kucun=-1 不限;shi_xiexie 不生成订单。"""
id = models.AutoField(primary_key=True)
huodong_id = models.PositiveIntegerField(db_index=True, verbose_name='活动ID')
mingzi = models.CharField(max_length=64, verbose_name='奖品名')
tupian_url = models.CharField(max_length=500, blank=True, default='', verbose_name='奖品图')
shangpin_id = models.PositiveIntegerField(
null=True, blank=True, db_index=True, verbose_name='中奖后履约商品ID',
)
quanzhong = models.PositiveIntegerField(default=1, verbose_name='中奖权重')
kucun = models.IntegerField(
default=-1, verbose_name='剩余库存',
help_text='-1=不限;0=不可抽中(控奖仍可强制);>0 抽中后减一',
)
paixu = models.PositiveSmallIntegerField(default=0, verbose_name='格子序号')
shi_xiexie = models.BooleanField(default=False, verbose_name='是否谢谢参与')
CreateTime = models.DateTimeField(auto_now_add=True)
UpdateTime = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'zhuanpan_jiangpin'
verbose_name = '转盘奖品'
verbose_name_plural = verbose_name
indexes = [models.Index(fields=['huodong_id', 'paixu'])]
class ZhuanpanKongjiang(QModel):
"""运营控奖:指定用户接下来若干次必中某奖。"""
id = models.AutoField(primary_key=True)
club_id = models.CharField(max_length=16, db_index=True, verbose_name='俱乐部')
huodong_id = models.PositiveIntegerField(db_index=True, verbose_name='活动ID')
yonghuid = models.CharField(max_length=32, db_index=True, verbose_name='用户UID')
jiangpin_id = models.PositiveIntegerField(db_index=True, verbose_name='指定奖品ID')
cishu = models.PositiveIntegerField(default=1, verbose_name='剩余次数')
qiyong = models.BooleanField(default=True, db_index=True, verbose_name='是否启用')
beizhu = models.CharField(max_length=200, blank=True, default='', verbose_name='备注')
CreateTime = models.DateTimeField(auto_now_add=True)
UpdateTime = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'zhuanpan_kongjiang'
verbose_name = '转盘控奖'
verbose_name_plural = verbose_name
indexes = [
models.Index(fields=['club_id', 'yonghuid', 'qiyong']),
models.Index(fields=['huodong_id', 'yonghuid']),
]
class ZhuanpanJihui(QModel):
"""转盘抽奖机会单:支付成功后待抽,不进抢单池;抽中后再写 Order。
zhuangtai: 9=待支付 1=已付待抽 2=已抽 3=关闭/作废
"""
id = models.AutoField(primary_key=True)
club_id = models.CharField(max_length=16, db_index=True, verbose_name='俱乐部')
yonghuid = models.CharField(max_length=32, db_index=True, verbose_name='用户UID')
huodong_id = models.PositiveIntegerField(db_index=True, verbose_name='活动ID')
czjilu_dingdan_id = models.CharField(
max_length=64, unique=True, db_index=True, verbose_name='支付流水单号',
)
jine = models.DecimalField(max_digits=10, decimal_places=2, default=0, verbose_name='购票金额')
zhuangtai = models.PositiveSmallIntegerField(default=9, db_index=True, verbose_name='状态')
nicheng = models.CharField(max_length=50, blank=True, default='', verbose_name='游戏昵称')
beizhu = models.CharField(max_length=200, blank=True, default='', verbose_name='备注')
zhongjiang_jiangpin_id = models.PositiveIntegerField(null=True, blank=True, verbose_name='中奖奖品ID')
zhongjiang_shangpin_id = models.PositiveIntegerField(null=True, blank=True, verbose_name='中奖商品ID')
dingdan_id = models.CharField(
max_length=32, blank=True, default='', db_index=True,
verbose_name='中奖后生成的订单号',
)
CreateTime = models.DateTimeField(auto_now_add=True)
UpdateTime = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'zhuanpan_jihui'
verbose_name = '转盘抽奖机会'
verbose_name_plural = verbose_name
indexes = [
models.Index(fields=['club_id', 'yonghuid', 'zhuangtai']),
]