Files

298 lines
10 KiB
Python
Raw Permalink 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.
"""打手自助更换管事(推荐官):配额、校验、换绑。
分红:会员 formal_cishu 按打手+会员+俱乐部历史笔数计算,换管事后不会把续费当成「首次」再给新人满额首充分红。
"""
import logging
from django.core.exceptions import ObjectDoesNotExist
from django.db import transaction
from django.db.models import F
from jituan.services.club_context import resolve_effective_club_id
from jituan.services.invite_guard import assert_invite_same_club
from users.models import DashouChangeGuanshiLog, DashouChangeGuanshiQuota, UserDashou, UserGuanshi
from users.business_models import User
logger = logging.getLogger(__name__)
DEFAULT_QUOTA = 1
def _dashou_display_name(user):
try:
return (user.DashouProfile.nicheng or '').strip() or user.UserUID
except ObjectDoesNotExist:
return user.UserUID
def _guanshi_brief(uid):
if not uid:
return None
try:
u = User.query.get(UserUID=uid)
except User.DoesNotExist:
return {'uid': uid, 'nicheng': '未知', 'touxiang': ''}
nicheng = '管事'
try:
nicheng = (u.BossProfile.nickname or '').strip() or nicheng
except ObjectDoesNotExist:
try:
nicheng = (u.DashouProfile.nicheng or '').strip() or nicheng
except ObjectDoesNotExist:
pass
return {
'uid': u.UserUID,
'nicheng': nicheng,
'touxiang': u.Avatar or '',
}
def get_or_create_quota(club_id, dashou_uid):
club_id = (club_id or '').strip()
dashou_uid = (dashou_uid or '').strip()
obj, _ = DashouChangeGuanshiQuota.objects.get_or_create(
club_id=club_id,
dashou_uid=dashou_uid,
defaults={'total_quota': DEFAULT_QUOTA, 'used_count': 0},
)
return obj
def quota_payload(quota):
return {
'total_quota': int(quota.total_quota or 0),
'used_count': int(quota.used_count or 0),
'remain_quota': quota.remain_quota,
}
def resolve_guanshi_by_invite(invite_code):
"""预览:邀请码 → 管事简要信息。"""
invite_code = (invite_code or '').strip()
if not invite_code:
return {'ok': False, 'code': 400, 'msg': '请输入管事邀请码'}
try:
guanshi = UserGuanshi.query.select_related('user').get(yaoqingma=invite_code)
except UserGuanshi.DoesNotExist:
return {'ok': False, 'code': 404, 'msg': '邀请码无效或不存在'}
if guanshi.zhuangtai != 1:
return {'ok': False, 'code': 403, 'msg': '该邀请码对应的管事账号已被禁用'}
return {
'ok': True,
'code': 200,
'msg': 'ok',
'data': _guanshi_brief(guanshi.user.UserUID),
}
def build_change_guanshi_info(request, user):
try:
dashou = user.DashouProfile
except ObjectDoesNotExist:
return {'ok': False, 'code': 403, 'msg': '您还不是打手'}
if int(getattr(dashou, 'zhanghaozhuangtai', 1) or 1) != 1:
return {'ok': False, 'code': 403, 'msg': '打手账号已禁用,无法更换管事'}
club_id = resolve_effective_club_id(request, user)
quota = get_or_create_quota(club_id, user.UserUID)
return {
'ok': True,
'code': 200,
'msg': 'ok',
'data': {
'club_id': club_id,
'self_uid': user.UserUID,
'current_guanshi': _guanshi_brief(dashou.yaoqingren),
'quota': quota_payload(quota),
},
}
def change_guanshi_by_invite(request, user, invite_code, source='miniapp', operator=None):
"""用管事邀请码换绑 dashou.yaoqingren。不改管事的组长。"""
invite_code = (invite_code or '').strip()
if not invite_code:
return {'ok': False, 'code': 400, 'msg': '请输入管事邀请码'}
if len(invite_code) > 100:
return {'ok': False, 'code': 400, 'msg': '邀请码长度超过限制'}
try:
dashou = user.DashouProfile
except ObjectDoesNotExist:
return {'ok': False, 'code': 403, 'msg': '您还不是打手'}
if int(getattr(dashou, 'zhanghaozhuangtai', 1) or 1) != 1:
return {'ok': False, 'code': 403, 'msg': '打手账号已禁用,无法更换管事'}
try:
new_gs = UserGuanshi.query.select_related('user').get(yaoqingma=invite_code)
except UserGuanshi.DoesNotExist:
return {'ok': False, 'code': 404, 'msg': '邀请码无效或不存在'}
if new_gs.zhuangtai != 1:
return {'ok': False, 'code': 403, 'msg': '该邀请码对应的管事账号已被禁用'}
new_uid = new_gs.user.UserUID
if new_uid == user.UserUID:
return {'ok': False, 'code': 400, 'msg': '不能将自己设为管事'}
old_uid = (dashou.yaoqingren or '').strip()
if old_uid and old_uid == new_uid:
return {'ok': False, 'code': 400, 'msg': '已是该管事,无需更换'}
ok, invite_msg = assert_invite_same_club(request, new_gs.user, user)
if not ok:
return {'ok': False, 'code': 403, 'msg': invite_msg}
club_id = resolve_effective_club_id(request, user)
op = (operator or user.UserUID or '').strip()
with transaction.atomic():
quota = (
DashouChangeGuanshiQuota.objects
.select_for_update()
.filter(club_id=club_id, dashou_uid=user.UserUID)
.first()
)
if not quota:
quota = DashouChangeGuanshiQuota.objects.create(
club_id=club_id,
dashou_uid=user.UserUID,
total_quota=DEFAULT_QUOTA,
used_count=0,
)
quota = (
DashouChangeGuanshiQuota.objects
.select_for_update()
.get(pk=quota.pk)
)
if quota.remain_quota <= 0:
return {'ok': False, 'code': 403, 'msg': '更换次数已用完,请联系客服增加次数'}
dashou = (
UserDashou.query
.select_for_update()
.select_related('user')
.get(pk=dashou.pk)
)
old_uid = (dashou.yaoqingren or '').strip()
if old_uid == new_uid:
return {'ok': False, 'code': 400, 'msg': '已是该管事,无需更换'}
if new_uid == user.UserUID:
return {'ok': False, 'code': 400, 'msg': '不能将自己设为管事'}
dashou.yaoqingren = new_uid
dashou.save(update_fields=['yaoqingren'])
if old_uid:
try:
old_gs = UserGuanshi.query.select_related('user').get(user__UserUID=old_uid)
if (old_gs.yaogingshuliang or 0) > 0:
UserGuanshi.query.filter(pk=old_gs.pk).update(
yaogingshuliang=F('yaogingshuliang') - 1
)
except UserGuanshi.DoesNotExist:
pass
UserGuanshi.query.filter(pk=new_gs.pk).update(
yaogingshuliang=F('yaogingshuliang') + 1
)
quota.used_count = int(quota.used_count or 0) + 1
quota.save(update_fields=['used_count', 'UpdateTime'])
DashouChangeGuanshiLog.objects.create(
club_id=club_id,
dashou_uid=user.UserUID,
dashou_name=_dashou_display_name(user)[:50],
old_guanshi_uid=old_uid or '',
new_guanshi_uid=new_uid,
new_invite_code=invite_code[:40],
source=source or 'miniapp',
operator=op[:64],
)
logger.info(
'dashou_change_guanshi ok club=%s ds=%s %s -> %s source=%s',
club_id, user.UserUID, old_uid, new_uid, source,
)
return {
'ok': True,
'code': 200,
'msg': '更换管事成功',
'data': {
'current_guanshi': _guanshi_brief(new_uid),
'quota': quota_payload(quota),
'old_guanshi_uid': old_uid or '',
'new_guanshi_uid': new_uid,
},
}
def set_total_quota(club_id, dashou_uid, total_quota):
try:
total = int(total_quota)
except (TypeError, ValueError):
return {'ok': False, 'code': 400, 'msg': '总次数须为整数'}
if total < 0:
return {'ok': False, 'code': 400, 'msg': '总次数不能为负数'}
quota = get_or_create_quota(club_id, dashou_uid)
if total < int(quota.used_count or 0):
return {
'ok': False,
'code': 400,
'msg': f'总次数不能小于已用次数(已用 {quota.used_count})',
}
quota.total_quota = total
quota.save(update_fields=['total_quota', 'UpdateTime'])
return {'ok': True, 'code': 0, 'msg': '已更新', 'data': quota_payload(quota)}
def add_quota(club_id, dashou_uid, add_n):
try:
n = int(add_n)
except (TypeError, ValueError):
return {'ok': False, 'code': 400, 'msg': '追加次数须为整数'}
if n <= 0:
return {'ok': False, 'code': 400, 'msg': '追加次数须大于 0'}
quota = get_or_create_quota(club_id, dashou_uid)
quota.total_quota = int(quota.total_quota or 0) + n
quota.save(update_fields=['total_quota', 'UpdateTime'])
return {'ok': True, 'code': 0, 'msg': '已追加', 'data': quota_payload(quota)}
def list_change_logs(club_id, dashou_uid, page=1, page_size=20):
qs = (
DashouChangeGuanshiLog.objects
.filter(club_id=club_id, dashou_uid=dashou_uid)
.order_by('-CreateTime', '-id')
)
total = qs.count()
try:
page = max(1, int(page))
except (TypeError, ValueError):
page = 1
try:
page_size = min(100, max(1, int(page_size)))
except (TypeError, ValueError):
page_size = 20
start = (page - 1) * page_size
rows = []
for row in qs[start:start + page_size]:
rows.append({
'id': row.id,
'old_guanshi_uid': row.old_guanshi_uid,
'new_guanshi_uid': row.new_guanshi_uid,
'new_invite_code': row.new_invite_code,
'source': row.source,
'operator': row.operator,
'created_at': row.CreateTime.isoformat() if row.CreateTime else None,
})
return {
'list': rows,
'total': total,
'page': page,
'page_size': page_size,
}