diff --git a/jituan/management/commands/diagnose_lsx_invite.py b/jituan/management/commands/diagnose_lsx_invite.py new file mode 100644 index 0000000..5179114 --- /dev/null +++ b/jituan/management/commands/diagnose_lsx_invite.py @@ -0,0 +1,127 @@ +"""诊断 / 对齐龙先生邀请种子与小程序 club_id。 + +用法: + python manage.py diagnose_lsx_invite + python manage.py diagnose_lsx_invite --align-to lsx + python manage.py diagnose_lsx_invite --align-to-appid +""" +from django.core.management.base import BaseCommand, CommandError +from django.db import transaction + +from jituan.models import Club +from users.business_models import User +from users.models import UserGuanshi, UserZuzhang + +LSX_APPID = 'wx7ff90e9d024fcdb8' +SEED_UIDS = ('9910001', '9910002') +FRONTEND_EXPECT = 'lsx' + + +class Command(BaseCommand): + help = '诊断龙先生种子邀请人 ClubID 与小程序是否一致,并可对齐' + + def add_arguments(self, parser): + parser.add_argument( + '--align-to', + type=str, + default='', + help='把种子用户 ClubID 改成指定值(如 lsx)', + ) + parser.add_argument( + '--align-to-appid', + action='store_true', + help='按 wx_appid=龙先生 找到俱乐部,把种子 ClubID 对齐过去', + ) + + def handle(self, *args, **options): + self.stdout.write(self.style.SUCCESS('=' * 60)) + self.stdout.write('前端龙先生小程序默认 X-Club-Id / CLUB_ID = lsx') + self.stdout.write(f'龙先生 AppID = {LSX_APPID}') + self.stdout.write('') + + clubs = list(Club.query.order_by('club_id')) + self.stdout.write(f'库里俱乐部 {len(clubs)} 个:') + target_by_app = None + for c in clubs: + mark = '' + if c.club_id == FRONTEND_EXPECT: + mark = ' ← 前端期望' + if (c.wx_appid or '') == LSX_APPID: + mark += ' ← AppID命中' + target_by_app = c + if '龙先生' in (c.name or ''): + mark += ' ← 名称命中' + self.stdout.write( + f' club_id={c.club_id!r} name={c.name!r} wx_appid={c.wx_appid or "(空)"}{mark}' + ) + + self.stdout.write('') + self.stdout.write('种子用户:') + for uid in SEED_UIDS: + user = User.query.filter(UserUID=uid).first() + if not user: + self.stdout.write(self.style.WARNING(f' {uid}: 不存在(还没跑 seed_lsx_invite_data?)')) + continue + role = [] + try: + z = user.ZuzhangProfile + role.append(f'组长码={z.yaoqingma}') + except Exception: + pass + try: + g = user.GuanshiProfile + role.append(f'管事码={g.yaoqingma}') + except Exception: + pass + club = (user.ClubID or '').strip() + ok = club == FRONTEND_EXPECT + line = f' {uid}: ClubID={club or "(空)"} {" | ".join(role) or "无身份"}' + if ok: + self.stdout.write(self.style.SUCCESS(line + ' ✓ 与前端 lsx 一致')) + else: + self.stdout.write(self.style.ERROR( + line + f' ✗ 与前端 lsx 不一致 → 会报「邀请码不属于当前小程序俱乐部」' + )) + + self.stdout.write('') + self.stdout.write(self.style.WARNING( + '结论:这是后端校验(invite_guard),不是前端邀请码输错。\n' + '小程序发 X-Club-Id=lsx,邀请人 User.ClubID 必须也是 lsx。' + )) + + align_to = (options.get('align_to') or '').strip() + if options.get('align_to_appid'): + if not target_by_app: + raise CommandError(f'没有 wx_appid={LSX_APPID} 的俱乐部,无法对齐') + align_to = target_by_app.club_id + self.stdout.write(f'将按 AppID 对齐到 club_id={align_to!r}') + + if not align_to: + self.stdout.write('') + self.stdout.write('若要修复,任选其一:') + self.stdout.write(' python manage.py diagnose_lsx_invite --align-to lsx') + self.stdout.write(' python manage.py diagnose_lsx_invite --align-to-appid') + self.stdout.write('然后前端 CLUB_ID 必须与种子 ClubID 相同。') + self.stdout.write(self.style.SUCCESS('=' * 60)) + return + + if not Club.query.filter(club_id=align_to).exists(): + raise CommandError(f'目标俱乐部 {align_to!r} 不存在,请先在后台创建') + + with transaction.atomic(): + for uid in SEED_UIDS: + user = User.query.filter(UserUID=uid).first() + if not user: + self.stdout.write(self.style.WARNING(f'跳过不存在用户 {uid}')) + continue + old = user.ClubID + user.ClubID = align_to + user.save(update_fields=['ClubID']) + self.stdout.write(self.style.SUCCESS( + f'已更新 {uid}: ClubID {old!r} → {align_to!r}' + )) + + self.stdout.write(self.style.SUCCESS( + f'\n对齐完成。请确认小程序 config/club-config.js 的 CLUB_ID = {align_to!r}' + )) + self.stdout.write(self.style.SUCCESS('=' * 60)) diff --git a/jituan/services/invite_guard.py b/jituan/services/invite_guard.py index 8e53b11..13bbae2 100644 --- a/jituan/services/invite_guard.py +++ b/jituan/services/invite_guard.py @@ -31,7 +31,11 @@ def assert_invite_same_club(request, inviter_user, invitee_user=None): 'invite_guard 管事俱乐部不符 inviter=%s inviter_club=%s req_club=%s', getattr(inviter_user, 'UserUID', '?'), inviter_club, club_id, ) - return False, '邀请码不属于当前小程序俱乐部' + # 文案带上两侧 club,方便排查「种子 ClubID ≠ 小程序 X-Club-Id」 + return False, ( + f'邀请码不属于当前小程序俱乐部' + f'(邀请人归属={inviter_club or "空"},当前小程序={club_id or "空"})' + ) if invitee_user is not None: invitee_club = resolve_invite_party_club(invitee_user, club_id)