Files
Django/jituan/services/club_seed_invite.py

202 lines
6.7 KiB
Python

"""俱乐部种子组长 / 管事(邀请链)。
规则:
- 若该俱乐部在组长表或管事表已有归属用户,则对应角色不再造种子;
- 两者都已有 → 整单跳过;
- 缺哪个补哪个;管事邀请人挂到该俱乐部现有/新建的组长 UID。
"""
from __future__ import annotations
import hashlib
import logging
import random
import time
from typing import Any, Dict, Optional, Tuple
from django.db import transaction
from jituan.models import Club
from jituan.services.default_invite import (
get_club_default_dashou_invite,
set_club_default_dashou_invite,
)
from users.business_models import User
from users.models import UserBoss, UserGuanshi, UserZuzhang
from utils.invitationcode_utils import CreateInvitationCode
logger = logging.getLogger(__name__)
def _club_zuzhang_qs(club_id: str):
return UserZuzhang.query.filter(user__ClubID=club_id)
def _club_guanshi_qs(club_id: str):
return UserGuanshi.query.filter(user__ClubID=club_id)
def club_invite_seed_status(club_id: str) -> Dict[str, Any]:
club_id = (club_id or '').strip()
zz_cnt = _club_zuzhang_qs(club_id).count() if club_id else 0
gs_cnt = _club_guanshi_qs(club_id).count() if club_id else 0
return {
'club_id': club_id,
'zuzhang_count': zz_cnt,
'guanshi_count': gs_cnt,
'need_seed': zz_cnt == 0 or gs_cnt == 0,
'has_zuzhang': zz_cnt > 0,
'has_guanshi': gs_cnt > 0,
}
def _alloc_user_uid() -> str:
for _ in range(30):
timestamp_part = str(int(time.time()))[-5:].zfill(5)
random_part = str(random.randint(0, 99)).zfill(2)
user_id = timestamp_part + random_part
if len(user_id) == 7 and user_id.isdigit():
if not User.query.filter(UserUID=user_id).exists():
return user_id
raise RuntimeError('生成种子用户UID失败')
def _phone_for(club_id: str, role: str) -> str:
digest = hashlib.md5(f'{club_id}:{role}:seed_phone_v1'.encode('utf-8')).hexdigest()
# 199 + 8 位数字,尽量不与真实号冲突
return '199' + ''.join(str(int(ch, 16) % 10) for ch in digest[:8])
def _ensure_seed_user(club_id: str, role: str, nickname: str) -> Tuple[User, bool]:
openid = f'{club_id}_seed_openid_{role}_v1'[:64]
phone = _phone_for(club_id, role)
user = User.query.filter(OpenID=openid).first()
if not user:
user = User.query.filter(Phone=phone, ClubID=club_id).first()
if user:
changed = False
if (user.ClubID or '') != club_id:
user.ClubID = club_id
changed = True
if changed:
user.save(update_fields=['ClubID'])
return user, False
uid = _alloc_user_uid()
user = User.query.create(
UserUID=uid,
UserName=f'{club_id}_seed_{role}',
OpenID=openid,
Phone=phone,
ClubID=club_id,
)
UserBoss.query.create(user=user, nickname=nickname)
return user, True
def _ensure_zuzhang(user: User) -> Tuple[UserZuzhang, bool]:
try:
return user.ZuzhangProfile, False
except UserZuzhang.DoesNotExist:
pass
z = UserZuzhang.query.create(
user=user,
yaoqingma=CreateInvitationCode(str(user.UserUID)),
zhuangtai=1,
)
return z, True
def _ensure_guanshi(user: User, zuzhang_uid: str) -> Tuple[UserGuanshi, bool]:
try:
return user.GuanshiProfile, False
except UserGuanshi.DoesNotExist:
pass
g = UserGuanshi.query.create(
user=user,
yaoqingma=CreateInvitationCode(str(user.UserUID)),
yaoqingren=zuzhang_uid,
zhuangtai=1,
)
return g, True
def ensure_club_seed_invite(club_id: str, set_default_invite: bool = True) -> Dict[str, Any]:
"""
为俱乐部补齐种子组长/管事。
已有对应角色数据则跳过该角色;两者都有则整单 skipped。
"""
club_id = (club_id or '').strip()
if not club_id:
return {'ok': False, 'msg': '缺少 club_id'}
club = Club.query.filter(club_id=club_id).first()
if not club:
return {'ok': False, 'msg': f'俱乐部 {club_id} 不存在'}
status_before = club_invite_seed_status(club_id)
if not status_before['need_seed']:
return {
'ok': True,
'skipped': True,
'msg': '该俱乐部已有组长与管事,无需种子',
'status': status_before,
'default_dashou_invite': get_club_default_dashou_invite(club_id),
}
created = {'zuzhang': False, 'guanshi': False}
zz_profile: Optional[UserZuzhang] = None
gs_profile: Optional[UserGuanshi] = None
zz_user: Optional[User] = None
gs_user: Optional[User] = None
with transaction.atomic():
if status_before['has_zuzhang']:
zz_profile = _club_zuzhang_qs(club_id).select_related('user').first()
zz_user = zz_profile.user if zz_profile else None
else:
zz_user, _ = _ensure_seed_user(club_id, 'zuzhang', f'{club.name or club_id}种子组长')
zz_profile, zz_new = _ensure_zuzhang(zz_user)
created['zuzhang'] = zz_new
if not zz_user or not zz_profile:
return {'ok': False, 'msg': '无法确定种子组长'}
if status_before['has_guanshi']:
gs_profile = _club_guanshi_qs(club_id).select_related('user').first()
gs_user = gs_profile.user if gs_profile else None
else:
gs_user, _ = _ensure_seed_user(club_id, 'guanshi', f'{club.name or club_id}种子管事')
gs_profile, gs_new = _ensure_guanshi(gs_user, str(zz_user.UserUID))
created['guanshi'] = gs_new
invite = ''
if gs_profile:
invite = (gs_profile.yaoqingma or '').strip()
# config_json 里还没有显式默认邀请码时,写入管事邀请码
raw_cfg_invite = ''
try:
raw_cfg_invite = ((club.config_json or {}).get('default_dashou_invite') or '').strip()
except Exception:
raw_cfg_invite = ''
if set_default_invite and invite and not raw_cfg_invite:
set_club_default_dashou_invite(club, invite)
club.save(update_fields=['config_json'])
status_after = club_invite_seed_status(club_id)
return {
'ok': True,
'skipped': False,
'msg': '种子组长/管事已就绪',
'created': created,
'status': status_after,
'zuzhang': {
'user_uid': str(zz_user.UserUID) if zz_user else '',
'invite_code': (zz_profile.yaoqingma if zz_profile else '') or '',
},
'guanshi': {
'user_uid': str(gs_user.UserUID) if gs_user else '',
'invite_code': (gs_profile.yaoqingma if gs_profile else '') or '',
},
'default_dashou_invite': get_club_default_dashou_invite(club_id),
}