feat: 打手自助更换管事(配额/防自换/同俱乐部)

This commit is contained in:
XingQue committed 2026-08-09 08:48:06 +08:00
1 parent d849991380
commit c1a5a3e988
12 files changed
+681 -4

No files matched your search

+3 -1
View File
@@ -6,7 +6,7 @@ from .view import (GetRolePermissionView,
ModifyAdminUserView, KefuGetDashouListView, KefuGetDashouDetailView, KefuUpdateDashouView, KefuGetShangjiaListView,
KefuGetShangjiaDetailView, KefuUpdateShangjiaView, KefuAddShangjiaView, GetProductBaseDataView, GetProductListView, SaveProductOrderView,
AddProductView, DeleteProductView, GetProductDetailView, UpdateProductView, UpdateZhiKanShenheView, UpdateMemberView, GetMemberListView,
AddMemberView, GetGuanliListView, GetGuanliDetailView, PromoteGuanshiToZuzhangView, ChangeInviterView, UpdateGuanliView, GuanshiChangeLeaderQuotaView, GuanshiChangeLeaderLogView, GetWithdrawSettingsView,
AddMemberView, GetGuanliListView, GetGuanliDetailView, PromoteGuanshiToZuzhangView, ChangeInviterView, UpdateGuanliView, GuanshiChangeLeaderQuotaView, GuanshiChangeLeaderLogView, DashouChangeGuanshiQuotaView, DashouChangeGuanshiLogView, GetWithdrawSettingsView,
UpdateWithdrawSettingsView, GetZuzhangListView, GetZuzhangDetailView, UpdateZuzhangView, GetOperationLogListView, GetProductTypeZoneView,
ModifyProductTypeZoneView, GetRateView, ModifyRateView, PopupNoticeListAPIView, PopupNoticeModifyAPIView,ShopListView,
ShopModifyView, ShopPublicTypeAndAuditView, ShopListForProductView, ShopProductModifyView,
@@ -58,6 +58,8 @@ urlpatterns = [
path('htghyqr', ChangeInviterView.as_view(), name='后台更换邀请人'),
path('htgs_huan_zz_quota', GuanshiChangeLeaderQuotaView.as_view(), name='后台管事换组长次数'),
path('htgs_huan_zz_logs', GuanshiChangeLeaderLogView.as_view(), name='后台管事换组长记录'),
path('htds_huan_gs_quota', DashouChangeGuanshiQuotaView.as_view(), name='后台打手换管事次数'),
path('htds_huan_gs_logs', DashouChangeGuanshiLogView.as_view(), name='后台打手换管事记录'),
path('htxggsxx', UpdateGuanliView.as_view(), name='后台更新管事数据'),
path('hqczrz', GetOperationLogListView.as_view(), name='后台操作日志列表'),
path('hthqtxpz', GetWithdrawSettingsView.as_view(), name='后台获取提现配置'),
+2
View File
@@ -52,6 +52,7 @@ from .views.members import (
from .views.guanli import (
GetGuanliListView, GetGuanliDetailView, PromoteGuanshiToZuzhangView,
UpdateGuanliView, GuanshiChangeLeaderQuotaView, GuanshiChangeLeaderLogView,
DashouChangeGuanshiQuotaView, DashouChangeGuanshiLogView,
)
from .views.withdraw_settings import (
GetWithdrawSettingsView, UpdateWithdrawSettingsView,
@@ -114,6 +115,7 @@ __all__ = [
# guanli
'GetGuanliListView', 'GetGuanliDetailView', 'PromoteGuanshiToZuzhangView',
'UpdateGuanliView', 'GuanshiChangeLeaderQuotaView', 'GuanshiChangeLeaderLogView',
'DashouChangeGuanshiQuotaView', 'DashouChangeGuanshiLogView',
# withdraw_settings
'GetWithdrawSettingsView', 'UpdateWithdrawSettingsView',
# zuzhang
+100
View File
@@ -983,6 +983,106 @@ class GuanshiChangeLeaderLogView(APIView):
return Response({'code': 0, 'data': data})
class DashouChangeGuanshiQuotaView(APIView):
"""
后台设置/追加打手换管事次数
POST /houtai/htds_huan_gs_quota
参数:username, yonghuid, action=set|add, total_quota / add_count
权限:001aa 或 000001
"""
permission_classes = []
parser_classes = [JSONParser]
def post(self, request):
username = request.data.get('username', '').strip()
yonghuid = request.data.get('yonghuid', '').strip()
action = (request.data.get('action') or 'add').strip().lower()
if not username:
return Response({'code': 401, 'msg': '缺少username'})
if not yonghuid:
return Response({'code': 400, 'msg': '缺少用户ID'})
kefu, permissions = verify_kefu_permission(request, username)
if kefu is None:
return permissions
if '001aa' not in permissions and '000001' not in permissions:
return Response({'code': 403, 'msg': '无权限修改换管事次数(需要001aa)'})
try:
user = User.query.select_related('DashouProfile').get(UserUID=yonghuid)
_ = user.DashouProfile
except User.DoesNotExist:
return Response({'code': 404, 'msg': '打手不存在'})
except ObjectDoesNotExist:
return Response({'code': 404, 'msg': '用户不是打手'})
denied = forbid_if_user_out_of_scope(request, user)
if denied:
return denied
from users.services.change_guanshi import set_total_quota, add_quota
club_id = _admin_quota_club_id(request, user)
if action == 'set':
result = set_total_quota(club_id, yonghuid, request.data.get('total_quota'))
else:
result = add_quota(
club_id,
yonghuid,
request.data.get('add_count') or request.data.get('add'),
)
if not result.get('ok'):
return Response({'code': result.get('code', 400), 'msg': result.get('msg', '失败')})
return Response({'code': 0, 'msg': result.get('msg', 'ok'), 'data': result.get('data')})
class DashouChangeGuanshiLogView(APIView):
"""
后台查询打手换管事记录
POST /houtai/htds_huan_gs_logs
参数:username, yonghuid, page, page_size
权限:打手管理相关任一
"""
permission_classes = []
parser_classes = [JSONParser]
def post(self, request):
username = request.data.get('username', '').strip()
yonghuid = request.data.get('yonghuid', '').strip()
if not username:
return Response({'code': 401, 'msg': '缺少username'})
if not yonghuid:
return Response({'code': 400, 'msg': '缺少用户ID'})
kefu, permissions = verify_kefu_permission(request, username)
if kefu is None:
return permissions
required_perms = {'001aa', '001bb', '001cc', '001dd', '001ee', '001ff', '001gg', '000001'}
if not any(p in permissions for p in required_perms):
return Response({'code': 403, 'msg': '您没有权限查看换管事记录'})
try:
user = User.query.select_related('DashouProfile').get(UserUID=yonghuid)
_ = user.DashouProfile
except User.DoesNotExist:
return Response({'code': 404, 'msg': '打手不存在'})
except ObjectDoesNotExist:
return Response({'code': 404, 'msg': '用户不是打手'})
denied = forbid_if_user_out_of_scope(request, user)
if denied:
return denied
from users.services.change_guanshi import list_change_logs
club_id = _admin_quota_club_id(request, user)
data = list_change_logs(
club_id,
yonghuid,
page=request.data.get('page', 1),
page_size=request.data.get('page_size', 20),
)
return Response({'code': 0, 'data': data})
+15
View File
@@ -528,6 +528,19 @@ class KefuGetDashouDetailView(APIView):
from jituan.services.gift_huiyuan import list_gift_huiyuan_for_admin
gift_member_list = list_gift_huiyuan_for_admin(uid)
from jituan.services.club_context import resolve_club_id_from_request, resolve_club_scope
from jituan.constants import DATA_SCOPE_ALL
from jituan.services.club_user import get_user_club_id
from users.services.change_guanshi import get_or_create_quota, quota_payload, list_change_logs
if resolve_club_scope(request) == DATA_SCOPE_ALL:
quota_club = get_user_club_id(user_main)
else:
quota_club = resolve_club_id_from_request(request)
quota = get_or_create_quota(quota_club, uid)
change_guanshi_quota = quota_payload(quota)
change_guanshi_logs = list_change_logs(quota_club, uid, page=1, page_size=20)
return Response({
'code': 0,
'data': {
@@ -536,6 +549,8 @@ class KefuGetDashouDetailView(APIView):
'gift_member_list': gift_member_list,
'all_members': all_members,
'permissions': permissions, # 返回当前客服的权限列表
'change_guanshi_quota': change_guanshi_quota,
'change_guanshi_logs': change_guanshi_logs,
}
})
@@ -0,0 +1,57 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('users', '0010_guanshi_change_leader'),
]
operations = [
migrations.CreateModel(
name='DashouChangeGuanshiQuota',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('club_id', models.CharField(db_index=True, max_length=16, verbose_name='俱乐部ID')),
('dashou_uid', models.CharField(db_index=True, max_length=7, verbose_name='打手UserUID')),
('total_quota', models.IntegerField(default=1, verbose_name='总次数')),
('used_count', models.IntegerField(default=0, verbose_name='已用次数')),
('CreateTime', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
('UpdateTime', models.DateTimeField(auto_now=True, verbose_name='更新时间')),
],
options={
'verbose_name': '打手换管事配额',
'verbose_name_plural': '打手换管事配额',
'db_table': 'dashou_change_guanshi_quota',
'unique_together': {('club_id', 'dashou_uid')},
},
),
migrations.CreateModel(
name='DashouChangeGuanshiLog',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('club_id', models.CharField(db_index=True, max_length=16, verbose_name='俱乐部ID')),
('dashou_uid', models.CharField(db_index=True, max_length=7, verbose_name='打手UserUID')),
('dashou_name', models.CharField(blank=True, default='', max_length=50, verbose_name='打手昵称')),
('old_guanshi_uid', models.CharField(blank=True, default='', max_length=7, verbose_name='原管事UID')),
('new_guanshi_uid', models.CharField(db_index=True, max_length=7, verbose_name='新管事UID')),
('new_invite_code', models.CharField(blank=True, default='', max_length=40, verbose_name='提交的邀请码')),
('source', models.CharField(default='miniapp', max_length=20, verbose_name='来源')),
('operator', models.CharField(blank=True, default='', max_length=64, verbose_name='操作人')),
('CreateTime', models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='更换时间')),
],
options={
'verbose_name': '打手换管事记录',
'verbose_name_plural': '打手换管事记录',
'db_table': 'dashou_change_guanshi_log',
},
),
migrations.AddIndex(
model_name='dashouchangeguanshiquota',
index=models.Index(fields=['club_id', 'dashou_uid'], name='dashou_chan_club_id_7c8a1e_idx'),
),
migrations.AddIndex(
model_name='dashouchangeguanshilog',
index=models.Index(fields=['club_id', 'dashou_uid', '-CreateTime'], name='dashou_chan_club_id_9f2b0d_idx'),
),
]
+45
View File
@@ -759,6 +759,51 @@ class GuanshiChangeLeaderLog(QModel):
]
# ==================== 打手更换管事:配额 / 记录 ====================
class DashouChangeGuanshiQuota(QModel):
"""打手自助更换管事(推荐官)次数配额"""
club_id = models.CharField(max_length=16, db_index=True, verbose_name='俱乐部ID')
dashou_uid = models.CharField(max_length=7, db_index=True, verbose_name='打手UserUID')
total_quota = models.IntegerField(default=1, verbose_name='总次数')
used_count = models.IntegerField(default=0, verbose_name='已用次数')
CreateTime = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
UpdateTime = models.DateTimeField(auto_now=True, verbose_name='更新时间')
class Meta:
db_table = 'dashou_change_guanshi_quota'
verbose_name = '打手换管事配额'
verbose_name_plural = verbose_name
unique_together = [['club_id', 'dashou_uid']]
indexes = [
models.Index(fields=['club_id', 'dashou_uid']),
]
@property
def remain_quota(self):
return max(0, int(self.total_quota or 0) - int(self.used_count or 0))
class DashouChangeGuanshiLog(QModel):
"""打手更换管事记录"""
club_id = models.CharField(max_length=16, db_index=True, verbose_name='俱乐部ID')
dashou_uid = models.CharField(max_length=7, db_index=True, verbose_name='打手UserUID')
dashou_name = models.CharField(max_length=50, blank=True, default='', verbose_name='打手昵称')
old_guanshi_uid = models.CharField(max_length=7, blank=True, default='', verbose_name='原管事UID')
new_guanshi_uid = models.CharField(max_length=7, db_index=True, verbose_name='新管事UID')
new_invite_code = models.CharField(max_length=40, blank=True, default='', verbose_name='提交的邀请码')
source = models.CharField(max_length=20, default='miniapp', verbose_name='来源')
operator = models.CharField(max_length=64, blank=True, default='', verbose_name='操作人')
CreateTime = models.DateTimeField(auto_now_add=True, db_index=True, verbose_name='更换时间')
class Meta:
db_table = 'dashou_change_guanshi_log'
verbose_name = '打手换管事记录'
verbose_name_plural = verbose_name
indexes = [
models.Index(fields=['club_id', 'dashou_uid', '-CreateTime']),
]
# === 业务用户模型注册 ===
# 导入业务子类,使 Django app registry 注册具体 User 模型(users.User)。
# 放在文件末尾以避免循环导入:此时本模块所有类已定义完毕。
+297
View File
@@ -0,0 +1,297 @@
"""打手自助更换管事(推荐官):配额、校验、换绑。
分红:会员 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,
}
+20
View File
@@ -59,6 +59,25 @@ def quota_payload(quota):
}
def resolve_zuzhang_by_invite(invite_code):
"""预览:邀请码 → 组长简要信息。"""
invite_code = (invite_code or '').strip()
if not invite_code:
return {'ok': False, 'code': 400, 'msg': '请输入组长邀请码'}
try:
zuzhang = UserZuzhang.query.select_related('user').get(yaoqingma=invite_code)
except UserZuzhang.DoesNotExist:
return {'ok': False, 'code': 404, 'msg': '邀请码无效或不存在'}
if zuzhang.zhuangtai != 1:
return {'ok': False, 'code': 403, 'msg': '该邀请码对应的组长账号已被禁用'}
return {
'ok': True,
'code': 200,
'msg': 'ok',
'data': _zuzhang_brief(zuzhang.user.UserUID),
}
def build_change_leader_info(request, user):
"""小程序 info 接口数据。"""
try:
@@ -77,6 +96,7 @@ def build_change_leader_info(request, user):
'msg': 'ok',
'data': {
'club_id': club_id,
'self_uid': user.UserUID,
'current_zuzhang': _zuzhang_brief(guanshi.yaoqingren),
'quota': quota_payload(quota),
},
+8 -1
View File
@@ -21,7 +21,8 @@ from .views import WechatMiniProgramLoginView, UserInfoUpdateView, DashouXinxiAP
GetUserPhoneView, BindUserPhoneView, BossSearchDashouView, \
FundFreezeMineView, FundFreezeMyLedgerView, FundFreezeMyLedgerDetailView, \
RoleAgreementCurrentView, RoleAgreementMineView, RoleAgreementSignView, \
GuanshiChangeZuzhangInfoView, GuanshiChangeZuzhangView
GuanshiChangeZuzhangInfoView, GuanshiChangeZuzhangPreviewView, GuanshiChangeZuzhangView, \
DashouChangeGuanshiInfoView, DashouChangeGuanshiPreviewView, DashouChangeGuanshiView
from .tixian_shenhe_views import TixianZddkshApplyView
from .paihang_views import PhbHqsjView
from .xzj_paihang_views import XzjPhbHqsjView
@@ -138,8 +139,14 @@ urlpatterns = [
# 管事自助更换组长
path('gs_huan_zuzhang_info', GuanshiChangeZuzhangInfoView.as_view(), name='管事换组长信息'),
path('gs_huan_zuzhang_preview', GuanshiChangeZuzhangPreviewView.as_view(), name='管事换组长预览'),
path('gs_huan_zuzhang', GuanshiChangeZuzhangView.as_view(), name='管事换组长提交'),
# 打手自助更换管事
path('ds_huan_guanshi_info', DashouChangeGuanshiInfoView.as_view(), name='打手换管事信息'),
path('ds_huan_guanshi_preview', DashouChangeGuanshiPreviewView.as_view(), name='打手换管事预览'),
path('ds_huan_guanshi', DashouChangeGuanshiView.as_view(), name='打手换管事提交'),
path('zuzhangzhuce', ZuzhangZhuceView.as_view(), name='组长注册'),
path('zuzhangxinxi', ZuzhangXinxiView.as_view(), name='组长刷新获取信息'),
+7
View File
@@ -76,9 +76,16 @@ from .role_agreement_mp import (
from .change_leader_mp import (
GuanshiChangeZuzhangInfoView,
GuanshiChangeZuzhangPreviewView,
GuanshiChangeZuzhangView,
)
from .change_guanshi_mp import (
DashouChangeGuanshiInfoView,
DashouChangeGuanshiPreviewView,
DashouChangeGuanshiView,
)
from .admin import (
AdGetOrderList,
AdGetOrderTypes,
+93
View File
@@ -0,0 +1,93 @@
"""小程序:打手更换管事(推荐官)。"""
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from users.services.change_guanshi import (
build_change_guanshi_info,
change_guanshi_by_invite,
resolve_guanshi_by_invite,
)
class DashouChangeGuanshiInfoView(APIView):
"""
POST /yonghu/ds_huan_guanshi_info
当前管事 + 剩余更换次数 + self_uid
"""
permission_classes = [IsAuthenticated]
def post(self, request):
result = build_change_guanshi_info(request, request.user)
if not result.get('ok'):
return Response({
'code': result.get('code', 403),
'msg': result.get('msg', '失败'),
'data': None,
})
return Response({
'code': 200,
'msg': result.get('msg', 'ok'),
'data': result.get('data'),
})
class DashouChangeGuanshiPreviewView(APIView):
"""
POST /yonghu/ds_huan_guanshi_preview
body: { inviteCode }
解析邀请码对应管事(用于前端防自换)
"""
permission_classes = [IsAuthenticated]
def post(self, request):
invite = (
request.data.get('inviteCode')
or request.data.get('yaoqingma')
or ''
)
result = resolve_guanshi_by_invite(invite)
if not result.get('ok'):
return Response({
'code': result.get('code', 400),
'msg': result.get('msg', '解析失败'),
'data': None,
})
return Response({
'code': 200,
'msg': 'ok',
'data': result.get('data'),
})
class DashouChangeGuanshiView(APIView):
"""
POST /yonghu/ds_huan_guanshi
body: { inviteCode: 管事邀请码 }
"""
permission_classes = [IsAuthenticated]
def post(self, request):
invite = (
request.data.get('inviteCode')
or request.data.get('yaoqingma')
or ''
)
result = change_guanshi_by_invite(
request,
request.user,
invite,
source='miniapp',
operator=getattr(request.user, 'UserUID', '') or '',
)
if not result.get('ok'):
return Response({
'code': result.get('code', 400),
'msg': result.get('msg', '更换失败'),
'data': None,
})
return Response({
'code': 200,
'msg': result.get('msg', '更换管事成功'),
'data': result.get('data'),
})
+34 -2
View File
@@ -3,13 +3,17 @@ from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from users.services.change_leader import build_change_leader_info, change_leader_by_invite
from users.services.change_leader import (
build_change_leader_info,
change_leader_by_invite,
resolve_zuzhang_by_invite,
)
class GuanshiChangeZuzhangInfoView(APIView):
"""
POST /yonghu/gs_huan_zuzhang_info
当前组长 + 剩余更换次数
当前组长 + 剩余更换次数 + self_uid
"""
permission_classes = [IsAuthenticated]
@@ -28,6 +32,34 @@ class GuanshiChangeZuzhangInfoView(APIView):
})
class GuanshiChangeZuzhangPreviewView(APIView):
"""
POST /yonghu/gs_huan_zuzhang_preview
body: { inviteCode }
解析邀请码对应组长(用于前端防自换)
"""
permission_classes = [IsAuthenticated]
def post(self, request):
invite = (
request.data.get('inviteCode')
or request.data.get('yaoqingma')
or ''
)
result = resolve_zuzhang_by_invite(invite)
if not result.get('ok'):
return Response({
'code': result.get('code', 400),
'msg': result.get('msg', '解析失败'),
'data': None,
})
return Response({
'code': 200,
'msg': 'ok',
'data': result.get('data'),
})
class GuanshiChangeZuzhangView(APIView):
"""
POST /yonghu/gs_huan_zuzhang