将文件夹命名从拼音改为英文,整理了一些代码,将导入语句迁移到了头部

This commit is contained in:
2026-06-14 16:39:09 +08:00
parent 1fd0301e0e
commit 21bb129c5d
178 changed files with 29249 additions and 35862 deletions

0
shop/__init__.py Normal file
View File

3
shop/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
shop/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class ShangdianConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'shangdian'

View File

@@ -0,0 +1,92 @@
# Generated by Django 4.2.27 on 2026-04-21 12:36
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('shangpin', '0010_shangpinleixing_is_cross_enabled'),
]
operations = [
migrations.CreateModel(
name='YonghuPingzheng',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='自增主键ID')),
('zhanghao', models.CharField(db_index=True, max_length=50, unique=True, verbose_name='登录账号')),
('mima', models.CharField(max_length=255, verbose_name='密码')),
('is_active', models.BooleanField(default=True, verbose_name='是否启用')),
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
('yonghu', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, to_field='yonghuid', verbose_name='关联用户ID')),
],
options={
'verbose_name': '用户认证凭证',
'verbose_name_plural': '用户认证凭证',
'db_table': 'yonghu_pingzheng',
},
),
migrations.CreateModel(
name='Dianpu',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='店铺自增ID')),
('dianpu_mingcheng', models.CharField(max_length=50, verbose_name='店铺名称')),
('dianpu_touxiang', models.CharField(blank=True, max_length=500, null=True, verbose_name='店铺头像URL')),
('lianxi_dianhua', models.CharField(blank=True, max_length=20, null=True, verbose_name='联系电话')),
('weixinhao', models.CharField(blank=True, max_length=50, null=True, verbose_name='微信号')),
('zhuangtai', models.IntegerField(choices=[(0, '封禁'), (1, '正常')], default=1, verbose_name='店铺状态')),
('bangding_yonghushu', models.PositiveIntegerField(default=0, verbose_name='绑定用户总数')),
('erweima_url', models.CharField(blank=True, max_length=500, null=True, verbose_name='店铺二维码相对URL')),
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
('pingzheng', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='shangdian.yonghupingzheng', verbose_name='关联用户凭证')),
],
options={
'verbose_name': '平台商家店铺',
'verbose_name_plural': '平台商家店铺',
'db_table': 'dianpu',
},
),
migrations.CreateModel(
name='YonghuDianpuBangding',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='绑定自增ID')),
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
('dianpu', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shangdian.dianpu', verbose_name='关联店铺ID')),
('yonghu', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, to_field='yonghuid', verbose_name='用户ID')),
],
options={
'verbose_name': '用户店铺绑定关系',
'verbose_name_plural': '用户店铺绑定关系',
'db_table': 'yonghu_dianpu_bangding',
'unique_together': {('yonghu', 'dianpu')},
},
),
migrations.CreateModel(
name='ShangpinLeixingDianpu',
fields=[
('id', models.AutoField(primary_key=True, serialize=False, verbose_name='类型自增ID')),
('jieshao', models.CharField(max_length=45, verbose_name='类型介绍')),
('tupian_url', models.CharField(blank=True, max_length=500, null=True, verbose_name='类型图片URL')),
('paixu', models.IntegerField(default=0, verbose_name='排序权重')),
('shangjia_zhuangtai', models.BooleanField(default=True, verbose_name='是否上架')),
('fengjin_zhuangtai', models.BooleanField(default=False, verbose_name='是否封禁')),
('shenhe_zhuangtai', models.BooleanField(default=False, help_text='True=审核通过False=未审核', verbose_name='平台审核状态')),
('dianpu', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shangdian.dianpu', verbose_name='所属店铺')),
('gonggong_leixing', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='shangpin.shangpinleixing', verbose_name='映射的公共商品类型')),
],
options={
'verbose_name': '店铺商品类型映射',
'verbose_name_plural': '店铺商品类型映射',
'db_table': 'shangdian_shangpin_leixing',
'indexes': [models.Index(fields=['dianpu'], name='shangdian_s_dianpu__100f93_idx'), models.Index(fields=['gonggong_leixing'], name='shangdian_s_gonggon_88a9a0_idx')],
},
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 4.2.27 on 2026-04-21 13:04
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('shangdian', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='yonghupingzheng',
name='yonghu',
field=models.CharField(db_index=True, max_length=7, unique=True, verbose_name='关联用户ID'),
),
]

View File

