feat: 新增 2H 运营台独立登录接口,不改旧 kefu-login
This commit is contained in:
1 parent
9b21bb0860
commit
545b235379
3 files changed
+256
-1
No files matched your search
@@ -0,0 +1,186 @@
|
||||
"""
|
||||
2H 运营台专用登录(与旧 /jituan/auth/kefu-login 完全独立)。
|
||||
|
||||
- 不修改、不调用旧登录 View
|
||||
- 校验通过后强制要求可访问目标 club_id(默认 eh)
|
||||
- 旧 kefu 后台继续只用 kefu-login,互不影响
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from django.utils import timezone
|
||||
from rest_framework_simplejwt.tokens import RefreshToken
|
||||
|
||||
from jituan.constants import DATA_SCOPE_SINGLE, SUPER_ADMIN_PHONES
|
||||
from jituan.services.admin_context import (
|
||||
build_admin_club_context,
|
||||
get_allowed_club_ids,
|
||||
is_hardcoded_super_phone,
|
||||
is_system_super_admin,
|
||||
)
|
||||
from jituan.services.kefu_menu import build_menu_access_payload
|
||||
from users.business_models import User
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
EH_CONSOLE_CLUB_ID = 'eh'
|
||||
|
||||
|
||||
def _authenticate_kefu_user(phone: str, password: str, erjimima: str):
|
||||
"""
|
||||
返回 (user, kefu, error_code, error_msg)
|
||||
error_code: None 表示成功;否则为业务码。
|
||||
"""
|
||||
phone = (phone or '').strip()
|
||||
password = password or ''
|
||||
erjimima = erjimima or ''
|
||||
|
||||
if not phone or not password:
|
||||
return None, None, 1, '请填写手机号和密码'
|
||||
|
||||
user_mains = list(
|
||||
User.query.filter(Phone=phone, KefuProfile__isnull=False).select_related('KefuProfile')
|
||||
)
|
||||
if not user_mains:
|
||||
for admin_user in User.query.filter(Phone=phone):
|
||||
if (
|
||||
admin_user.IsSuperuser
|
||||
or admin_user.UserType == 'admin'
|
||||
or admin_user.Phone in SUPER_ADMIN_PHONES
|
||||
):
|
||||
user_mains = [admin_user]
|
||||
break
|
||||
|
||||
if not user_mains:
|
||||
return None, None, 1, '用户不存在'
|
||||
|
||||
target_user = None
|
||||
target_kefu = None
|
||||
password_matched = False
|
||||
|
||||
for user in user_mains:
|
||||
if not user.CheckPassword(password):
|
||||
continue
|
||||
password_matched = True
|
||||
|
||||
try:
|
||||
kefu_probe = user.KefuProfile
|
||||
except Exception:
|
||||
kefu_probe = None
|
||||
|
||||
if kefu_probe is not None and int(getattr(kefu_probe, 'zhuangtai', 1) or 0) != 1:
|
||||
if not is_hardcoded_super_phone(user):
|
||||
if target_user is None:
|
||||
target_user = user
|
||||
target_kefu = kefu_probe
|
||||
continue
|
||||
|
||||
is_admin = (
|
||||
user.IsSuperuser
|
||||
or user.UserType == 'admin'
|
||||
or user.Phone in SUPER_ADMIN_PHONES
|
||||
)
|
||||
if is_admin:
|
||||
return user, kefu_probe, None, None
|
||||
|
||||
kefu = kefu_probe
|
||||
if kefu is None:
|
||||
continue
|
||||
stored_erji = kefu.erjimima or ''
|
||||
if stored_erji.startswith(('$2b$', '$2a$')) and len(stored_erji) == 60:
|
||||
import bcrypt as _bcrypt
|
||||
if not _bcrypt.checkpw(erjimima.encode('utf-8'), stored_erji.encode('utf-8')):
|
||||
continue
|
||||
elif stored_erji != erjimima:
|
||||
continue
|
||||
if kefu.zhuangtai != 1:
|
||||
if target_user is None:
|
||||
target_user = user
|
||||
target_kefu = kefu
|
||||
continue
|
||||
return user, kefu, None, None
|
||||
|
||||
if target_user and target_kefu and int(getattr(target_kefu, 'zhuangtai', 1) or 0) != 1:
|
||||
return None, None, 4, '账号已被封禁'
|
||||
if password_matched:
|
||||
return None, None, 1, '二级密码错误'
|
||||
return None, None, 1, '密码错误'
|
||||
|
||||
|
||||
def user_can_access_console_club(user, club_id: str) -> bool:
|
||||
if is_system_super_admin(user):
|
||||
return True
|
||||
return club_id in get_allowed_club_ids(user)
|
||||
|
||||
|
||||
def login_eh_console(request, phone: str, password: str, erjimima: str, club_id: str = EH_CONSOLE_CLUB_ID):
|
||||
"""
|
||||
2H 运营台登录。
|
||||
返回 (http_status, body_dict)
|
||||
"""
|
||||
target_club = (club_id or EH_CONSOLE_CLUB_ID).strip() or EH_CONSOLE_CLUB_ID
|
||||
user, kefu, err_code, err_msg = _authenticate_kefu_user(phone, password, erjimima)
|
||||
if err_code is not None:
|
||||
http = 403 if err_code == 4 else 400
|
||||
return http, {'code': err_code, 'msg': err_msg, 'data': None}
|
||||
|
||||
if not user_can_access_console_club(user, target_club):
|
||||
allowed = sorted(get_allowed_club_ids(user))
|
||||
logger.warning(
|
||||
'eh-console 拒登 phone=%s want=%s allowed=%s',
|
||||
phone, target_club, allowed,
|
||||
)
|
||||
return 403, {
|
||||
'code': 403,
|
||||
'msg': f'无权进入俱乐部「{target_club}」运营台,请使用已在该俱乐部配置的账号',
|
||||
'data': {
|
||||
'allowed': False,
|
||||
'club_id': target_club,
|
||||
'allowed_club_ids': allowed,
|
||||
},
|
||||
}
|
||||
|
||||
user.IP = _request_ip(request)
|
||||
user.UserLastLoginDate = timezone.now()
|
||||
user.save(update_fields=['IP', 'UserLastLoginDate'])
|
||||
|
||||
token = str(RefreshToken.for_user(user).access_token)
|
||||
nicheng = kefu.nicheng if kefu else (user.UserName or user.Phone)
|
||||
club_context = build_admin_club_context(user)
|
||||
|
||||
# 菜单按目标单店 eh 计算(不按集团 all)
|
||||
request.user = user
|
||||
request.club_id = target_club
|
||||
request.club_scope = DATA_SCOPE_SINGLE
|
||||
menu_access, _ = build_menu_access_payload(request, phone)
|
||||
|
||||
return 200, {
|
||||
'code': 0,
|
||||
'msg': '登录成功',
|
||||
'data': {
|
||||
'token': token,
|
||||
'nicheng': nicheng,
|
||||
'yonghuid': user.UserUID,
|
||||
'console': 'eh',
|
||||
'fixed_club_id': target_club,
|
||||
'club_context': club_context,
|
||||
'menu_access': menu_access if menu_access else {
|
||||
'visible_page_ids': [],
|
||||
'visible_paths': [],
|
||||
'permission_codes': [],
|
||||
'role_names': [],
|
||||
'yonghuid': user.UserUID or '',
|
||||
'can_switch_club': False,
|
||||
'is_group_admin': False,
|
||||
'menu_ready': False,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _request_ip(request):
|
||||
xff = request.META.get('HTTP_X_FORWARDED_FOR')
|
||||
if xff:
|
||||
return xff.split(',')[0].strip()
|
||||
return request.META.get('REMOTE_ADDR') or ''
|
||||
@@ -50,6 +50,8 @@ from jituan.views import (
|
||||
ClubHuiyuanStatsView,
|
||||
ClubKefuLoginView,
|
||||
ClubKefuMenuAccessView,
|
||||
EhConsoleLoginView,
|
||||
ClubConsoleGateView,
|
||||
ClubPermDebugView,
|
||||
ClubListView,
|
||||
ClubDefaultInviteView,
|
||||
@@ -131,8 +133,10 @@ urlpatterns = [
|
||||
path('auth/wechat-oauth-login', ClubWechatOAuthLoginView.as_view(), name='jituan_wechat_oauth_login'),
|
||||
path('auth/refresh-wx-bind', ClubWxBindRefreshView.as_view(), name='jituan_refresh_wx_bind'),
|
||||
path('auth/kefu-login', ClubKefuLoginView.as_view(), name='jituan_kefu_login'),
|
||||
path('auth/eh-console-login', EhConsoleLoginView.as_view(), name='jituan_eh_console_login'),
|
||||
path('auth/me-context', ClubMeContextView.as_view(), name='jituan_me_context'),
|
||||
path('auth/menu-access', ClubKefuMenuAccessView.as_view(), name='jituan_menu_access'),
|
||||
path('auth/console-gate', ClubConsoleGateView.as_view(), name='jituan_console_gate'),
|
||||
path('auth/perm-debug', ClubPermDebugView.as_view(), name='jituan_perm_debug'),
|
||||
path('auth/dashou-register', ClubDashouRegisterView.as_view(), name='jituan_dashou_register'),
|
||||
path('houtai/club-manage', ClubManageView.as_view(), name='jituan_club_manage'),
|
||||
|
||||
+66
-1
@@ -12,7 +12,13 @@ from rest_framework_simplejwt.tokens import RefreshToken
|
||||
|
||||
from jituan.constants import SUPER_ADMIN_PHONES, ADMIN_ROLE_LABELS, CLUB_ID_DEFAULT, DATA_SCOPE_ALL, DATA_SCOPE_SINGLE
|
||||
from jituan.models import Club, AdminAssignment
|
||||
from jituan.services.admin_context import build_admin_club_context, is_kefu_backend_account, can_manage_admin_assignments
|
||||
from jituan.services.admin_context import (
|
||||
build_admin_club_context,
|
||||
is_kefu_backend_account,
|
||||
can_manage_admin_assignments,
|
||||
get_allowed_club_ids,
|
||||
is_system_super_admin,
|
||||
)
|
||||
from jituan.services.kefu_menu import build_menu_access_payload
|
||||
from jituan.services.wechat_login import (
|
||||
login_or_register_by_club,
|
||||
@@ -345,6 +351,65 @@ class ClubKefuMenuAccessView(APIView):
|
||||
return Response({'code': 0, 'msg': 'ok', 'data': payload})
|
||||
|
||||
|
||||
class EhConsoleLoginView(APIView):
|
||||
"""
|
||||
2H 运营台专用登录(全新接口,与旧后台无关)。
|
||||
POST /jituan/auth/eh-console-login
|
||||
body: { phone, password, erjimima }
|
||||
|
||||
注意:绝不改动 /jituan/auth/kefu-login;旧 kefu 后台继续用旧登录。
|
||||
"""
|
||||
throttle_classes = [AnonRateThrottle]
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
def post(self, request):
|
||||
from jituan.services.eh_console_login import EH_CONSOLE_CLUB_ID, login_eh_console
|
||||
|
||||
phone = (request.data.get('phone') or '').strip()
|
||||
password = request.data.get('password') or ''
|
||||
erjimima = request.data.get('erjimima') or ''
|
||||
http_status, body = login_eh_console(
|
||||
request, phone, password, erjimima, club_id=EH_CONSOLE_CLUB_ID,
|
||||
)
|
||||
return Response(body, status=http_status)
|
||||
|
||||
|
||||
class ClubConsoleGateView(APIView):
|
||||
"""
|
||||
GET /jituan/auth/console-gate?club_id=eh
|
||||
2H 运营台会话复查门禁(新接口)。不参与旧后台登录。
|
||||
"""
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def get(self, request):
|
||||
from jituan.services.eh_console_login import user_can_access_console_club
|
||||
|
||||
user = request.user
|
||||
if not is_kefu_backend_account(user):
|
||||
return Response({'code': 403, 'msg': '非后台账号', 'data': None}, status=403)
|
||||
|
||||
target = (request.query_params.get('club_id') or '').strip() or 'eh'
|
||||
allowed = sorted(get_allowed_club_ids(user))
|
||||
ok = bool(user_can_access_console_club(user, target))
|
||||
payload = {
|
||||
'allowed': ok,
|
||||
'club_id': target,
|
||||
'allowed_club_ids': allowed,
|
||||
'phone': getattr(user, 'Phone', '') or '',
|
||||
'is_super': bool(is_system_super_admin(user)),
|
||||
}
|
||||
if not ok:
|
||||
return Response(
|
||||
{
|
||||
'code': 403,
|
||||
'msg': f'无权进入俱乐部「{target}」运营台,请使用已在该俱乐部配置的账号',
|
||||
'data': payload,
|
||||
},
|
||||
status=status.HTTP_403_FORBIDDEN,
|
||||
)
|
||||
return Response({'code': 0, 'msg': 'ok', 'data': payload})
|
||||
|
||||
|
||||
class ClubPermDebugView(APIView):
|
||||
"""GET /jituan/auth/perm-debug — 当前俱乐部权限解析明细。"""
|
||||
|
||||
|
||||
Reference in new issue
Block a user