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

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

507 lines
23 KiB
Python
Raw Permalink 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.
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 时走转盘支付,不走普通点单进抢池;默认 Falsexq/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 路径或完整 URLxq 可不填
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']),
]