@@ -0,0 +1,85 @@
# Generated by Django 4.2.27 on 2026-04-23 14:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('shangdian', '0002_alter_yonghupingzheng_yonghu'),
]
operations = [
migrations.CreateModel(
name='DianpuMorenPeizhi',
fields=[
('id', models.PositiveSmallIntegerField(default=1, editable=False, primary_key=True, serialize=False)),
('moren_choucheng_feilv', models.DecimalField(decimal_places=4, default=0.1, max_digits=5, verbose_name='默认订单抽成费率小数0.1000=10%')),
('moren_tixian_xiane', models.DecimalField(decimal_places=2, default=1000.0, max_digits=10, verbose_name='默认可提现限额')),
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
],
options={
'verbose_name': '店铺默认配置',
'verbose_name_plural': '店铺默认配置',
'db_table': 'dianpu_moren_peizhi',
},
),
migrations.AddField(
model_name='dianpu',
name='kaiqi_meiri_xiane',
field=models.BooleanField(default=False, verbose_name='是否开启每日提现限额'),
),
migrations.AddField(
model_name='dianpu',
name='kaiqi_shouyi_choucheng',
field=models.BooleanField(default=False, verbose_name='是否开启店铺订单收益抽成'),
),
migrations.AddField(
model_name='dianpu',
name='ketixian_yue',
field=models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='店铺可提现余额'),
),
migrations.AddField(
model_name='dianpu',
name='meiri_tixian_xiane',
field=models.DecimalField(blank=True, decimal_places=2, default=0.0, max_digits=10, null=True, verbose_name='店铺每日提现限额'),
),
migrations.AddField(
model_name='dianpu',
name='shouyi_choucheng_feilv',
field=models.DecimalField(blank=True, decimal_places=4, default=0.0, max_digits=5, null=True, verbose_name='店铺订单收益抽成费率小数如0.1000=10%'),
),
migrations.AddField(
model_name='dianpu',
name='zhengqu_zonge',
field=models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='店铺赚取总额'),
),
migrations.CreateModel(
name='DianpuShouzhiMeiriTongji',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('dianpu_id', models.PositiveIntegerField(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='')),
('xiadan_zongliang', models.PositiveIntegerField(default=0, verbose_name='下单总量')),
('xiadan_zonge', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='下单总额')),
('chengjiao_zongliang', models.PositiveIntegerField(default=0, verbose_name='成交总量')),
('chengjiao_zonge', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='成交总额')),
('tuikuan_zongliang', models.PositiveIntegerField(default=0, verbose_name='退款总量')),
('tuikuan_zonge', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='退款总额')),
('shouyi_zonge', models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='收益总额')),
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
],
options={
'verbose_name': '店铺每日收支统计',
'verbose_name_plural': '店铺每日收支统计',
'db_table': 'dianpu_shouzhi_meiri_tongji',
'indexes': [models.Index(fields=['dianpu_id', 'riqi'], name='dianpu_shou_dianpu__bffdb2_idx'), models.Index(fields=['nian', 'yue', 'ri'], name='dianpu_shou_nian_a0f415_idx')],
'unique_together': {('dianpu_id', 'riqi')},
},
),
]

View File

@@ -0,0 +1,27 @@
# Generated by Django 4.2.27 on 2026-04-25 12:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('shangdian', '0003_dianpumorenpeizhi_dianpu_kaiqi_meiri_xiane_and_more'),
]
operations = [
migrations.CreateModel(
name='DianpuShangpinShenheShezhi',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('kaiqi_shenhe', models.BooleanField(default=False, verbose_name='是否开启商品审核模式')),
('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('update_time', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
],
options={
'verbose_name': '商品审核模式配置',
'verbose_name_plural': '商品审核模式配置',
'db_table': 'dianpu_shangpin_shenhe_shezhi',
},
),
]

View File

@@ -0,0 +1,33 @@
# Generated by Django 4.2.27 on 2026-05-05 10:33
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('shangdian', '0004_dianpushangpinshenheshezhi'),
]
operations = [
migrations.AddField(
model_name='dianpu',
name='fenhong_choucheng_feilv',
field=models.DecimalField(blank=True, decimal_places=4, default=0.05, max_digits=5, null=True, verbose_name='上级分红抽成费率(默认5%)'),
),
migrations.AddField(
model_name='dianpu',
name='shangji_fenhong_leiji',
field=models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='作为上级累计获得分红'),
),
migrations.AddField(
model_name='dianpu',
name='shangji_ketixian_fenhong',
field=models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='作为上级可提现分红余额'),
),
migrations.AddField(
model_name='dianpu',
name='yaoqing_dianpu_id',
field=models.PositiveIntegerField(blank=True, db_index=True, null=True, verbose_name='邀请人店铺ID(上级)'),
),
]

View File

@@ -0,0 +1,38 @@
# Generated by Django 4.2.27 on 2026-05-10 18:17
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('shangdian', '0005_dianpu_fenhong_choucheng_feilv_and_more'),
]
operations = [
migrations.AddField(
model_name='dianpushouzhimeiritongji',
name='gei_shangji_cishu',
field=models.PositiveIntegerField(default=0, verbose_name='给上级分红次数'),
),
migrations.AddField(
model_name='dianpushouzhimeiritongji',
name='gei_shangji_zonge',
field=models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='给上级分红总额'),
),
migrations.AddField(
model_name='dianpushouzhimeiritongji',
name='shangji_fenhong_cishu',
field=models.PositiveIntegerField(default=0, verbose_name='作为上级获得分红次数'),
),
migrations.AddField(
model_name='dianpushouzhimeiritongji',
name='shangji_fenhong_zonge',
field=models.DecimalField(decimal_places=2, default=0.0, max_digits=12, verbose_name='作为上级获得分红总额'),
),
migrations.AddField(
model_name='dianpushouzhimeiritongji',
name='shangji_id',
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='当天对应的上级店铺ID若无则为空'),
),
]

View File

227
shop/models.py Normal file
View File

