Files
Django/users/services/c_invite.py
XingQue 435422e716 fix: 点单端推荐官按UID绑定,二维码进首页且去掉邀请码
扫码参数改为 yaoqingren=UserUID 落地点单首页;海报默认不再复用管事背景;不影响打手端与支付抢单分红。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-21 02:48:16 +08:00

162 lines
6.0 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.
"""点单端老板推荐官:按推荐官 UserUID 绑定,店铺跟随。无邀请码。"""
from django.db import transaction
from django.db.models import F
from shop.models import Dianpu, YonghuDianpuBangding
from users.models import YonghuCYaoqing
from users.business_models import User
import logging
logger = logging.getLogger(__name__)
def get_or_create_c_yaoqing(user):
"""获取或创建用户的点单端推荐关系记录。"""
profile, _ = YonghuCYaoqing.objects.get_or_create(user=user)
return profile
def _unbind_shop(user_locked):
"""解除用户店铺绑定并扣减旧店绑定人数。"""
binding = (
YonghuDianpuBangding.objects.select_for_update()
.filter(yonghu=user_locked)
.select_related('dianpu')
.first()
)
if not binding:
return None
old = binding.dianpu
old.bangding_yonghushu = F('bangding_yonghushu') - 1
old.save(update_fields=['bangding_yonghushu'])
binding.delete()
return old.id
def _bind_shop(user_locked, dianpu_locked):
"""将用户绑定到指定店铺(已存在则换绑)。返回 (changed, msg)。"""
if dianpu_locked.zhuangtai != 1:
return False, '推荐官店铺已被封禁'
try:
existing = YonghuDianpuBangding.objects.select_for_update().get(yonghu=user_locked)
if existing.dianpu_id == dianpu_locked.id:
return False, '已绑定该店铺'
old = existing.dianpu
old.bangding_yonghushu = F('bangding_yonghushu') - 1
old.save(update_fields=['bangding_yonghushu'])
existing.dianpu = dianpu_locked
existing.save(update_fields=['dianpu', 'UpdateTime'])
except YonghuDianpuBangding.DoesNotExist:
YonghuDianpuBangding.query.create(yonghu=user_locked, dianpu=dianpu_locked)
dianpu_locked.bangding_yonghushu = F('bangding_yonghushu') + 1
dianpu_locked.save(update_fields=['bangding_yonghushu'])
return True, '店铺已跟随推荐官换绑'
def sync_shop_from_inviter(invitee_user, inviter_user):
"""
按推荐官店铺同步被推荐人店铺:
- 推荐官有正常店铺 → 被推荐人绑到同一店
- 推荐官无店铺 → 被推荐人解绑店铺(回落平台公共商品逻辑)
须在 transaction.atomic 内调用。
"""
invitee_locked = User.objects.select_for_update().get(UserUUID=invitee_user.UserUUID)
inviter_locked = User.objects.select_for_update().get(UserUUID=inviter_user.UserUUID)
inviter_binding = (
YonghuDianpuBangding.objects.select_for_update()
.filter(yonghu=inviter_locked)
.select_related('dianpu')
.first()
)
if inviter_binding and inviter_binding.dianpu.zhuangtai == 1:
dianpu_locked = Dianpu.objects.select_for_update().get(id=inviter_binding.dianpu_id)
changed, msg = _bind_shop(invitee_locked, dianpu_locked)
return {'shop_synced': True, 'dianpu_id': dianpu_locked.id, 'changed': changed, 'msg': msg}
old_id = _unbind_shop(invitee_locked)
return {
'shop_synced': False,
'dianpu_id': None,
'changed': bool(old_id),
'msg': '推荐官未绑定店铺,已解除店铺绑定' if old_id else '推荐官未绑定店铺',
}
def bind_c_inviter_by_uid(invitee_user, inviter_uid):
"""
用推荐官 UserUID 绑定/换绑,并同步店铺。
扫码参数yaoqingren / inviterUid不是邀请码
"""
uid = (inviter_uid or '').strip()
if not uid:
return {'code': 400, 'msg': '推荐官ID不能为空', 'data': None}
if len(uid) > 20:
return {'code': 400, 'msg': '推荐官ID格式错误', 'data': None}
try:
inviter = User.query.get(UserUID=uid)
except User.DoesNotExist:
return {'code': 404, 'msg': '推荐官不存在', 'data': None}
if inviter.UserUID == invitee_user.UserUID:
return {'code': 400, 'msg': '不能绑定自己为推荐官', 'data': None}
with transaction.atomic():
# 推荐官侧也要有档案,便于计数「我推荐的人」
inviter_profile = get_or_create_c_yaoqing(inviter)
invitee_profile = get_or_create_c_yaoqing(invitee_user)
invitee_profile = YonghuCYaoqing.objects.select_for_update().get(pk=invitee_profile.pk)
inviter_profile = YonghuCYaoqing.objects.select_for_update().get(pk=inviter_profile.pk)
old_inviter_uid = (invitee_profile.yaoqingren or '').strip() or None
new_inviter_uid = inviter.UserUID
if old_inviter_uid == new_inviter_uid:
shop_info = sync_shop_from_inviter(invitee_user, inviter)
return {
'code': 200,
'msg': '已绑定该推荐官',
'data': {
'yaoqingren': new_inviter_uid,
'rebinding': False,
'shop': shop_info,
},
}
if old_inviter_uid:
old_p = (
YonghuCYaoqing.objects.select_for_update()
.filter(user__UserUID=old_inviter_uid)
.first()
)
if old_p and old_p.yaogingshuliang > 0:
old_p.yaogingshuliang = F('yaogingshuliang') - 1
old_p.save(update_fields=['yaogingshuliang', 'UpdateTime'])
inviter_profile.yaogingshuliang = F('yaogingshuliang') + 1
inviter_profile.save(update_fields=['yaogingshuliang', 'UpdateTime'])
invitee_profile.yaoqingren = new_inviter_uid
invitee_profile.save(update_fields=['yaoqingren', 'UpdateTime'])
shop_info = sync_shop_from_inviter(invitee_user, inviter)
logger.info(
'点单端推荐官绑定: invitee=%s inviter=%s old=%s shop=%s',
invitee_user.UserUID, new_inviter_uid, old_inviter_uid, shop_info,
)
return {
'code': 200,
'msg': '绑定成功' if not old_inviter_uid else '已换绑推荐官',
'data': {
'yaoqingren': new_inviter_uid,
'rebinding': bool(old_inviter_uid),
'shop': shop_info,
},
}