feat: 龙先生审核版游戏护航商品种子脚本
仅 lxs;shenhezhuangtai=2;正向陪伴文案;默认头像图;dry-run + 双确认。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
311
jituan/management/commands/seed_lxs_shenhe_huhang_catalog.py
Normal file
311
jituan/management/commands/seed_lxs_shenhe_huhang_catalog.py
Normal file
@@ -0,0 +1,311 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
为龙先生电竞(lxs)写入审核专用(shenhezhuangtai=2)「游戏护航/陪伴」公共商品目录。
|
||||
|
||||
范围:
|
||||
- 仅 club_id=lxs(名称须含「龙先生」)
|
||||
- 全局审核类型:取 ShangpinLeixing 中 shenhezhuangtai=2 的第一条;若无则创建「娱乐护航」
|
||||
- 创建若干审核专区;30 个商品主要挂在第一个专区
|
||||
- 商品图统一用默认头像相对路径 a_long/morentouxiang.jpg
|
||||
- 文案仅正向「护航/陪伴/开黑」,绝不出现代练、代打、上分等
|
||||
|
||||
不碰:正常商品(shenhezhuangtai=1)、其它俱乐部、店铺商品。
|
||||
|
||||
用法:
|
||||
python manage.py seed_lxs_shenhe_huhang_catalog
|
||||
python manage.py seed_lxs_shenhe_huhang_catalog --apply --i-confirm-club=lxs
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
from django.core.management.base import BaseCommand, CommandError
|
||||
from django.db import transaction
|
||||
|
||||
ALLOWED_CLUB_ID = 'lxs'
|
||||
NAME_HINT = '龙先生'
|
||||
DEFAULT_AVATAR = 'a_long/morentouxiang.jpg'
|
||||
SHENHE = 2 # 审核专用
|
||||
|
||||
# 专区(审核版)
|
||||
ZONE_DEFS = [
|
||||
{'mingzi': '休闲开黑', 'paixu': 30},
|
||||
{'mingzi': '语音陪伴', 'paixu': 20},
|
||||
{'mingzi': '团队协作', 'paixu': 10},
|
||||
]
|
||||
|
||||
# 30 个正向护航/陪伴向商品(标题 + 介绍 + 价格),禁止代练代打语义
|
||||
PRODUCT_DEFS = [
|
||||
('王者荣耀·休闲开黑陪伴', '友好组队开黑,轻松娱乐,语音交流,快乐优先。', '19.90'),
|
||||
('和平精英·小队同行', '休闲模式组队畅玩,互相配合,体验团队乐趣。', '19.90'),
|
||||
('原神·探索同行', '一起探索地图与活动,聊天解谜,轻松愉快。', '29.90'),
|
||||
('星穹铁道·剧情陪伴', '陪你体验剧情与活动关卡,节奏友好,交流顺畅。', '29.90'),
|
||||
('无畏契约·娱乐五排', '娱乐局友好开黑,沟通协作,享受对局过程。', '25.90'),
|
||||
('英雄联盟·娱乐双排', '休闲娱乐局陪伴,友好沟通,重在开心。', '25.90'),
|
||||
('永劫无间·双人协作', '双人娱乐局同行,互相支援,体验动作乐趣。', '22.90'),
|
||||
('三角洲行动·小队协作', '小队任务友好配合,沟通顺畅,快乐完成目标。', '29.90'),
|
||||
('蛋仔派对·欢乐同乐', '休闲派对模式一起玩,轻松互动,气氛友好。', '15.90'),
|
||||
('逆水寒·社交同游', '一起游历风景与活动,聊天社交,轻松惬意。', '29.90'),
|
||||
('鸣潮·旅途陪伴', '结伴体验开放世界内容,节奏舒适,友好交流。', '29.90'),
|
||||
('绝区零·日常同行', '日常内容一起体验,互相提醒,轻松完成。', '22.90'),
|
||||
('崩坏3·活动陪伴', '活动关卡友好同行,沟通配合,享受剧情。', '19.90'),
|
||||
('命运冠位指定·活动同行', '活动副本休闲体验,节奏友好,互相鼓励。', '19.90'),
|
||||
('明日方舟·关卡陪伴', '关卡推图友好协助,讲解思路,轻松通关体验。', '22.90'),
|
||||
('第五人格·娱乐开黑', '娱乐模式组队,友好沟通,享受对抗乐趣。', '19.90'),
|
||||
('CS2·休闲约战', '休闲模式友好对局,语音交流,重在体验。', '25.90'),
|
||||
('Dota2·娱乐组队', '娱乐局结伴开黑,沟通协作,快乐优先。', '25.90'),
|
||||
('炉石传说·休闲对局', '休闲模式一起玩,交流构筑心得,气氛轻松。', '15.90'),
|
||||
('金铲铲之战·娱乐对局', '娱乐模式畅玩,聊天互动,轻松愉快。', '15.90'),
|
||||
('穿越火线·休闲开黑', '休闲模式组队,友好沟通,享受射击乐趣。', '19.90'),
|
||||
('CFHD·娱乐同行', '娱乐局结伴,语音交流,过程友好。', '19.90'),
|
||||
('NBA2K·友谊赛陪伴', '友谊赛模式一起打,轻松互动,享受比赛。', '22.90'),
|
||||
('FIFA·友谊赛同行', '友谊赛友好对战,交流心得,气氛轻松。', '22.90'),
|
||||
('我的世界·建造同行', '一起建造与探索,创意交流,氛围友好。', '19.90'),
|
||||
('幻兽帕鲁·合作体验', '合作模式结伴体验,互相帮助,轻松有趣。', '19.90'),
|
||||
('双人成行·合作通关', '合作解谜一起玩,沟通配合,欢乐通关。', '29.90'),
|
||||
('It Takes Two·双人协作', '双人关卡友好协作,互动解谜,享受过程。', '29.90'),
|
||||
('斯普拉遁·休闲对战', '休闲对战结伴,轻松交流,享受涂色乐趣。', '15.90'),
|
||||
('动物森友会·岛上同游', '一起逛岛、装饰与活动,聊天放松,氛围温馨。', '19.90'),
|
||||
]
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = '龙先生(lxs)审核版护航商品种子(默认 dry-run)'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('--apply', action='store_true', help='真正写库')
|
||||
parser.add_argument(
|
||||
'--i-confirm-club',
|
||||
type=str,
|
||||
default='',
|
||||
help='必须等于 lxs 才允许 --apply',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--club-id',
|
||||
type=str,
|
||||
default=ALLOWED_CLUB_ID,
|
||||
help='仅允许 lxs',
|
||||
)
|
||||
|
||||
def handle(self, *args, **options):
|
||||
from jituan.models import Club
|
||||
from products.models import Shangpin, ShangpinLeixing, ShangpinZhuanqu
|
||||
|
||||
club_id = (options.get('club_id') or '').strip()
|
||||
apply = bool(options.get('apply'))
|
||||
confirm = (options.get('i_confirm_club') or '').strip()
|
||||
|
||||
if club_id != ALLOWED_CLUB_ID:
|
||||
raise CommandError(
|
||||
f'安全拦截:只允许 club_id={ALLOWED_CLUB_ID!r},你传的是 {club_id!r}'
|
||||
)
|
||||
|
||||
club = Club.query.filter(club_id=ALLOWED_CLUB_ID).first()
|
||||
if not club:
|
||||
raise CommandError(f'俱乐部 {ALLOWED_CLUB_ID} 不存在')
|
||||
if NAME_HINT not in (club.name or ''):
|
||||
raise CommandError(
|
||||
f'安全拦截:{ALLOWED_CLUB_ID} 的 name={club.name!r} 不含「{NAME_HINT}」'
|
||||
)
|
||||
|
||||
if apply and confirm != ALLOWED_CLUB_ID:
|
||||
raise CommandError(
|
||||
'安全拦截:--apply 必须带 --i-confirm-club=lxs\n'
|
||||
' python manage.py seed_lxs_shenhe_huhang_catalog --apply --i-confirm-club=lxs'
|
||||
)
|
||||
|
||||
# 审核类型:第一条;没有则计划创建
|
||||
leixing = (
|
||||
ShangpinLeixing.query
|
||||
.filter(shenhezhuangtai=SHENHE)
|
||||
.order_by('id')
|
||||
.first()
|
||||
)
|
||||
leixing_action = 'REUSE'
|
||||
if not leixing:
|
||||
leixing_action = 'CREATE'
|
||||
leixing = None
|
||||
|
||||
# 专区计划
|
||||
zone_plans = []
|
||||
for zdef in ZONE_DEFS:
|
||||
exist = ShangpinZhuanqu.query.filter(
|
||||
club_id=ALLOWED_CLUB_ID,
|
||||
mingzi=zdef['mingzi'],
|
||||
shenhezhuangtai=SHENHE,
|
||||
dianpu__isnull=True,
|
||||
).first()
|
||||
if exist and leixing and exist.leixing_id == leixing.id:
|
||||
zone_plans.append({'def': zdef, 'action': 'SKIP', 'row': exist})
|
||||
elif exist and not leixing:
|
||||
# 类型尚未创建,名称撞车则复用该专区(apply 时再绑类型)
|
||||
zone_plans.append({'def': zdef, 'action': 'SKIP', 'row': exist})
|
||||
else:
|
||||
zone_plans.append({'def': zdef, 'action': 'CREATE', 'row': None})
|
||||
|
||||
# 商品计划:全部挂第一个专区
|
||||
first_zone_name = ZONE_DEFS[0]['mingzi']
|
||||
product_plans = []
|
||||
for title, intro, price in PRODUCT_DEFS:
|
||||
# 预检:同 club + 同标题 + 审核态是否已有
|
||||
exist_p = Shangpin.query.filter(
|
||||
club_id=ALLOWED_CLUB_ID,
|
||||
biaoqian=title,
|
||||
shenhezhuangtai=SHENHE,
|
||||
dianpu__isnull=True,
|
||||
).first()
|
||||
product_plans.append({
|
||||
'title': title,
|
||||
'intro': intro,
|
||||
'price': price,
|
||||
'action': 'SKIP' if exist_p else 'CREATE',
|
||||
'exist_id': exist_p.id if exist_p else None,
|
||||
})
|
||||
|
||||
mode = 'APPLY' if apply else 'DRY-RUN'
|
||||
self.stdout.write(self.style.NOTICE(
|
||||
f'\n===== {mode} 龙先生审核版护航商品 =====\n'
|
||||
f'club={ALLOWED_CLUB_ID} ({club.name})\n'
|
||||
f'审核类型: {leixing_action} '
|
||||
f'{("id="+str(leixing.id)+" name="+repr(leixing.jieshao)) if leixing else "将创建「娱乐护航」"}\n'
|
||||
f'默认图标: {DEFAULT_AVATAR}\n'
|
||||
f'专区计划: create={sum(1 for z in zone_plans if z["action"]=="CREATE")} '
|
||||
f'skip={sum(1 for z in zone_plans if z["action"]=="SKIP")}\n'
|
||||
f'商品计划: create={sum(1 for p in product_plans if p["action"]=="CREATE")} '
|
||||
f'skip={sum(1 for p in product_plans if p["action"]=="SKIP")} '
|
||||
f'(共{len(product_plans)},挂专区「{first_zone_name}」)\n'
|
||||
))
|
||||
|
||||
self.stdout.write('—— 专区 ——')
|
||||
for z in zone_plans:
|
||||
self.stdout.write(f' [{z["action"]}] {z["def"]["mingzi"]}')
|
||||
|
||||
self.stdout.write('—— 商品样例(前8)——')
|
||||
for p in product_plans[:8]:
|
||||
self.stdout.write(f' [{p["action"]}] {p["title"]} ¥{p["price"]}')
|
||||
if len(product_plans) > 8:
|
||||
self.stdout.write(f' ... 其余 {len(product_plans)-8} 个省略')
|
||||
|
||||
banned = ('代练', '代打', '上分', '上段', '包星', '定位赛代')
|
||||
for p in product_plans:
|
||||
blob = p['title'] + p['intro']
|
||||
for b in banned:
|
||||
if b in blob:
|
||||
raise CommandError(f'文案含禁止词「{b}」: {p["title"]}')
|
||||
|
||||
if not apply:
|
||||
self.stdout.write(self.style.WARNING(
|
||||
'\n以上仅计划。确认后:\n'
|
||||
' python manage.py seed_lxs_shenhe_huhang_catalog --apply --i-confirm-club=lxs\n'
|
||||
))
|
||||
return
|
||||
|
||||
stats = {
|
||||
'leixing_created': 0,
|
||||
'zones_created': 0,
|
||||
'zones_skipped': 0,
|
||||
'products_created': 0,
|
||||
'products_skipped': 0,
|
||||
}
|
||||
|
||||
with transaction.atomic():
|
||||
if leixing is None:
|
||||
leixing = ShangpinLeixing.query.create(
|
||||
jieshao='娱乐护航',
|
||||
tupian_url=DEFAULT_AVATAR,
|
||||
paixu=0,
|
||||
shenhezhuangtai=SHENHE,
|
||||
yaoqiuleixing=None,
|
||||
huiyuan_id=None,
|
||||
yongjin=None,
|
||||
)
|
||||
stats['leixing_created'] = 1
|
||||
elif leixing.shenhezhuangtai != SHENHE:
|
||||
raise CommandError('解析到的类型不是审核专用,已中止')
|
||||
|
||||
zone_id_by_name = {}
|
||||
for zp in zone_plans:
|
||||
zdef = zp['def']
|
||||
if zp['action'] == 'SKIP' and zp['row']:
|
||||
# 若旧专区 leixing 不对,不改(只增不改);仅复用 id
|
||||
zone_id_by_name[zdef['mingzi']] = zp['row'].id
|
||||
stats['zones_skipped'] += 1
|
||||
continue
|
||||
exist = ShangpinZhuanqu.query.filter(
|
||||
club_id=ALLOWED_CLUB_ID,
|
||||
mingzi=zdef['mingzi'],
|
||||
leixing_id=leixing.id,
|
||||
shenhezhuangtai=SHENHE,
|
||||
dianpu__isnull=True,
|
||||
).first()
|
||||
if exist:
|
||||
zone_id_by_name[zdef['mingzi']] = exist.id
|
||||
stats['zones_skipped'] += 1
|
||||
continue
|
||||
row = ShangpinZhuanqu.query.create(
|
||||
mingzi=zdef['mingzi'],
|
||||
leixing_id=leixing.id,
|
||||
club_id=ALLOWED_CLUB_ID,
|
||||
tupian_url=DEFAULT_AVATAR,
|
||||
shenhezhuangtai=SHENHE,
|
||||
paixu=int(zdef['paixu']),
|
||||
shangjia_zhuangtai=True,
|
||||
fengjin_zhuangtai=True,
|
||||
dianpu=None,
|
||||
dianpu_leixing=None,
|
||||
)
|
||||
zone_id_by_name[zdef['mingzi']] = row.id
|
||||
stats['zones_created'] += 1
|
||||
|
||||
first_zq_id = zone_id_by_name.get(first_zone_name)
|
||||
if not first_zq_id:
|
||||
raise CommandError(f'第一个专区「{first_zone_name}」未就绪')
|
||||
|
||||
for i, pp in enumerate(product_plans):
|
||||
if pp['action'] == 'SKIP':
|
||||
stats['products_skipped'] += 1
|
||||
continue
|
||||
exist_p = Shangpin.query.filter(
|
||||
club_id=ALLOWED_CLUB_ID,
|
||||
biaoqian=pp['title'],
|
||||
zhuanqu_id=first_zq_id,
|
||||
shenhezhuangtai=SHENHE,
|
||||
dianpu__isnull=True,
|
||||
).first()
|
||||
if exist_p:
|
||||
stats['products_skipped'] += 1
|
||||
continue
|
||||
Shangpin.query.create(
|
||||
biaoqian=pp['title'],
|
||||
jiage=Decimal(pp['price']),
|
||||
kucun=99999,
|
||||
leixing_id=leixing.id,
|
||||
zhuanqu_id=first_zq_id,
|
||||
zhenshi_xiaoliang=0,
|
||||
duiwai_xiaoliang=0,
|
||||
jieshao=pp['intro'],
|
||||
xiadan_xuzhi='请保持友好沟通,本服务为休闲娱乐陪伴,禁止不良言行。',
|
||||
guize_tupian='',
|
||||
tupian_url=DEFAULT_AVATAR,
|
||||
yaoqiuleixing=leixing.yaoqiuleixing,
|
||||
huiyuan_id=leixing.huiyuan_id,
|
||||
yongjin=leixing.yongjin,
|
||||
kaioi_ewai_dashou_fencheng=False,
|
||||
ewai_dashou_fencheng=None,
|
||||
paixu=1000 - i,
|
||||
shenhezhuangtai=SHENHE,
|
||||
shangjia_zhuangtai=True,
|
||||
fengjin_zhuangtai=True,
|
||||
shenhe_zhuangtai=True,
|
||||
club_id=ALLOWED_CLUB_ID,
|
||||
shi_zhuanpan=False,
|
||||
dianpu=None,
|
||||
dianpu_leixing=None,
|
||||
)
|
||||
stats['products_created'] += 1
|
||||
|
||||
self.stdout.write(self.style.SUCCESS(
|
||||
f'\n完成: {stats}\n'
|
||||
f'审核类型 id={leixing.id}「{leixing.jieshao}」;'
|
||||
f'商品均 shenhezhuangtai=2,仅审核模式可见。'
|
||||
))
|
||||
Reference in New Issue
Block a user