@@ -0,0 +1,227 @@
from django.db import models
# Create your models here.
class YonghuPingzheng(models.Model):
id = models.AutoField(primary_key=True, verbose_name='自增主键ID')
yonghu = models.CharField(
max_length=7,
unique=True, # 确保一个用户只有一条凭证记录
db_index=True,
verbose_name='关联用户ID'
)
zhanghao = models.CharField(max_length=50, unique=True, db_index=True, verbose_name='登录账号')
mima = models.CharField(max_length=255, verbose_name='密码')
is_active = models.BooleanField(default=True, verbose_name='是否启用')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
class Meta:
db_table = 'yonghu_pingzheng'
verbose_name = '用户认证凭证'
verbose_name_plural = verbose_name
class Dianpu(models.Model):
id = models.AutoField(primary_key=True, verbose_name='店铺自增ID')
pingzheng = models.OneToOneField(
'shangdian.YonghuPingzheng', # 同应用内可简写,但完整路径更清晰
on_delete=models.CASCADE,
db_index=True,
verbose_name='关联用户凭证'
)
dianpu_mingcheng = models.CharField(max_length=50, verbose_name='店铺名称')
dianpu_touxiang = models.CharField(max_length=500, null=True, blank=True, verbose_name='店铺头像URL')
lianxi_dianhua = models.CharField(max_length=20, null=True, blank=True, verbose_name='联系电话')
weixinhao = models.CharField(max_length=50, null=True, blank=True, verbose_name='微信号')
zhuangtai = models.IntegerField(choices=[(0, '封禁'), (1, '正常')], default=1, verbose_name='店铺状态')
bangding_yonghushu = models.PositiveIntegerField(default=0, verbose_name='绑定用户总数')
erweima_url = models.CharField(max_length=500, null=True, blank=True, verbose_name='店铺二维码相对URL')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
kaiqi_shouyi_choucheng = models.BooleanField(default=False, verbose_name='是否开启店铺订单收益抽成')
shouyi_choucheng_feilv = models.DecimalField(max_digits=5, decimal_places=4, null=True, blank=True, default=0.0000,
verbose_name='店铺订单收益抽成费率小数如0.1000=10%')
ketixian_yue = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='店铺可提现余额')
zhengqu_zonge = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='店铺赚取总额')
meiri_tixian_xiane = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True, default=0.00,
verbose_name='店铺每日提现限额')
kaiqi_meiri_xiane = models.BooleanField(default=False, verbose_name='是否开启每日提现限额')
yaoqing_dianpu_id = models.PositiveIntegerField(null=True, blank=True, db_index=True,
verbose_name='邀请人店铺ID(上级)')
fenhong_choucheng_feilv = models.DecimalField(max_digits=5, decimal_places=4, null=True, blank=True, default=0.0500,
verbose_name='上级分红抽成费率(默认5%)')
shangji_fenhong_leiji = models.DecimalField(max_digits=12, decimal_places=2, default=0.00,
verbose_name='作为上级累计获得分红')
shangji_ketixian_fenhong = models.DecimalField(max_digits=12, decimal_places=2, default=0.00,
verbose_name='作为上级可提现分红余额')
class Meta:
db_table = 'dianpu'
verbose_name = '平台商家店铺'
verbose_name_plural = verbose_name
class DianpuMorenPeizhi(models.Model):
id = models.PositiveSmallIntegerField(primary_key=True, default=1, editable=False)
moren_choucheng_feilv = models.DecimalField(max_digits=5, decimal_places=4, default=0.1000, verbose_name='默认订单抽成费率小数0.1000=10%')
moren_tixian_xiane = models.DecimalField(max_digits=10, decimal_places=2, default=1000.00, verbose_name='默认可提现限额')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
class Meta:
db_table = 'dianpu_moren_peizhi'
verbose_name = '店铺默认配置'
verbose_name_plural = verbose_name
def __str__(self):
return f"默认配置 - 费率:{self.moren_choucheng_feilv} 限额:{self.moren_tixian_xiane}"
def save(self, *args, **kwargs):
self.id = 1
super().save(*args, **kwargs)
class DianpuShouzhiMeiriTongji(models.Model):
dianpu_id = models.PositiveIntegerField(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='')
xiadan_zongliang = models.PositiveIntegerField(default=0, verbose_name='下单总量')
xiadan_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='退款总额')
shouyi_zonge = models.DecimalField(max_digits=12, decimal_places=2, default=0.00, verbose_name='收益总额')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
shangji_fenhong_cishu = models.PositiveIntegerField(default=0, verbose_name='作为上级获得分红次数')
shangji_fenhong_zonge = models.DecimalField(max_digits=12, decimal_places=2, default=0.00,
verbose_name='作为上级获得分红总额')
# ---------- 作为下级,给上级的分红记录 ----------
shangji_id = models.PositiveIntegerField(null=True, blank=True, verbose_name='当天对应的上级店铺ID若无则为空')
gei_shangji_cishu = models.PositiveIntegerField(default=0, verbose_name='给上级分红次数')
gei_shangji_zonge = models.DecimalField(max_digits=12, decimal_places=2, default=0.00,
verbose_name='给上级分红总额')
class Meta:
db_table = 'dianpu_shouzhi_meiri_tongji'
verbose_name = '店铺每日收支统计'
verbose_name_plural = verbose_name
unique_together = [['dianpu_id', 'riqi']]
indexes = [
models.Index(fields=['dianpu_id', 'riqi']),
models.Index(fields=['nian', 'yue', 'ri']),
]
def __str__(self):
return f"店铺ID:{self.dianpu_id} - {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)
class YonghuDianpuBangding(models.Model):
id = models.AutoField(primary_key=True, verbose_name='绑定自增ID')
yonghu = models.ForeignKey(
'yonghu.UserMain', # 🔥 完整路径
to_field='yonghuid',
on_delete=models.CASCADE,
db_index=True,
verbose_name='用户ID'
)
dianpu = models.ForeignKey('Dianpu', on_delete=models.CASCADE, db_index=True, verbose_name='关联店铺ID')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
class Meta:
db_table = 'yonghu_dianpu_bangding'
verbose_name = '用户店铺绑定关系'
verbose_name_plural = verbose_name
unique_together = [['yonghu', 'dianpu']]
# shangdian/models.py
class ShangpinLeixingDianpu(models.Model):
"""
店铺级商品类型映射表
与公共商品类型表 ShangpinLeixing 强制关联,用于店铺自定义类型
"""
id = models.AutoField(primary_key=True, verbose_name='类型自增ID')
dianpu = models.ForeignKey(
'shangdian.Dianpu',
on_delete=models.CASCADE,
db_index=True,
verbose_name='所属店铺'
)
# 🔥 强制映射到公共商品类型,不可为空
gonggong_leixing = models.ForeignKey(
'shangpin.ShangpinLeixing',
on_delete=models.CASCADE,
db_index=True,
verbose_name='映射的公共商品类型'
)
jieshao = models.CharField(max_length=45, verbose_name='类型介绍')
tupian_url = models.CharField(max_length=500, null=True, blank=True, verbose_name='类型图片URL')
paixu = models.IntegerField(default=0, verbose_name='排序权重')
shangjia_zhuangtai = models.BooleanField(default=True, verbose_name='是否上架')
fengjin_zhuangtai = models.BooleanField(default=False, verbose_name='是否封禁')
shenhe_zhuangtai = models.BooleanField(default=False, verbose_name='平台审核状态',help_text='True=审核通过False=未审核')
class Meta:
db_table = 'shangdian_shangpin_leixing'
verbose_name = '店铺商品类型映射'
verbose_name_plural = verbose_name
indexes = [
models.Index(fields=['dianpu']),
models.Index(fields=['gonggong_leixing']),
]
def __str__(self):
return f"{self.dianpu.dianpu_mingcheng} - {self.jieshao}"
# shangdian/models.py 或独立应用 models.py
class DianpuShangpinShenheShezhi(models.Model):
"""店铺商品审核模式全局配置(仅一条记录)"""
kaiqi_shenhe = models.BooleanField(default=False, verbose_name='是否开启商品审核模式')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
update_time = models.DateTimeField(auto_now=True, verbose_name='更新时间')
class Meta:
db_table = 'dianpu_shangpin_shenhe_shezhi'
verbose_name = '商品审核模式配置'
verbose_name_plural = verbose_name
def save(self, *args, **kwargs):
self.pk = 1 # 强制使用 ID=1 保证唯一
super().save(*args, **kwargs)

