diff --git a/products/migrations/0014_zhuanpan_kucun_kongjiang.py b/products/migrations/0014_zhuanpan_kucun_kongjiang.py new file mode 100644 index 0000000..d19cc90 --- /dev/null +++ b/products/migrations/0014_zhuanpan_kucun_kongjiang.py @@ -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'), + ), + ] diff --git a/products/models.py b/products/models.py index ed6382e..92b005c 100644 --- a/products/models.py +++ b/products/models.py @@ -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。 diff --git a/products/urls.py b/products/urls.py index bff1e57..aac3327 100644 --- a/products/urls.py +++ b/products/urls.py @@ -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='商家支付成功'), # 商家支付成功轮询 diff --git a/products/views/__init__.py b/products/views/__init__.py index 9e0031d..535335b 100644 --- a/products/views/__init__.py +++ b/products/views/__init__.py @@ -67,6 +67,7 @@ from .zhuanpan_payment import ( ZhuanpanWode, AdZhuanpanHuodongView, AdZhuanpanJiangpinView, + AdZhuanpanKongjiangView, ) from .shangpin_admin import ( ShangpinLeixingFabuView, diff --git a/products/views/zhuanpan_payment.py b/products/views/zhuanpan_payment.py index 7fafd62..e8981c6 100644 --- a/products/views/zhuanpan_payment.py +++ b/products/views/zhuanpan_payment.py @@ -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'})