From 98b4526efa88b98b6421a0c6786b542eead13562 Mon Sep 17 00:00:00 2001 From: XingQue Date: Sun, 26 Jul 2026 02:01:02 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=B0=8F=E6=B1=90?= =?UTF-8?q?=E7=94=B5=E7=AB=9E=E7=A7=8D=E5=AD=90=E7=BB=84=E9=95=BF/?= =?UTF-8?q?=E7=AE=A1=E4=BA=8B=E5=91=BD=E4=BB=A4=20seed=5Fxx=5Finvite=5Fdat?= =?UTF-8?q?a?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- .../commands/seed_xx_invite_data.py | 223 ++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 jituan/management/commands/seed_xx_invite_data.py diff --git a/jituan/management/commands/seed_xx_invite_data.py b/jituan/management/commands/seed_xx_invite_data.py new file mode 100644 index 0000000..21df128 --- /dev/null +++ b/jituan/management/commands/seed_xx_invite_data.py @@ -0,0 +1,223 @@ +"""小汐电竞邀请链种子:插入组长+管事(含邀请码)。 + +默认 club_id=xx;找不到会列出全部俱乐部。 +可用: + python manage.py seed_xx_invite_data + python manage.py seed_xx_invite_data --club-id xx + python manage.py seed_xx_invite_data --list-clubs +""" +from django.core.management.base import BaseCommand, CommandError +from django.db import transaction + +from jituan.models import Club, ClubHuiyuanPrice +from users.business_models import User +from users.models import UserBoss, UserGuanshi, UserZuzhang +from utils.invitationcode_utils import CreateInvitationCode + +DEFAULT_CLUB_ID = 'xx' +WX_APPID = 'wx0d445b76f93774fa' +NAME_HINTS = ('小汐', '小溪', '小西') + +# 固定种子账号(可重复执行,幂等;UID/手机避开 xzj 990000x、lsx 991000x) +SEED_ZUZHANG = { + 'UserUID': '9920001', + 'OpenID': 'xx_seed_openid_zuzhang_v1', + 'Phone': '19920000001', + 'UserName': 'xx_seed_zuzhang', + 'nickname': '小汐种子组长', +} +SEED_GUANSHI = { + 'UserUID': '9920002', + 'OpenID': 'xx_seed_openid_guanshi_v1', + 'Phone': '19920000002', + 'UserName': 'xx_seed_guanshi', + 'nickname': '小汐种子管事', +} + + +def _list_clubs(stdout, style): + clubs = list(Club.query.order_by('sort_order', 'club_id')) + stdout.write(style.SUCCESS('=' * 60)) + stdout.write(f'库里俱乐部共 {len(clubs)} 个:') + for c in clubs: + stdout.write('-' * 40) + stdout.write(f' club_id={c.club_id!r} name={c.name!r} status={c.status}') + stdout.write(f' wx_appid={c.wx_appid or "(空)"}') + stdout.write(style.SUCCESS('=' * 60)) + return clubs + + +def _resolve_club(club_id: str, stdout, style): + """优先精确 club_id;其次 wx_appid;再名称含小汐/小溪/小西。""" + cid = (club_id or DEFAULT_CLUB_ID).strip() + club = Club.query.filter(club_id=cid).first() + if club: + return club + + stdout.write(style.WARNING(f'精确 club_id={cid!r} 不存在,尝试按 AppID/名称匹配…')) + + by_app = Club.query.filter(wx_appid=WX_APPID).first() + if by_app: + stdout.write(style.WARNING( + f'按 wx_appid={WX_APPID} 命中: club_id={by_app.club_id!r} name={by_app.name!r}' + )) + return by_app + + hits = [] + for hint in NAME_HINTS: + hits.extend(list(Club.query.filter(name__icontains=hint))) + # 去重 + seen = set() + unique = [] + for c in hits: + if c.club_id in seen: + continue + seen.add(c.club_id) + unique.append(c) + + if len(unique) == 1: + c = unique[0] + stdout.write(style.WARNING( + f'按名称命中唯一: club_id={c.club_id!r} name={c.name!r}' + )) + return c + if len(unique) > 1: + stdout.write(style.ERROR('名称匹配到多条,请用 --club-id 指定:')) + for c in unique: + stdout.write(f' - {c.club_id!r} / {c.name!r}') + return None + + return None + + +def _ensure_user(meta, club_id): + user = User.query.filter(UserUID=meta['UserUID']).first() + if not user: + user = User.query.filter(OpenID=meta['OpenID']).first() + if user: + if (user.ClubID or '') != club_id: + user.ClubID = club_id + user.save(update_fields=['ClubID']) + return user, False + + user = User.query.create( + UserUID=meta['UserUID'], + UserName=meta['UserName'], + OpenID=meta['OpenID'], + Phone=meta['Phone'], + ClubID=club_id, + ) + UserBoss.query.create(user=user, nickname=meta['nickname']) + return user, True + + +def _ensure_zuzhang(user): + try: + return user.ZuzhangProfile, False + except UserZuzhang.DoesNotExist: + pass + yaoqingma = CreateInvitationCode(str(user.UserUID)) + z = UserZuzhang.query.create( + user=user, + yaoqingma=yaoqingma, + zhuangtai=1, + ) + return z, True + + +def _ensure_guanshi(user, zuzhang_uid): + try: + return user.GuanshiProfile, False + except UserGuanshi.DoesNotExist: + pass + yaoqingma = CreateInvitationCode(str(user.UserUID)) + g = UserGuanshi.query.create( + user=user, + yaoqingma=yaoqingma, + yaoqingren=zuzhang_uid, + zhuangtai=1, + ) + return g, True + + +class Command(BaseCommand): + help = '插入小汐电竞种子组长+管事(含邀请码);找不到俱乐部时会列出全部 club' + + def add_arguments(self, parser): + parser.add_argument( + '--club-id', + type=str, + default=DEFAULT_CLUB_ID, + help='目标俱乐部 ID(默认 xx)', + ) + parser.add_argument( + '--list-clubs', + action='store_true', + help='只列出库里所有俱乐部,不写入种子', + ) + parser.add_argument( + '--force-new-codes', + action='store_true', + help='已存在时重新生成邀请码(慎用)', + ) + + def handle(self, *args, **options): + if options.get('list_clubs'): + _list_clubs(self.stdout, self.style) + return + + want_id = (options.get('club_id') or DEFAULT_CLUB_ID).strip() + club = _resolve_club(want_id, self.stdout, self.style) + if club is None: + self.stdout.write(self.style.ERROR( + f'找不到可用俱乐部(请求 club_id={want_id!r})。' + )) + self.stdout.write(self.style.WARNING( + '请先在后台创建小汐俱乐部(club_id=xx),或:\n' + ' python manage.py seed_xx_invite_data --list-clubs\n' + ' python manage.py seed_xx_invite_data --club-id 真实id' + )) + _list_clubs(self.stdout, self.style) + raise CommandError('俱乐部不存在,已中止(未写入任何种子)') + + club_id = club.club_id + self.stdout.write(self.style.SUCCESS( + f'将写入种子到俱乐部: club_id={club_id!r} name={club.name!r} ' + f'wx_appid={club.wx_appid or "(空)"}' + )) + + with transaction.atomic(): + zz_user, zz_new = _ensure_user(SEED_ZUZHANG, club_id) + zz_profile, zz_profile_new = _ensure_zuzhang(zz_user) + if options.get('force_new_codes') and not zz_profile_new: + zz_profile.yaoqingma = CreateInvitationCode(str(zz_user.UserUID)) + zz_profile.save(update_fields=['yaoqingma']) + + gs_user, gs_new = _ensure_user(SEED_GUANSHI, club_id) + gs_profile, gs_profile_new = _ensure_guanshi(gs_user, zz_user.UserUID) + if options.get('force_new_codes') and not gs_profile_new: + gs_profile.yaoqingma = CreateInvitationCode(str(gs_user.UserUID)) + gs_profile.save(update_fields=['yaoqingma']) + + price_cnt = ClubHuiyuanPrice.query.filter(club_id=club_id).count() + + self.stdout.write(self.style.SUCCESS('=' * 60)) + self.stdout.write(self.style.SUCCESS(f'小汐电竞邀请链种子已就绪(club_id={club_id})')) + self.stdout.write(self.style.SUCCESS('=' * 60)) + self.stdout.write(f'club_huiyuan_price 条数: {price_cnt}') + self.stdout.write('') + self.stdout.write('【组长】管事注册用这个邀请码(guanshizhuce):') + self.stdout.write(f' UserUID={zz_user.UserUID} Phone={SEED_ZUZHANG["Phone"]}') + self.stdout.write(f' 组长邀请码 inviteCode = {zz_profile.yaoqingma}') + self.stdout.write('') + self.stdout.write('【管事】打手/接单员注册用这个邀请码(dashouzhuce):') + self.stdout.write(f' UserUID={gs_user.UserUID} Phone={SEED_GUANSHI["Phone"]}') + self.stdout.write(f' 管事邀请码 inviteCode = {gs_profile.yaoqingma}') + self.stdout.write('') + self.stdout.write('商家:后台「商家管理 → 添加商家」按用户UID开通') + self.stdout.write('') + self.stdout.write('新建: 组长=%s 管事=%s' % ( + '是' if zz_new or zz_profile_new else '否(已存在)', + '是' if gs_new or gs_profile_new else '否(已存在)', + )) + self.stdout.write(self.style.SUCCESS('=' * 60))