3
shop/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

41
shop/urls.py Normal file
View File

@@ -0,0 +1,41 @@
from django.urls import path
from .views import BindDianpuView, ShopGoodsView, ShangdianLoginView, ShopOverviewView, ShopQrcodeView, \
ShopProductConfigView, ShopProductModifyView,ShopProductListView, ShopProductModifyView1,\
MemberListView,DianpuShouzhiTongjiView, QrcodeDownloadView, SubShopListView, SubShopModifyView,\
ShopOrderManageView,ShopOrderStatisticsView, ShopRefundView, ShopForceCompleteView,\
ShopTransferHallView, ShopFineApplyView
urlpatterns = [
path('bdsd', BindDianpuView.as_view(), name='绑定或更新商家店铺'),
path('hqsp', ShopGoodsView.as_view(), name='获取商品数据'),
path('login', ShangdianLoginView.as_view(), name='商家登录'),
path('sjdpgl', ShopOverviewView.as_view(), name='商家获取店铺信息'),
path('sdscdpm', ShopQrcodeView.as_view(), name='生成或更新店铺二维码'),
path('dphqspxx', ShopProductConfigView.as_view(), name='商店获取商品类型专区'),
path('xgdpsplx', ShopProductModifyView.as_view(), name='商店修改添加修改商品类型专区'),
path('sdhqspsj', ShopProductListView.as_view(), name='商店获取商品数据'),
path('xgdpsplx1', ShopProductModifyView1.as_view(), name='商店增删改商品数据'),
path('hqhy', MemberListView.as_view(), name='商店获取会员'),
path('dpsztj', DianpuShouzhiTongjiView.as_view(), name='获取店铺数据统计'),
path('xzewm', QrcodeDownloadView.as_view(), name='下载店铺二维码'),
path('wdxjdp', SubShopListView.as_view(), name='获取下级店铺列表'), # 获取下级店铺列表
path('xjdpbj', SubShopModifyView.as_view(), name='添加/修改下级店铺'), # 添加/修改下级店铺
# 稍后添加具体路由
# 稍后添加具体路由
path('ddlbhq', ShopOrderManageView.as_view(), name='店铺订单数据统计与获取'),
path('ddltj', ShopOrderStatisticsView.as_view(), name='店铺订单数量统计接口'),
path('tk', ShopRefundView.as_view(), name='店铺退款'),
path('qzjd', ShopForceCompleteView.as_view(), name='店铺强制结算'),
path('zydt', ShopTransferHallView.as_view(), name='店铺转移大厅'),
path('fksq', ShopFineApplyView.as_view(), name='店铺开罚单'),
]

640
shop/utils.py Normal file
View File

