Files
Django/jituan/management/commands/reset_lxs_shenhe_yule_peiban.py
XingQue da4514eeda feat: 龙先生审核目录重置为娱乐陪伴正能量商品
三审核类型改名;每类型2专区×10商品;避开代练代打;dry-run双确认。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-25 15:52:00 +08:00

333 lines
15 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""
龙先生(lxs)审核目录重置为「娱乐陪伴」正向内容(非代练代打、不过度游戏化)。
针对:全局 3 个 shenhezhuangtai=2 的商品类型 + 俱乐部 lxs 下专区/商品。
动作:
1) 将 3 个审核类型名改为:娱乐陪伴 / 休闲同行 / 友好互动改全局类型名dry-run 会提示)
2) 清空 lxs 下这些类型的审核专区+商品(只删 DB不删 OSS
3) 每个类型建 2 个专区;每个专区 10 个商品(共 60
4) 图标一律 a_long/morentouxiang.jpg
5) 文案纯正能量陪伴/交流,禁止代练代打上分等词
用法:
python manage.py reset_lxs_shenhe_yule_peiban
python manage.py reset_lxs_shenhe_yule_peiban --list
python manage.py reset_lxs_shenhe_yule_peiban --apply --i-confirm=lxs-yule-peiban
"""
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 = '龙先生'
SHENHE = 2
DEFAULT_AVATAR = 'a_long/morentouxiang.jpg'
CONFIRM_TOKEN = 'lxs-yule-peiban'
EXPECTED_TYPE_COUNT = 3
# 三个审核类型目标名称(按现有审核类型 id 升序一一对应)
TYPE_NAMES = ['娱乐陪伴', '休闲同行', '友好互动']
# 每个类型 2 个专区名
ZONE_NAMES_BY_TYPE = [
['温暖陪伴', '轻松畅聊'],
['兴趣交流', '日常鼓励'],
['正能量同行', '休闲时光'],
]
# 每个专区 10 个商品:(标题, 介绍, 价格)
# 刻意弱化具体竞技/上分,强调陪伴、交流、正向娱乐
PRODUCTS_PER_ZONE = [
[
('暖心语音陪伴', '友好语音交流,倾听分享,营造轻松正能量氛围。', '19.90'),
('轻松心情疏导', '温和聊天陪伴,帮助放松心情,传递积极态度。', '19.90'),
('兴趣话题畅聊', '围绕健康兴趣爱好交流,互相启发,气氛友善。', '15.90'),
('正能量晚间陪伴', '晚间轻松聊天,分享日常点滴,友好互动。', '19.90'),
('学习打卡互勉', '互相提醒学习计划,鼓励坚持,共同进步。', '15.90'),
('运动打卡陪伴', '交流运动习惯与打卡心得,互相鼓励坚持。', '15.90'),
('阅读心得分享', '分享好书与阅读感受,文明交流,开阔视野。', '12.90'),
('音乐放松同行', '一起听歌聊天放松,分享喜爱曲目,气氛轻松。', '12.90'),
('生活小妙招交流', '交流生活经验与小技巧,互助友爱。', '12.90'),
('周末休闲畅聊', '周末轻松闲聊,分享愉快见闻,友好陪伴。', '15.90'),
],
[
('兴趣社群式交流', '按兴趣轻松交流,结识同好,氛围文明友好。', '15.90'),
('手工创作分享', '分享手工与创作过程,互相欣赏鼓励。', '15.90'),
('摄影见闻交流', '交流摄影与风景见闻,传递美好感受。', '12.90'),
('厨艺心得闲聊', '聊聊家常菜与烹饪心得,轻松愉快。', '12.90'),
('宠物日常分享', '分享萌宠日常,友好互动,温暖治愈。', '12.90'),
('旅行见闻讲述', '讲述旅行见闻与风景,开阔眼界,正能量满满。', '19.90'),
('电影观后闲谈', '交流观影感受与推荐,文明讨论,轻松愉快。', '12.90'),
('桌游规则解说', '友好讲解休闲桌游玩法,一起快乐体验。', '15.90'),
('棋类休闲对弈', '休闲棋类友好对弈,重在交流与乐趣。', '15.90'),
('午后茶歇闲聊', '午后轻松聊天陪伴,舒缓节奏,气氛温馨。', '12.90'),
],
[
('早安鼓励打卡', '清晨互相问候鼓励,开启积极一天。', '9.90'),
('习惯养成互勉', '一起制定小目标,互相督促,温柔坚持。', '15.90'),
('情绪友好倾听', '认真倾听烦恼,给予友善支持与正向建议。', '19.90'),
('时间管理小谈', '交流高效作息小方法,互相提醒,积极向上。', '15.90'),
('环保生活分享', '分享绿色生活小习惯,传递责任与温暖。', '9.90'),
('志愿服务见闻', '交流公益与志愿服务见闻,弘扬正能量。', '12.90'),
('亲子沟通小贴士', '交流亲子相处经验,友善互助(非医疗建议)。', '15.90'),
('职场礼仪闲谈', '聊聊职场沟通礼仪与礼貌习惯,积极文明。', '15.90'),
('节日祝福陪伴', '节日期间友好问候与陪伴聊天,传递祝福。', '12.90'),
('睡前放松畅聊', '睡前轻松聊天,帮助放松,友好结束一天。', '15.90'),
],
]
BANNED_WORDS = (
'代练', '代打', '上分', '上段', '包星', '定位赛', '代肝',
'代刷', '代打手', '代练平台', '代练服务', '护航上分',
)
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',
type=str,
default='',
help=f'必须等于 {CONFIRM_TOKEN} 才允许 --apply',
)
parser.add_argument(
'--list',
action='store_true',
help='只列出当前审核类型及 lxs 存量',
)
parser.add_argument(
'--skip-rename-types',
action='store_true',
help='不修改全局审核类型名称,仅重建 lxs 专区/商品',
)
def handle(self, *args, **options):
from jituan.models import Club
from products.models import Shangpin, ShangpinLeixing, ShangpinZhuanqu
apply = bool(options.get('apply'))
confirm = (options.get('i_confirm') or '').strip()
skip_rename = bool(options.get('skip_rename_types'))
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}'
)
audit_types = list(
ShangpinLeixing.query.filter(shenhezhuangtai=SHENHE).order_by('id')
)
if len(audit_types) != EXPECTED_TYPE_COUNT:
raise CommandError(
f'安全拦截:审核类型数量应为 {EXPECTED_TYPE_COUNT}'
f'当前为 {len(audit_types)}'
+ ', '.join(f'id={t.id}{t.jieshao}' for t in audit_types)
)
leixing_ids = [t.id for t in audit_types]
def counts():
z = ShangpinZhuanqu.query.filter(
club_id=ALLOWED_CLUB_ID,
leixing_id__in=leixing_ids,
shenhezhuangtai=SHENHE,
dianpu__isnull=True,
).count()
p = Shangpin.query.filter(
club_id=ALLOWED_CLUB_ID,
leixing_id__in=leixing_ids,
shenhezhuangtai=SHENHE,
dianpu__isnull=True,
).count()
return z, p
if options.get('list'):
self.stdout.write(self.style.NOTICE(
f'审核类型 → 计划改名lxs 当前专区/商品:'
))
for i, t in enumerate(audit_types):
z = ShangpinZhuanqu.query.filter(
club_id=ALLOWED_CLUB_ID, leixing_id=t.id,
shenhezhuangtai=SHENHE, dianpu__isnull=True,
).count()
p = Shangpin.query.filter(
club_id=ALLOWED_CLUB_ID, leixing_id=t.id,
shenhezhuangtai=SHENHE, dianpu__isnull=True,
).count()
self.stdout.write(
f' id={t.id}{t.jieshao}」→「{TYPE_NAMES[i]}'
f'lxs zones={z} products={p}'
)
return
# 文案自检
for zone_list in PRODUCTS_PER_ZONE:
if len(zone_list) != 10:
raise CommandError('内部错误:每个专区必须恰好 10 个商品')
for title, intro, _price in zone_list:
blob = title + intro
for w in BANNED_WORDS:
if w in blob:
raise CommandError(f'文案含禁止词「{w}」: {title}')
for zones in ZONE_NAMES_BY_TYPE:
if len(zones) != 2:
raise CommandError('内部错误:每个类型必须恰好 2 个专区')
if apply and confirm != CONFIRM_TOKEN:
raise CommandError(
f'安全拦截:--apply 必须带 --i-confirm={CONFIRM_TOKEN}\n'
f' python manage.py reset_lxs_shenhe_yule_peiban '
f'--apply --i-confirm={CONFIRM_TOKEN}'
)
old_z, old_p = counts()
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'将清空 lxs 审核专区={old_z} 商品={old_p},再写入 '
f'{EXPECTED_TYPE_COUNT}类型×2专区×10商品=60\n'
f'图标: {DEFAULT_AVATAR}\n'
f'改名全局类型: {"" if skip_rename else "是(影响所有俱乐部审核展示名)"}\n'
))
self.stdout.write('—— 类型改名计划 ——')
for i, t in enumerate(audit_types):
self.stdout.write(f' id={t.id}{t.jieshao}」→「{TYPE_NAMES[i]}')
self.stdout.write('—— 专区/商品结构 ——')
for i, tname in enumerate(TYPE_NAMES):
for zn in ZONE_NAMES_BY_TYPE[i]:
self.stdout.write(f'{tname}」/「{zn}」→ 10 个正能量商品')
self.stdout.write('—— 商品样例 ——')
for title, intro, price in PRODUCTS_PER_ZONE[0][:3]:
self.stdout.write(f' {title} ¥{price} | {intro}')
if not apply:
self.stdout.write(self.style.WARNING(
'\n以上仅计划。确认后:\n'
f' python manage.py reset_lxs_shenhe_yule_peiban '
f'--apply --i-confirm={CONFIRM_TOKEN}\n'
))
return
stats = {
'types_renamed': 0,
'deleted_products': 0,
'deleted_zones': 0,
'zones_created': 0,
'products_created': 0,
}
with transaction.atomic():
if not skip_rename:
for i, t in enumerate(audit_types):
new_name = TYPE_NAMES[i]
if (t.jieshao or '') != new_name:
t.jieshao = new_name
t.tupian_url = t.tupian_url or DEFAULT_AVATAR
t.save()
stats['types_renamed'] += 1
n_prod, _ = Shangpin.query.filter(
club_id=ALLOWED_CLUB_ID,
leixing_id__in=leixing_ids,
shenhezhuangtai=SHENHE,
dianpu__isnull=True,
).delete()
n_zone, _ = ShangpinZhuanqu.query.filter(
club_id=ALLOWED_CLUB_ID,
leixing_id__in=leixing_ids,
shenhezhuangtai=SHENHE,
dianpu__isnull=True,
).delete()
stats['deleted_products'] = n_prod
stats['deleted_zones'] = n_zone
# 按类型顺序重建
for i, lx in enumerate(audit_types):
# 刷新名称(若已 rename
lx = ShangpinLeixing.query.filter(id=lx.id).first()
zone_names = ZONE_NAMES_BY_TYPE[i]
# 两个专区共用同一组 10 商品模板?用户要每个专区 10 个 —
# 使用 PRODUCTS_PER_ZONE[i] 作为该类型两专区的商品池,
# 第一专区用全部 10 个;第二专区标题加专区后缀避免同类型下标题撞车,
# 或两专区用同一文案但标题带专区名区分。
base_products = PRODUCTS_PER_ZONE[i]
for zi, zname in enumerate(zone_names):
zrow = ShangpinZhuanqu.query.create(
mingzi=zname,
leixing_id=lx.id,
club_id=ALLOWED_CLUB_ID,
tupian_url=DEFAULT_AVATAR,
shenhezhuangtai=SHENHE,
paixu=20 - zi * 10,
shangjia_zhuangtai=True,
fengjin_zhuangtai=True,
dianpu=None,
dianpu_leixing=None,
)
stats['zones_created'] += 1
for pi, (title, intro, price) in enumerate(base_products):
# 同类型不同专区:标题加专区名,避免去重冲突且语义清晰
full_title = f'{title}·{zname}'
for w in BANNED_WORDS:
if w in full_title or w in intro:
raise CommandError(f'禁止词命中: {full_title}')
Shangpin.query.create(
biaoqian=full_title[:45],
jiage=Decimal(price),
kucun=99999,
leixing_id=lx.id,
zhuanqu_id=zrow.id,
zhenshi_xiaoliang=0,
duiwai_xiaoliang=0,
jieshao=intro,
xiadan_xuzhi=(
'本服务为正向休闲陪伴与交流,请文明沟通。'
'禁止任何代练、代打或不正当行为。'
),
guize_tupian='',
tupian_url=DEFAULT_AVATAR,
yaoqiuleixing=lx.yaoqiuleixing,
huiyuan_id=lx.huiyuan_id,
yongjin=lx.yongjin,
kaioi_ewai_dashou_fencheng=False,
ewai_dashou_fencheng=None,
paixu=100 - pi,
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
if stats['zones_created'] != 6 or stats['products_created'] != 60:
raise CommandError(
f'数量异常: zones={stats["zones_created"]} products={stats["products_created"]}'
f'期望 6/60已回滚'
)
self.stdout.write(self.style.SUCCESS(
f'\n完成: {stats}\n'
f'均为审核专用(shenhezhuangtai=2),仅审核模式可见;文案已避开代练代打。'
))