feat: 转盘按商品重设计(库存、控奖、后台免填商品编号)
转盘即商品自动建绑;奖池支持库存;控奖指定用户必中;xq/xzj 普通点单路径不变。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
50
products/migrations/0014_zhuanpan_kucun_kongjiang.py
Normal file
50
products/migrations/0014_zhuanpan_kucun_kongjiang.py
Normal file
@@ -0,0 +1,50 @@
|
||||
# 转盘奖品库存 + 运营控奖表
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('products', '0013_zhuanpan_and_zhuanqu_icon'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='zhuanpanjiangpin',
|
||||
name='kucun',
|
||||
field=models.IntegerField(
|
||||
default=-1,
|
||||
help_text='-1=不限;0=不可抽中(控奖仍可强制);>0 抽中后减一',
|
||||
verbose_name='剩余库存',
|
||||
),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ZhuanpanKongjiang',
|
||||
fields=[
|
||||
('id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('club_id', models.CharField(db_index=True, max_length=16, verbose_name='俱乐部')),
|
||||
('huodong_id', models.PositiveIntegerField(db_index=True, verbose_name='活动ID')),
|
||||
('yonghuid', models.CharField(db_index=True, max_length=32, verbose_name='用户UID')),
|
||||
('jiangpin_id', models.PositiveIntegerField(db_index=True, verbose_name='指定奖品ID')),
|
||||
('cishu', models.PositiveIntegerField(default=1, verbose_name='剩余次数')),
|
||||
('qiyong', models.BooleanField(db_index=True, default=True, verbose_name='是否启用')),
|
||||
('beizhu', models.CharField(blank=True, default='', max_length=200, verbose_name='备注')),
|
||||
('CreateTime', models.DateTimeField(auto_now_add=True)),
|
||||
('UpdateTime', models.DateTimeField(auto_now=True)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': '转盘控奖',
|
||||
'verbose_name_plural': '转盘控奖',
|
||||
'db_table': 'zhuanpan_kongjiang',
|
||||
},
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='zhuanpankongjiang',
|
||||
index=models.Index(fields=['club_id', 'yonghuid', 'qiyong'], name='zhuanpan_kj_club_uid_qy_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='zhuanpankongjiang',
|
||||
index=models.Index(fields=['huodong_id', 'yonghuid'], name='zhuanpan_kj_hd_uid_idx'),
|
||||
),
|
||||
]
|
||||
@@ -404,13 +404,13 @@ class ShangpinMeiriTongji(QModel):
|
||||
# ===========================================================================
|
||||
|
||||
class ZhuanpanHuodong(QModel):
|
||||
"""转盘活动:一个俱乐部可有多个;购票价格取关联商品 jiage。"""
|
||||
"""转盘活动:与转盘商品 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',
|
||||
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='备注')
|
||||
@@ -425,7 +425,7 @@ class ZhuanpanHuodong(QModel):
|
||||
|
||||
|
||||
class ZhuanpanJiangpin(QModel):
|
||||
"""转盘奖品格:权重越高越易中;shi_xiexie=True 表示谢谢参与(不生成订单)。"""
|
||||
"""转盘奖品格:权重越高越易中;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='奖品名')
|
||||
@@ -434,6 +434,10 @@ class ZhuanpanJiangpin(QModel):
|
||||
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)
|
||||
@@ -446,6 +450,29 @@ class ZhuanpanJiangpin(QModel):
|
||||
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。
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ from .views import ShangpinHuoquView, ShangpinXiangqingHuoquView, DashouHuiyuanL
|
||||
HuiyuanTypeListView, HuiyuanRecordListView, ZuzhangFenhongView, CzhqdyView, DsqrgmdhView, \
|
||||
AlipayCzjiluNotifyView, CzjiluRepairView, \
|
||||
ZhuanpanGoumai, ZhuanpanLunxun, ZhuanpanShibai, ZhuanpanHuitiao, ZhuanpanJiangpinList, \
|
||||
ZhuanpanChoujiang, ZhuanpanWode, AdZhuanpanHuodongView, AdZhuanpanJiangpinView
|
||||
ZhuanpanChoujiang, ZhuanpanWode, AdZhuanpanHuodongView, AdZhuanpanJiangpinView, AdZhuanpanKongjiangView
|
||||
|
||||
urlpatterns = [
|
||||
#path('shangpinhuoqu/', ShangpinHuoquView.as_view(), name='获取商品数据商品类型和专区'),
|
||||
@@ -51,6 +51,7 @@ urlpatterns = [
|
||||
path('zhuanpanwode', ZhuanpanWode.as_view(), name='我的转盘机会'),
|
||||
path('adzhuanpanhd', AdZhuanpanHuodongView.as_view(), name='后台转盘活动'),
|
||||
path('adzhuanpanjp', AdZhuanpanJiangpinView.as_view(), name='后台转盘奖品'),
|
||||
path('adzhuanpankj', AdZhuanpanKongjiangView.as_view(), name='后台转盘控奖'),
|
||||
#path('sjhuitiao', ShangjiaHuitiao.as_view(), name='商家支付官方回调'), # 商家支付回调
|
||||
path('sjshibai', ShangjiaShibai.as_view(), name='商家支付失败'), # 商家支付失败处理
|
||||
path('sjcg', ShangjiaChenggong.as_view(), name='商家支付成功'), # 商家支付成功轮询
|
||||
|
||||
@@ -67,6 +67,7 @@ from .zhuanpan_payment import (
|
||||
ZhuanpanWode,
|
||||
AdZhuanpanHuodongView,
|
||||
AdZhuanpanJiangpinView,
|
||||
AdZhuanpanKongjiangView,
|
||||
)
|
||||
from .shangpin_admin import (
|
||||
ShangpinLeixingFabuView,
|
||||
|
||||
@@ -39,7 +39,8 @@ from products.czjilu_types import (
|
||||
close_unpaid_czjilu,
|
||||
)
|
||||
from products.models import (
|
||||
Czjilu, Shangpin, ZhuanpanHuodong, ZhuanpanJiangpin, ZhuanpanJihui,
|
||||
Czjilu, Shangpin, ShangpinLeixing, ShangpinZhuanqu,
|
||||
ZhuanpanHuodong, ZhuanpanJiangpin, ZhuanpanJihui, ZhuanpanKongjiang,
|
||||
)
|
||||
from utils.money import yuan_to_fen
|
||||
|
||||
@@ -54,17 +55,74 @@ def _gen_czzp_id():
|
||||
|
||||
|
||||
def _pick_jiangpin(jiangpin_list):
|
||||
"""按权重抽奖,返回选中的奖品对象。"""
|
||||
total = sum(max(int(j.quanzhong or 0), 0) for j in jiangpin_list)
|
||||
"""按权重抽奖(仅库存未耗尽:kucun==-1 或不限 / kucun>0)。"""
|
||||
pool = [j for j in jiangpin_list if int(getattr(j, 'kucun', -1)) != 0]
|
||||
if not pool:
|
||||
raise ValueError('奖品库存已全部抽完')
|
||||
total = sum(max(int(j.quanzhong or 0), 0) for j in pool)
|
||||
if total <= 0:
|
||||
return jiangpin_list[0]
|
||||
return pool[0]
|
||||
r = random.randint(1, total)
|
||||
acc = 0
|
||||
for j in jiangpin_list:
|
||||
for j in pool:
|
||||
acc += max(int(j.quanzhong or 0), 0)
|
||||
if r <= acc:
|
||||
return j
|
||||
return jiangpin_list[-1]
|
||||
return pool[-1]
|
||||
|
||||
|
||||
def _consume_jiangpin_stock(jiangpin):
|
||||
"""普通抽中或控奖命中后扣库存;-1 不限不扣;0 不扣(控奖可强制)。"""
|
||||
k = int(getattr(jiangpin, 'kucun', -1))
|
||||
if k > 0:
|
||||
jiangpin.kucun = k - 1
|
||||
jiangpin.save(update_fields=['kucun', 'UpdateTime'])
|
||||
|
||||
|
||||
def _take_kongjiang(club_id, huodong_id, yonghuid):
|
||||
"""取一条可用控奖并扣减次数;返回奖品对象或 None。"""
|
||||
row = (
|
||||
ZhuanpanKongjiang.query.select_for_update()
|
||||
.filter(
|
||||
club_id=club_id,
|
||||
huodong_id=huodong_id,
|
||||
yonghuid=yonghuid,
|
||||
qiyong=True,
|
||||
cishu__gt=0,
|
||||
)
|
||||
.order_by('id')
|
||||
.first()
|
||||
)
|
||||
if not row:
|
||||
return None
|
||||
prize = ZhuanpanJiangpin.query.filter(id=row.jiangpin_id, huodong_id=huodong_id).first()
|
||||
if not prize:
|
||||
row.qiyong = False
|
||||
row.save(update_fields=['qiyong', 'UpdateTime'])
|
||||
return None
|
||||
row.cishu = max(int(row.cishu or 0) - 1, 0)
|
||||
if row.cishu <= 0:
|
||||
row.qiyong = False
|
||||
row.save(update_fields=['cishu', 'qiyong', 'UpdateTime'])
|
||||
return prize
|
||||
|
||||
|
||||
def _serialize_huodong_row(h, ticket=None):
|
||||
if ticket is None:
|
||||
ticket = Shangpin.query.filter(id=h.shangpin_id).first()
|
||||
return {
|
||||
'id': h.id,
|
||||
'mingzi': h.mingzi,
|
||||
'shangpin_id': h.shangpin_id,
|
||||
'qiyong': h.qiyong,
|
||||
'beizhu': h.beizhu,
|
||||
'biaoqian': (ticket.biaoqian if ticket else '') or h.mingzi,
|
||||
'jiage': str(ticket.jiage) if ticket else '0',
|
||||
'tupian_url': (ticket.tupian_url if ticket else '') or '',
|
||||
'leixing_id': ticket.leixing_id if ticket else None,
|
||||
'zhuanqu_id': ticket.zhuanqu_id if ticket else None,
|
||||
'shangjia_zhuangtai': bool(ticket.shangjia_zhuangtai) if ticket else False,
|
||||
}
|
||||
|
||||
|
||||
def _create_pool_order_from_prize(jihui, jiangpin, request_user):
|
||||
@@ -448,12 +506,19 @@ class ZhuanpanChoujiang(APIView):
|
||||
}, status=400)
|
||||
|
||||
prizes = list(
|
||||
ZhuanpanJiangpin.query.filter(huodong_id=jihui.huodong_id).order_by('paixu', 'id')
|
||||
ZhuanpanJiangpin.query.select_for_update()
|
||||
.filter(huodong_id=jihui.huodong_id)
|
||||
.order_by('paixu', 'id')
|
||||
)
|
||||
if not prizes:
|
||||
return Response({'code': 400, 'message': '奖品未配置'}, status=400)
|
||||
|
||||
win = _pick_jiangpin(prizes)
|
||||
# 1) 控奖优先 2) 权重+库存
|
||||
win = _take_kongjiang(jihui.club_id, jihui.huodong_id, jihui.yonghuid)
|
||||
if win is None:
|
||||
win = _pick_jiangpin(prizes)
|
||||
_consume_jiangpin_stock(win)
|
||||
|
||||
dingdan_id = ''
|
||||
if not win.shi_xiexie and win.shangpin_id:
|
||||
dingdan_id = _create_pool_order_from_prize(jihui, win, request.user)
|
||||
@@ -515,52 +580,140 @@ class ZhuanpanWode(APIView):
|
||||
|
||||
|
||||
class AdZhuanpanHuodongView(APIView):
|
||||
"""后台:转盘活动 list/save。POST action=list|save|toggle"""
|
||||
"""后台:转盘商品(=活动) list / create_product / save / toggle / meta。
|
||||
|
||||
运营不再手填购票商品编号:创建时直接填名称/价格/类型,自动建 Shangpin+Huodong。
|
||||
"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
action = (request.data.get('action') or 'list').strip()
|
||||
club_id = resolve_effective_club_id(request)
|
||||
if action == 'list':
|
||||
rows = list(ZhuanpanHuodong.query.filter(club_id=club_id).order_by('-id'))
|
||||
|
||||
if action == 'meta':
|
||||
types = list(
|
||||
ShangpinLeixing.query.order_by('id').values('id', 'jieshao')
|
||||
)
|
||||
zones = list(
|
||||
ShangpinZhuanqu.query.filter(club_id=club_id)
|
||||
.order_by('-paixu', 'id')
|
||||
.values('id', 'mingzi', 'leixing_id')
|
||||
)
|
||||
# 履约商品:本俱乐部非转盘购票商品
|
||||
goods = list(
|
||||
Shangpin.query.filter(club_id=club_id, shi_zhuanpan=False)
|
||||
.order_by('-paixu', '-id')
|
||||
.values('id', 'biaoqian', 'jiage', 'tupian_url', 'leixing_id')[:500]
|
||||
)
|
||||
for g in goods:
|
||||
g['jiage'] = str(g.get('jiage') or 0)
|
||||
return Response({
|
||||
'code': 0,
|
||||
'data': [
|
||||
{
|
||||
'id': h.id,
|
||||
'mingzi': h.mingzi,
|
||||
'shangpin_id': h.shangpin_id,
|
||||
'qiyong': h.qiyong,
|
||||
'beizhu': h.beizhu,
|
||||
}
|
||||
for h in rows
|
||||
],
|
||||
'data': {'leixing': types, 'zhuanqu': zones, 'goods': goods},
|
||||
})
|
||||
if action == 'save':
|
||||
hid = request.data.get('id')
|
||||
mingzi = (request.data.get('mingzi') or '幸运转盘').strip()
|
||||
shangpin_id = int(request.data.get('shangpin_id') or 0)
|
||||
|
||||
if action == 'list':
|
||||
rows = list(ZhuanpanHuodong.query.filter(club_id=club_id).order_by('-id'))
|
||||
sp_ids = [h.shangpin_id for h in rows]
|
||||
tickets = {
|
||||
s.id: s
|
||||
for s in Shangpin.query.filter(id__in=sp_ids, club_id=club_id)
|
||||
} if sp_ids else {}
|
||||
return Response({
|
||||
'code': 0,
|
||||
'data': [_serialize_huodong_row(h, tickets.get(h.shangpin_id)) for h in rows],
|
||||
})
|
||||
|
||||
if action == 'create_product':
|
||||
biaoqian = (request.data.get('biaoqian') or request.data.get('mingzi') or '').strip()
|
||||
mingzi = (request.data.get('mingzi') or biaoqian or '幸运转盘').strip()
|
||||
try:
|
||||
jiage = Decimal(str(request.data.get('jiage') or 0))
|
||||
except Exception:
|
||||
return Response({'code': 1, 'msg': '价格格式错误'})
|
||||
if not biaoqian:
|
||||
return Response({'code': 1, 'msg': '请填写转盘商品名称'})
|
||||
if jiage <= 0:
|
||||
return Response({'code': 1, 'msg': '价格必须大于 0'})
|
||||
leixing_id = request.data.get('leixing_id')
|
||||
if not leixing_id:
|
||||
return Response({'code': 1, 'msg': '请选择商品类型'})
|
||||
try:
|
||||
leixing_id = int(leixing_id)
|
||||
except (TypeError, ValueError):
|
||||
return Response({'code': 1, 'msg': '商品类型无效'})
|
||||
zhuanqu_id = request.data.get('zhuanqu_id') or None
|
||||
if zhuanqu_id in ('', None):
|
||||
zhuanqu_id = None
|
||||
else:
|
||||
zhuanqu_id = int(zhuanqu_id)
|
||||
tupian_url = (request.data.get('tupian_url') or '').strip()
|
||||
beizhu = (request.data.get('beizhu') or '').strip()
|
||||
qiyong = bool(request.data.get('qiyong', True))
|
||||
if not shangpin_id:
|
||||
return Response({'code': 1, 'msg': '需要 shangpin_id'})
|
||||
# 标记购票商品
|
||||
Shangpin.query.filter(id=shangpin_id, club_id=club_id).update(shi_zhuanpan=True)
|
||||
if hid:
|
||||
h = ZhuanpanHuodong.query.filter(id=int(hid), club_id=club_id).first()
|
||||
if not h:
|
||||
return Response({'code': 1, 'msg': '活动不存在'})
|
||||
h.mingzi = mingzi
|
||||
h.shangpin_id = shangpin_id
|
||||
h.beizhu = beizhu
|
||||
h.qiyong = qiyong
|
||||
h.save()
|
||||
else:
|
||||
h = ZhuanpanHuodong.query.create(
|
||||
club_id=club_id, mingzi=mingzi, shangpin_id=shangpin_id,
|
||||
beizhu=beizhu, qiyong=qiyong,
|
||||
|
||||
with transaction.atomic():
|
||||
ticket = Shangpin.query.create(
|
||||
biaoqian=biaoqian[:45],
|
||||
jiage=jiage,
|
||||
kucun=99999,
|
||||
leixing_id=leixing_id,
|
||||
zhuanqu_id=zhuanqu_id,
|
||||
jieshao=beizhu or '转盘购票商品',
|
||||
tupian_url=tupian_url,
|
||||
club_id=club_id,
|
||||
shi_zhuanpan=True,
|
||||
shangjia_zhuangtai=True,
|
||||
fengjin_zhuangtai=True,
|
||||
shenhezhuangtai=1,
|
||||
paixu=0,
|
||||
)
|
||||
return Response({'code': 0, 'data': {'id': h.id}})
|
||||
h = ZhuanpanHuodong.query.create(
|
||||
club_id=club_id,
|
||||
mingzi=mingzi[:64],
|
||||
shangpin_id=ticket.id,
|
||||
beizhu=beizhu,
|
||||
qiyong=qiyong,
|
||||
)
|
||||
return Response({'code': 0, 'data': _serialize_huodong_row(h, ticket)})
|
||||
|
||||
if action == 'save':
|
||||
hid = request.data.get('id')
|
||||
if not hid:
|
||||
return Response({'code': 1, 'msg': '缺少 id,新建请用 create_product'})
|
||||
h = ZhuanpanHuodong.query.filter(id=int(hid), club_id=club_id).first()
|
||||
if not h:
|
||||
return Response({'code': 1, 'msg': '转盘不存在'})
|
||||
ticket = Shangpin.query.filter(id=h.shangpin_id, club_id=club_id).first()
|
||||
mingzi = (request.data.get('mingzi') or h.mingzi).strip()
|
||||
biaoqian = (request.data.get('biaoqian') or (ticket.biaoqian if ticket else mingzi) or mingzi).strip()
|
||||
beizhu = request.data.get('beizhu')
|
||||
if beizhu is None:
|
||||
beizhu = h.beizhu
|
||||
else:
|
||||
beizhu = str(beizhu).strip()
|
||||
qiyong = bool(request.data.get('qiyong', h.qiyong))
|
||||
tupian_url = request.data.get('tupian_url')
|
||||
jiage_raw = request.data.get('jiage')
|
||||
|
||||
h.mingzi = mingzi[:64]
|
||||
h.beizhu = beizhu
|
||||
h.qiyong = qiyong
|
||||
h.save()
|
||||
if ticket:
|
||||
ticket.biaoqian = biaoqian[:45]
|
||||
ticket.shi_zhuanpan = True
|
||||
if tupian_url is not None:
|
||||
ticket.tupian_url = str(tupian_url).strip()
|
||||
if jiage_raw is not None and str(jiage_raw).strip() != '':
|
||||
try:
|
||||
jv = Decimal(str(jiage_raw))
|
||||
if jv > 0:
|
||||
ticket.jiage = jv
|
||||
except Exception:
|
||||
return Response({'code': 1, 'msg': '价格格式错误'})
|
||||
ticket.save()
|
||||
return Response({'code': 0, 'data': _serialize_huodong_row(h, ticket)})
|
||||
|
||||
if action == 'toggle':
|
||||
hid = int(request.data.get('id') or 0)
|
||||
h = ZhuanpanHuodong.query.filter(id=hid, club_id=club_id).first()
|
||||
@@ -568,12 +721,17 @@ class AdZhuanpanHuodongView(APIView):
|
||||
return Response({'code': 1, 'msg': '不存在'})
|
||||
h.qiyong = not h.qiyong
|
||||
h.save(update_fields=['qiyong', 'UpdateTime'])
|
||||
ticket = Shangpin.query.filter(id=h.shangpin_id, club_id=club_id).first()
|
||||
if ticket:
|
||||
ticket.shangjia_zhuangtai = h.qiyong
|
||||
ticket.save(update_fields=['shangjia_zhuangtai', 'UpdateTime'])
|
||||
return Response({'code': 0, 'data': {'qiyong': h.qiyong}})
|
||||
|
||||
return Response({'code': 1, 'msg': '未知 action'})
|
||||
|
||||
|
||||
class AdZhuanpanJiangpinView(APIView):
|
||||
"""后台:奖品 list/save/delete。"""
|
||||
"""后台:奖品 list/save/delete(含库存;履约商品用下拉 id,不手填语义)。"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
@@ -582,10 +740,17 @@ class AdZhuanpanJiangpinView(APIView):
|
||||
huodong_id = int(request.data.get('huodong_id') or 0)
|
||||
huodong = ZhuanpanHuodong.query.filter(id=huodong_id, club_id=club_id).first()
|
||||
if not huodong:
|
||||
return Response({'code': 1, 'msg': '活动不存在或不属于当前俱乐部'})
|
||||
return Response({'code': 1, 'msg': '转盘不存在或不属于当前俱乐部'})
|
||||
|
||||
if action == 'list':
|
||||
rows = list(ZhuanpanJiangpin.query.filter(huodong_id=huodong_id).order_by('paixu', 'id'))
|
||||
fulfill_ids = [p.shangpin_id for p in rows if p.shangpin_id]
|
||||
name_map = {}
|
||||
if fulfill_ids:
|
||||
name_map = {
|
||||
s.id: s.biaoqian
|
||||
for s in Shangpin.query.filter(id__in=fulfill_ids, club_id=club_id)
|
||||
}
|
||||
return Response({
|
||||
'code': 0,
|
||||
'data': [
|
||||
@@ -594,7 +759,9 @@ class AdZhuanpanJiangpinView(APIView):
|
||||
'mingzi': p.mingzi,
|
||||
'tupian_url': p.tupian_url,
|
||||
'shangpin_id': p.shangpin_id,
|
||||
'shangpin_biaoqian': name_map.get(p.shangpin_id, '') if p.shangpin_id else '',
|
||||
'quanzhong': p.quanzhong,
|
||||
'kucun': int(getattr(p, 'kucun', -1)),
|
||||
'paixu': p.paixu,
|
||||
'shi_xiexie': p.shi_xiexie,
|
||||
}
|
||||
@@ -603,11 +770,17 @@ class AdZhuanpanJiangpinView(APIView):
|
||||
})
|
||||
if action == 'save':
|
||||
pid = request.data.get('id')
|
||||
kucun_raw = request.data.get('kucun', -1)
|
||||
try:
|
||||
kucun = int(kucun_raw)
|
||||
except (TypeError, ValueError):
|
||||
kucun = -1
|
||||
data = dict(
|
||||
mingzi=(request.data.get('mingzi') or '').strip() or '奖品',
|
||||
tupian_url=(request.data.get('tupian_url') or '').strip(),
|
||||
shangpin_id=request.data.get('shangpin_id') or None,
|
||||
quanzhong=int(request.data.get('quanzhong') or 1),
|
||||
kucun=kucun,
|
||||
paixu=int(request.data.get('paixu') or 0),
|
||||
shi_xiexie=bool(request.data.get('shi_xiexie', False)),
|
||||
)
|
||||
@@ -615,6 +788,14 @@ class AdZhuanpanJiangpinView(APIView):
|
||||
data['shangpin_id'] = None
|
||||
else:
|
||||
data['shangpin_id'] = int(data['shangpin_id'])
|
||||
# 履约商品必须属于本俱乐部且不能是转盘购票商品
|
||||
ful = Shangpin.query.filter(id=data['shangpin_id'], club_id=club_id).first()
|
||||
if not ful:
|
||||
return Response({'code': 1, 'msg': '履约商品不存在'})
|
||||
if getattr(ful, 'shi_zhuanpan', False):
|
||||
return Response({'code': 1, 'msg': '不能把转盘购票商品设为履约商品'})
|
||||
if data['shi_xiexie']:
|
||||
data['shangpin_id'] = None
|
||||
if pid:
|
||||
p = ZhuanpanJiangpin.query.filter(id=int(pid), huodong_id=huodong_id).first()
|
||||
if not p:
|
||||
@@ -628,5 +809,89 @@ class AdZhuanpanJiangpinView(APIView):
|
||||
if action == 'delete':
|
||||
pid = int(request.data.get('id') or 0)
|
||||
ZhuanpanJiangpin.query.filter(id=pid, huodong_id=huodong_id).delete()
|
||||
ZhuanpanKongjiang.query.filter(jiangpin_id=pid, huodong_id=huodong_id).delete()
|
||||
return Response({'code': 0})
|
||||
return Response({'code': 1, 'msg': '未知 action'})
|
||||
|
||||
|
||||
class AdZhuanpanKongjiangView(APIView):
|
||||
"""后台控奖:指定用户必中某奖若干次。"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request):
|
||||
action = (request.data.get('action') or 'list').strip()
|
||||
club_id = resolve_effective_club_id(request)
|
||||
huodong_id = int(request.data.get('huodong_id') or 0)
|
||||
huodong = ZhuanpanHuodong.query.filter(id=huodong_id, club_id=club_id).first()
|
||||
if not huodong:
|
||||
return Response({'code': 1, 'msg': '转盘不存在或不属于当前俱乐部'})
|
||||
|
||||
if action == 'list':
|
||||
rows = list(
|
||||
ZhuanpanKongjiang.query.filter(huodong_id=huodong_id, club_id=club_id)
|
||||
.order_by('-id')[:200]
|
||||
)
|
||||
jp_ids = [r.jiangpin_id for r in rows]
|
||||
jp_map = {
|
||||
p.id: p.mingzi
|
||||
for p in ZhuanpanJiangpin.query.filter(id__in=jp_ids, huodong_id=huodong_id)
|
||||
} if jp_ids else {}
|
||||
return Response({
|
||||
'code': 0,
|
||||
'data': [
|
||||
{
|
||||
'id': r.id,
|
||||
'yonghuid': r.yonghuid,
|
||||
'jiangpin_id': r.jiangpin_id,
|
||||
'jiangpin_mingzi': jp_map.get(r.jiangpin_id, ''),
|
||||
'cishu': r.cishu,
|
||||
'qiyong': r.qiyong,
|
||||
'beizhu': r.beizhu,
|
||||
}
|
||||
for r in rows
|
||||
],
|
||||
})
|
||||
if action == 'save':
|
||||
yonghuid = str(request.data.get('yonghuid') or '').strip()
|
||||
jiangpin_id = int(request.data.get('jiangpin_id') or 0)
|
||||
cishu = int(request.data.get('cishu') or 1)
|
||||
beizhu = (request.data.get('beizhu') or '').strip()
|
||||
qiyong = bool(request.data.get('qiyong', True))
|
||||
if not yonghuid:
|
||||
return Response({'code': 1, 'msg': '请填写用户 UID'})
|
||||
if cishu < 1:
|
||||
return Response({'code': 1, 'msg': '次数至少为 1'})
|
||||
prize = ZhuanpanJiangpin.query.filter(id=jiangpin_id, huodong_id=huodong_id).first()
|
||||
if not prize:
|
||||
return Response({'code': 1, 'msg': '奖品不存在'})
|
||||
kid = request.data.get('id')
|
||||
if kid:
|
||||
row = ZhuanpanKongjiang.query.filter(
|
||||
id=int(kid), huodong_id=huodong_id, club_id=club_id
|
||||
).first()
|
||||
if not row:
|
||||
return Response({'code': 1, 'msg': '控奖记录不存在'})
|
||||
row.yonghuid = yonghuid
|
||||
row.jiangpin_id = jiangpin_id
|
||||
row.cishu = cishu
|
||||
row.beizhu = beizhu
|
||||
row.qiyong = qiyong
|
||||
row.save()
|
||||
else:
|
||||
row = ZhuanpanKongjiang.query.create(
|
||||
club_id=club_id,
|
||||
huodong_id=huodong_id,
|
||||
yonghuid=yonghuid,
|
||||
jiangpin_id=jiangpin_id,
|
||||
cishu=cishu,
|
||||
beizhu=beizhu,
|
||||
qiyong=qiyong,
|
||||
)
|
||||
return Response({'code': 0, 'data': {'id': row.id}})
|
||||
if action == 'delete':
|
||||
kid = int(request.data.get('id') or 0)
|
||||
ZhuanpanKongjiang.query.filter(
|
||||
id=kid, huodong_id=huodong_id, club_id=club_id
|
||||
).delete()
|
||||
return Response({'code': 0})
|
||||
return Response({'code': 1, 'msg': '未知 action'})
|
||||
|
||||
Reference in New Issue
Block a user