@@ -0,0 +1,640 @@
from rest_framework.response import Response
from .models import Dianpu, YonghuPingzheng
from products.models import Shangpin
from .models import Dianpu, DianpuMorenPeizhi
# shangdian/utils.py 或 tongji/utils.py
import logging
from datetime import date
from decimal import Decimal
from django.db import transaction
from django.db.models import F
from .models import Dianpu, DianpuShouzhiMeiriTongji
logger = logging.getLogger(__name__)
def verify_shop_permission(request, frontend_user_id=None):
"""
验证商家后台用户身份及店铺状态
参数:
request: DRF request 对象
frontend_user_id: 前端传递的用户ID用于防越权校验可选
返回:
(dianpu_obj, None) # 验证成功,返回店铺对象
(None, Response) # 验证失败,返回错误响应
"""
current_user = request.user
# 1. 从 request.user 获取当前认证用户的 yonghuid
# 注意:您的用户模型可能通过 JWT 的 sub 或其他字段标识,此处假设 request.user.yonghuid 存在
if not hasattr(current_user, 'yonghuid'):
logger.error(f"用户对象缺少 yonghuid 属性: {current_user}")
return None, Response(
{'code': 500, 'msg': '用户身份信息异常'}
)
auth_user_id = current_user.yonghuid
# 2. 越权检测如果前端传了用户ID必须与当前登录用户一致
if frontend_user_id is not None and str(frontend_user_id) != str(auth_user_id):
logger.warning(
f"越权尝试: 登录用户 {auth_user_id} 尝试操作 {frontend_user_id} 的数据"
)
return None, Response(
{'code': 403, 'msg': '无权操作其他用户数据'}
)
# 3. 查询用户凭证表,验证账号是否存在且状态正常
try:
pingzheng = YonghuPingzheng.objects.get(yonghu=auth_user_id)
if not pingzheng.is_active:
return None, Response(
{'code': 403, 'msg': '用户账号已被禁用'}
)
except YonghuPingzheng.DoesNotExist:
return None, Response(
{'code': 404, 'msg': '用户凭证不存在'}
)
# 4. 查询关联的店铺信息,验证店铺状态
try:
dianpu = Dianpu.objects.get(pingzheng=pingzheng)
if dianpu.zhuangtai != 1: # 1 表示正常
return None, Response(
{'code': 403, 'msg': '店铺已被封禁'}
)
except Dianpu.DoesNotExist:
return None, Response(
{'code': 404, 'msg': '店铺信息不存在'}
)
# 验证通过,返回店铺对象
return dianpu, None
'''def update_dianpu_daily_stat(
dianpu_id,
caozuo_leixing,
dingdan_jiage=Decimal('0.00'),
dianpu_shouyi=Decimal('0.00')
):
"""
原子更新店铺每日统计数据,并根据结算操作更新店铺余额与总额(高并发安全)
参数:
dianpu_id: 店铺ID (int)
caozuo_leixing: 操作类型
1 = 下单
2 = 结算(成交)
3 = 退款
dingdan_jiage: 订单金额商品价格默认0.00
dianpu_shouyi: 店铺本次收益金额默认0.00
注意:
- 金额若小于0则自动设为0
- 日期自动取当天
- 使用 select_for_update 配合事务,保证并发时数据绝对准确
- 若店铺不存在,仅记录日志不中断主流程
- 结算操作会同步更新 Dianpu 表的 ketixian_yue 和 zhengqu_zonge
"""
# 参数归一化与合法性处理
dingdan_jiage = Decimal(str(dingdan_jiage)) if dingdan_jiage else Decimal('0.00')
dianpu_shouyi = Decimal(str(dianpu_shouyi)) if dianpu_shouyi else Decimal('0.00')
if dingdan_jiage < 0:
dingdan_jiage = Decimal('0.00')
if dianpu_shouyi < 0:
dianpu_shouyi = Decimal('0.00')
today = date.today()
# 校验店铺是否存在(不加锁,仅校验)
if not Dianpu.objects.filter(id=dianpu_id).exists():
logger.error(f"店铺ID {dianpu_id} 不存在,统计更新失败")
return
# 开启事务,使用行锁确保并发安全
with transaction.atomic():
# 1. 获取或创建统计记录,并锁定该行
tongji, created = DianpuShouzhiMeiriTongji.objects.select_for_update().get_or_create(
dianpu_id=dianpu_id,
riqi=today,
defaults={
'nian': today.year,
'yue': today.month,
'ri': today.day,
'xiadan_zongliang': 0,
'xiadan_zonge': Decimal('0.00'),
'chengjiao_zongliang': 0,
'chengjiao_zonge': Decimal('0.00'),
'tuikuan_zongliang': 0,
'tuikuan_zonge': Decimal('0.00'),
'shouyi_zonge': Decimal('0.00'),
}
)
# 根据操作类型构建统计更新字段
update_fields = {}
if caozuo_leixing == 1: # 下单
update_fields['xiadan_zongliang'] = F('xiadan_zongliang') + 1
update_fields['xiadan_zonge'] = F('xiadan_zonge') + dingdan_jiage
elif caozuo_leixing == 2: # 结算(成交)
update_fields['chengjiao_zongliang'] = F('chengjiao_zongliang') + 1
update_fields['chengjiao_zonge'] = F('chengjiao_zonge') + dingdan_jiage
update_fields['shouyi_zonge'] = F('shouyi_zonge') + dianpu_shouyi
# 结算时同步更新店铺表的可提现余额和赚取总额(需加锁店铺行)
try:
dianpu = Dianpu.objects.select_for_update().get(id=dianpu_id)
dianpu.ketixian_yue = F('ketixian_yue') + dianpu_shouyi
dianpu.zhengqu_zonge = F('zhengqu_zonge') + dianpu_shouyi
dianpu.save(update_fields=['ketixian_yue', 'zhengqu_zonge', 'update_time'])
except Dianpu.DoesNotExist:
logger.error(f"结算时店铺ID {dianpu_id} 不存在,但已校验过,此处不应发生")
raise
elif caozuo_leixing == 3: # 退款
update_fields['tuikuan_zongliang'] = F('tuikuan_zongliang') + 1
update_fields['tuikuan_zonge'] = F('tuikuan_zonge') + dingdan_jiage
# 退款是否扣减收益需根据业务决定,此处只累加退款统计
else:
logger.warning(f"未知的操作类型: {caozuo_leixing}")
return
# 原子更新统计字段
if update_fields:
for field, value in update_fields.items():
setattr(tongji, field, value)
tongji.save(update_fields=list(update_fields.keys()))
logger.info(
f"店铺统计更新: 店铺ID={dianpu_id}, 操作类型={caozuo_leixing}, "
f"订单金额={dingdan_jiage}, 店铺收益={dianpu_shouyi}, 日期={today}"
)
def calculate_pingtai_and_dianpu_shouyi(shangpin_id, dingdan_jiage, dashou_fencheng):
"""
根据商品价格和打手分成计算平台收益和店铺收益
逻辑:
总收益 = 订单金额 - 打手分成
1. 从商品表获取所属店铺ID
2. 若商品无店铺ID平台收益 = 总收益,店铺收益 = 0
3. 若有店铺ID
- 查询店铺配置:
* 若店铺开启抽成,则费率 = 店铺表.shouyi_choucheng_feilv
* 否则从店铺默认配置表(ID=1)获取默认费率,若记录不存在则自动创建
- 平台收益 = 总收益 * 费率
- 店铺收益 = 总收益 - 平台收益
参数:
shangpin_id: 商品ID (int)
dingdan_jiage: 订单金额(商品价格)(Decimal 或 数字)
dashou_fencheng: 打手分成金额 (Decimal 或 数字)
返回:
(dianpu_id, pingtai_shouyi, dianpu_shouyi) 三元组
dianpu_id 可能为 None
pingtai_shouyi 必定有值
dianpu_shouyi 必定有值
"""
# 类型转换与安全处理
dingdan_jiage = Decimal(str(dingdan_jiage)) if dingdan_jiage else Decimal('0.00')
dashou_fencheng = Decimal(str(dashou_fencheng)) if dashou_fencheng else Decimal('0.00')
if dingdan_jiage < 0:
dingdan_jiage = Decimal('0.00')
if dashou_fencheng < 0:
dashou_fencheng = Decimal('0.00')
# 计算总收益(可用于抽成的基础)
zong_shouyi = dingdan_jiage - dashou_fencheng
if zong_shouyi < 0:
zong_shouyi = Decimal('0.00')
# 获取商品信息及所属店铺
try:
shangpin = Shangpin.objects.select_related('dianpu').only('id', 'dianpu').get(id=shangpin_id)
except Shangpin.DoesNotExist:
logger.error(f"计算收益失败商品ID {shangpin_id} 不存在")
return None, zong_shouyi, Decimal('0.00') # 无商品时平台收益为总收益店铺0
dianpu_id = shangpin.dianpu_id
# 无店铺ID平台拿走全部总收益店铺收益为0
if dianpu_id is None:
logger.info(f"商品 {shangpin_id} 未关联店铺,平台收益 = 总收益 {zong_shouyi}")
return None, zong_shouyi, Decimal('0.00')
# 有店铺ID查询店铺抽成配置
try:
dianpu = Dianpu.objects.only(
'id', 'kaiqi_shouyi_choucheng', 'shouyi_choucheng_feilv'
).get(id=dianpu_id)
except Dianpu.DoesNotExist:
logger.error(f"计算收益失败店铺ID {dianpu_id} 不存在")
return dianpu_id, zong_shouyi, Decimal('0.00') # 店铺不存在时平台收益也为总收益
if dianpu.kaiqi_shouyi_choucheng and dianpu.shouyi_choucheng_feilv is not None:
feilv = dianpu.shouyi_choucheng_feilv
else:
# 从默认配置表获取或创建
moren_peizhi, created = DianpuMorenPeizhi.objects.get_or_create(
id=1,
defaults={
'moren_choucheng_feilv': Decimal('0.1000'), # 默认10%
'moren_tixian_xiane': Decimal('1000.00')
}
)
feilv = moren_peizhi.moren_choucheng_feilv
# 确保费率在合法范围内
if feilv is None or feilv < 0:
feilv = Decimal('0.0000')
if feilv > 1:
feilv = Decimal('1.0000')
# 计算平台收益(费率 * 总收益)
pingtai_shouyi = zong_shouyi * feilv
# 店铺收益 = 总收益 - 平台收益
dianpu_shouyi = zong_shouyi - pingtai_shouyi
logger.info(
f"收益计算: 商品ID={shangpin_id}, 订单金额={dingdan_jiage}, "
f"打手分成={dashou_fencheng}, 总收益={zong_shouyi}, 费率={feilv}, "
f"平台收益={pingtai_shouyi}, 店铺收益={dianpu_shouyi}"
)
return dianpu_id, pingtai_shouyi, dianpu_shouyi'''
def update_dianpu_daily_stat(
dianpu_id,
caozuo_leixing,
dingdan_jiage=Decimal('0.00'),
dianpu_shouyi=Decimal('0.00'),
shangji_dianpu_id=None, # 新增上级店铺ID
shangji_fenhong=Decimal('0.00') # 新增:该笔订单给上级的分红
):
"""
原子更新店铺每日统计数据,结算时同步更新店铺余额,并安全处理上级分红统计。
参数:
dianpu_id: 本店铺ID
caozuo_leixing: 1=下单, 2=结算, 3=退款
dingdan_jiage: 订单金额
dianpu_shouyi: 店铺自身收益
shangji_dianpu_id: 上级店铺ID可选
shangji_fenhong: 给上级的分红金额(可选)
注意:
- 金额自动归一化负数取0
- 使用 select_for_update 行锁保证并发安全
- 结算时,本店余额和统计先落定,上级分红统计放在 try 块内,失败不影响本店
- 新增字段均包含在统计表中:给上级次数/总额/上级ID以及作为上级获得次数/总额
"""
# 金额安全处理
dingdan_jiage = Decimal(str(dingdan_jiage)) if dingdan_jiage else Decimal('0.00')
dianpu_shouyi = Decimal(str(dianpu_shouyi)) if dianpu_shouyi else Decimal('0.00')
shangji_fenhong = Decimal(str(shangji_fenhong)) if shangji_fenhong else Decimal('0.00')
if dingdan_jiage < 0: dingdan_jiage = Decimal('0.00')
if dianpu_shouyi < 0: dianpu_shouyi = Decimal('0.00')
if shangji_fenhong < 0: shangji_fenhong = Decimal('0.00')
today = date.today()
# 轻量校验店铺存在性
if not Dianpu.objects.filter(id=dianpu_id).exists():
logger.error(f"店铺ID {dianpu_id} 不存在,统计更新失败")
return
# 开启事务,全部使用行锁
with transaction.atomic():
# 1. 获取或创建本店今日统计记录并锁定
tongji, created = DianpuShouzhiMeiriTongji.objects.select_for_update().get_or_create(
dianpu_id=dianpu_id,
riqi=today,
defaults={
'nian': today.year,
'yue': today.month,
'ri': today.day,
'xiadan_zongliang': 0,
'xiadan_zonge': Decimal('0.00'),
'chengjiao_zongliang': 0,
'chengjiao_zonge': Decimal('0.00'),
'tuikuan_zongliang': 0,
'tuikuan_zonge': Decimal('0.00'),
'shouyi_zonge': Decimal('0.00'),
# 新增字段默认值
'shangji_fenhong_cishu': 0,
'shangji_fenhong_zonge': Decimal('0.00'),
'gei_shangji_cishu': 0,
'gei_shangji_zonge': Decimal('0.00'),
'shangji_id': None,
}
)
# 构建更新字段字典
update_fields_dict = {}
if caozuo_leixing == 1: # 下单
update_fields_dict['xiadan_zongliang'] = F('xiadan_zongliang') + 1
update_fields_dict['xiadan_zonge'] = F('xiadan_zonge') + dingdan_jiage
elif caozuo_leixing == 2: # 结算
# --- 本店成交统计 ---
update_fields_dict['chengjiao_zongliang'] = F('chengjiao_zongliang') + 1
update_fields_dict['chengjiao_zonge'] = F('chengjiao_zonge') + dingdan_jiage
update_fields_dict['shouyi_zonge'] = F('shouyi_zonge') + dianpu_shouyi
# --- 更新本店主表余额(加锁) ---
try:
dianpu_locked = Dianpu.objects.select_for_update().get(id=dianpu_id)
dianpu_locked.ketixian_yue = F('ketixian_yue') + dianpu_shouyi
dianpu_locked.zhengqu_zonge = F('zhengqu_zonge') + dianpu_shouyi
dianpu_locked.save(update_fields=['ketixian_yue', 'zhengqu_zonge', 'update_time'])
except Dianpu.DoesNotExist:
logger.error(f"结算时店铺 {dianpu_id} 不存在")
raise
# --- 处理给上级的分红(本店作为下级)---
if shangji_dianpu_id and shangji_fenhong > 0:
# 更新本店统计的“给上级”字段
update_fields_dict['gei_shangji_cishu'] = F('gei_shangji_cishu') + 1
update_fields_dict['gei_shangji_zonge'] = F('gei_shangji_zonge') + shangji_fenhong
# 设置当天上级店铺ID仅当天第一次结算时写入后续保持不变
current_shangji_id = tongji.shangji_id
if current_shangji_id is None:
update_fields_dict['shangji_id'] = shangji_dianpu_id
elif current_shangji_id != shangji_dianpu_id:
logger.warning(
f"店铺 {dianpu_id} 当天上级ID不一致已有 {current_shangji_id}"
f"新上级 {shangji_dianpu_id}保持原上级ID"
)
# --- 更新上级店铺的“作为上级获得”统计(完全隔离保护) ---
try:
_update_shangji_gain_stat_within_tx(shangji_dianpu_id, shangji_fenhong)
except Exception as e:
logger.error(
f"更新上级店铺 {shangji_dianpu_id} 统计失败,不影响本店: {e}"
)
elif caozuo_leixing == 3: # 退款
update_fields_dict['tuikuan_zongliang'] = F('tuikuan_zongliang') + 1
update_fields_dict['tuikuan_zonge'] = F('tuikuan_zonge') + dingdan_jiage
else:
logger.warning(f"未知操作类型: {caozuo_leixing}")
return
# 原子更新本店统计字段
if update_fields_dict:
for field, value in update_fields_dict.items():
setattr(tongji, field, value)
tongji.save(update_fields=list(update_fields_dict.keys()))
logger.info(
f"店铺统计更新完成: dianpu={dianpu_id}, 操作={caozuo_leixing}, "
f"金额={dingdan_jiage}, 店铺收益={dianpu_shouyi}, "
f"上级ID={shangji_dianpu_id}, 上级分红={shangji_fenhong}"
)
def _update_shangji_gain_stat_within_tx(shangji_dianpu_id, fenhong_jine):
"""
在事务内更新上级店铺的“作为上级获得”统计及主表余额。
注意:本函数必须由处于同一事务中的 update_dianpu_daily_stat 调用,
并且调用方已获取必要的行锁,这里再次加锁不会死锁(按相同顺序加锁)。
"""
today = date.today()
# 获取或创建上级店铺的今日统计记录并加锁
tongji_shangji, _ = DianpuShouzhiMeiriTongji.objects.select_for_update().get_or_create(
dianpu_id=shangji_dianpu_id,
riqi=today,
defaults={
'nian': today.year,
'yue': today.month,
'ri': today.day,
'xiadan_zongliang': 0,
'xiadan_zonge': Decimal('0.00'),
'chengjiao_zongliang': 0,
'chengjiao_zonge': Decimal('0.00'),
'tuikuan_zongliang': 0,
'tuikuan_zonge': Decimal('0.00'),
'shouyi_zonge': Decimal('0.00'),
'shangji_fenhong_cishu': 0,
'shangji_fenhong_zonge': Decimal('0.00'),
'gei_shangji_cishu': 0,
'gei_shangji_zonge': Decimal('0.00'),
'shangji_id': None,
}
)
# 更新上级店铺的“作为上级获得”字段
tongji_shangji.shangji_fenhong_cishu = F('shangji_fenhong_cishu') + 1
tongji_shangji.shangji_fenhong_zonge = F('shangji_fenhong_zonge') + fenhong_jine
tongji_shangji.save(update_fields=['shangji_fenhong_cishu', 'shangji_fenhong_zonge', 'update_time'])
# 更新上级店铺主表的分红余额和累计分红
shangji = Dianpu.objects.select_for_update().get(id=shangji_dianpu_id)
shangji.shangji_fenhong_leiji = F('shangji_fenhong_leiji') + fenhong_jine
shangji.shangji_ketixian_fenhong = F('shangji_ketixian_fenhong') + fenhong_jine
shangji.save(update_fields=['shangji_fenhong_leiji', 'shangji_ketixian_fenhong', 'update_time'])
logger.info(f"上级店铺 {shangji_dianpu_id} 获得分红 +{fenhong_jine}")
def calculate_pingtai_and_dianpu_shouyi(shangpin_id, dingdan_jiage, dashou_fencheng):
"""
计算平台收益、店铺收益、上级分红
分配链路(上级分红完全隔离,不影响平台及店铺核心计算):
总收益 = 订单金额 - 打手分成
平台抽成 = 总收益 * (店铺开启则用店铺费率,否则用平台默认费率)
剩余收益 = 总收益 - 平台抽成
上级分红 = 剩余收益 * 本店设置的上级分红费率(若有上级且满足条件)
店铺收益 = 剩余收益 - 上级分红
参数:
shangpin_id: 商品ID
dingdan_jiage: 订单金额
dashou_fencheng: 打手分成金额
返回:
五元组 (dianpu_id, pingtai_shouyi, dianpu_shouyi, shangji_dianpu_id, shangji_fenhong)
- dianpu_id: 店铺ID可能为None
- pingtai_shouyi: 平台收益
- dianpu_shouyi: 店铺最终收益
- shangji_dianpu_id: 上级店铺ID可能为None
- shangji_fenhong: 上级分红金额默认0.00
"""
# ==================== 金额安全处理 ====================
dingdan_jiage = Decimal(str(dingdan_jiage)) if dingdan_jiage else Decimal('0.00')
dashou_fencheng = Decimal(str(dashou_fencheng)) if dashou_fencheng else Decimal('0.00')
if dingdan_jiage < 0:
dingdan_jiage = Decimal('0.00')
if dashou_fencheng < 0:
dashou_fencheng = Decimal('0.00')
zong_shouyi = dingdan_jiage - dashou_fencheng
if zong_shouyi < 0:
zong_shouyi = Decimal('0.00')
# ==================== 获取商品及店铺 ====================
try:
shangpin = Shangpin.objects.select_related('dianpu').only('id', 'dianpu').get(id=shangpin_id)
except Shangpin.DoesNotExist:
logger.error(f"收益计算失败商品ID {shangpin_id} 不存在")
return None, zong_shouyi, Decimal('0.00'), None, Decimal('0.00')
dianpu_id = shangpin.dianpu_id
if dianpu_id is None:
logger.info(f"商品 {shangpin_id} 未关联店铺,平台收益={zong_shouyi}")
return None, zong_shouyi, Decimal('0.00'), None, Decimal('0.00')
# ==================== 获取店铺配置 ====================
try:
dianpu = Dianpu.objects.only(
'id', 'kaiqi_shouyi_choucheng', 'shouyi_choucheng_feilv',
'yaoqing_dianpu_id', 'fenhong_choucheng_feilv'
).get(id=dianpu_id)
except Dianpu.DoesNotExist:
logger.error(f"收益计算失败店铺ID {dianpu_id} 不存在")
return dianpu_id, zong_shouyi, Decimal('0.00'), None, Decimal('0.00')
# ==================== 平台抽成 ====================
if dianpu.kaiqi_shouyi_choucheng and dianpu.shouyi_choucheng_feilv is not None:
feilv = dianpu.shouyi_choucheng_feilv
else:
moren_peizhi, created = DianpuMorenPeizhi.objects.get_or_create(
id=1,
defaults={
'moren_choucheng_feilv': Decimal('0.1000'),
'moren_tixian_xiane': Decimal('1000.00')
}
)
feilv = moren_peizhi.moren_choucheng_feilv
if feilv is None or feilv < 0:
feilv = Decimal('0.0000')
if feilv > 1:
feilv = Decimal('1.0000')
pingtai_shouyi = zong_shouyi * feilv
shengyu = zong_shouyi - pingtai_shouyi
# ==================== 上级分红(完全隔离,不影响店铺收益) ====================
shangji_dianpu_id = dianpu.yaoqing_dianpu_id
shangji_fenhong = Decimal('0.00')
if shangji_dianpu_id and dianpu.fenhong_choucheng_feilv:
try:
# 检查上级店铺状态
shangji = Dianpu.objects.filter(id=shangji_dianpu_id).only('id', 'zhuangtai').first()
if shangji and shangji.zhuangtai == 1:
fz_rate = dianpu.fenhong_choucheng_feilv
if fz_rate and fz_rate > 0:
shangji_fenhong = shengyu * fz_rate
if shangji_fenhong < 0:
shangji_fenhong = Decimal('0.00')
if shangji_fenhong > shengyu:
shangji_fenhong = shengyu
shengyu -= shangji_fenhong
else:
if shangji:
logger.info(f"上级店铺 {shangji_dianpu_id} 状态非正常,不参与分红")
else:
logger.warning(f"上级店铺ID {shangji_dianpu_id} 不存在,不参与分红")
except Exception as e:
logger.exception(f"上级分红计算异常,忽略: {e}")
shangji_fenhong = Decimal('0.00')
# 注意shengyu 未被修改,店铺收益不变
dianpu_shouyi = shengyu
logger.info(
f"收益计算完成: 商品={shangpin_id}, 总收益={zong_shouyi}, 平台={pingtai_shouyi}, "
f"上级ID={shangji_dianpu_id}, 上级分红={shangji_fenhong}, 店铺={dianpu_shouyi}"
)
return dianpu_id, pingtai_shouyi, dianpu_shouyi, shangji_dianpu_id, shangji_fenhong
def validate_shangpin_and_dianpu(shangpin_id):
"""
校验商品及关联店铺(区分有无店铺)
无店铺:仅校验商品是否上架
有店铺:校验商品审核、封禁、上架,以及店铺是否封禁
返回:
(True, None, shangpin_obj) # 通过
(False, "错误信息", None) # 失败
"""
try:
shangpin = Shangpin.objects.select_related('dianpu').get(id=shangpin_id)
except Shangpin.DoesNotExist:
return False, "商品不存在", None
# 无店铺:只检查上架
if not shangpin.dianpu_id:
if not shangpin.shangjia_zhuangtai:
return False, "商品已下架", None
return True, None, shangpin
# 有店铺:完整校验
if not shangpin.shenhe_zhuangtai:
return False, "商品未通过平台审核", None
if shangpin.fengjin_zhuangtai:
return False, "商品已被封禁", None
if not shangpin.shangjia_zhuangtai:
return False, "商品已下架", None
try:
if shangpin.dianpu.zhuangtai != 1:
return False, "店铺已被封禁", None
except Exception:
return False, "关联店铺异常", None
return True, None, shangpin

3373
shop/views.py Normal file

File diff suppressed because it is too large Load Diff