fix: 小程序配置接口改用客服权限校验,避免401踢登录
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
"""小程序系统配置 & 页面资源 — 后台管理接口"""
|
||||
"""小程序系统配置 & 页面资源 — 后台管理接口(客服终端 + 管理员)"""
|
||||
import logging
|
||||
|
||||
from django.db import transaction
|
||||
from rest_framework import permissions, status
|
||||
from rest_framework import permissions
|
||||
from rest_framework.parsers import FormParser, JSONParser, MultiPartParser
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from houtai.utils import verify_kefu_permission
|
||||
from peizhi.models import XcxSysPeizhi, XcxPageZiyuan
|
||||
from utils.oss_utils import upload_to_oss, validate_image
|
||||
from utils.page_ziyuan_service import DEFAULT_ZIYUAN, build_page_assets_for_api, clear_page_ziyuan_cache
|
||||
@@ -15,17 +16,53 @@ from yonghu.models import AdminProfile
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 与弹窗公告等小程序配置页共用权限码
|
||||
REQUIRED_PERMISSION = '8080a'
|
||||
|
||||
def _verify_admin(request, zhanghao):
|
||||
if not zhanghao:
|
||||
return False, Response({'code': 1, 'msg': '账号不能为空', 'data': None}, status=status.HTTP_400_BAD_REQUEST)
|
||||
user_main = request.user
|
||||
if user_main.phone != zhanghao or user_main.user_type != 'admin':
|
||||
return False, Response({'code': 1, 'msg': '无权限', 'data': None}, status=status.HTTP_401_UNAUTHORIZED)
|
||||
try:
|
||||
user_main.admin_profile
|
||||
except AdminProfile.DoesNotExist:
|
||||
return False, Response({'code': 1, 'msg': '无权限', 'data': None}, status=status.HTTP_401_UNAUTHORIZED)
|
||||
|
||||
def _frontend_account(request):
|
||||
raw = (
|
||||
request.data.get('username')
|
||||
or request.data.get('phone')
|
||||
or request.data.get('zhanghao')
|
||||
or ''
|
||||
)
|
||||
return str(raw).strip()
|
||||
|
||||
|
||||
def _authorize_miniapp_config(request):
|
||||
"""
|
||||
客服终端:verify_kefu_permission + 8080a
|
||||
管理员账号:user_type=admin 且 phone 匹配(兼容旧后台)
|
||||
失败一律 HTTP 200 + code,避免前端 401 误踢登录
|
||||
"""
|
||||
account = _frontend_account(request)
|
||||
if not account:
|
||||
return False, Response({'code': 1, 'msg': '账号不能为空', 'data': None})
|
||||
|
||||
user = request.user
|
||||
if user.user_type == 'admin' and user.phone == account:
|
||||
try:
|
||||
user.admin_profile
|
||||
return True, None
|
||||
except AdminProfile.DoesNotExist:
|
||||
pass
|
||||
|
||||
kefu, perms_or_resp = verify_kefu_permission(request, account)
|
||||
if kefu is None:
|
||||
if isinstance(perms_or_resp, Response):
|
||||
body = perms_or_resp.data if hasattr(perms_or_resp, 'data') else {}
|
||||
code = body.get('code', 403)
|
||||
msg = body.get('msg', '身份验证失败')
|
||||
return False, Response({'code': code, 'msg': msg, 'data': None})
|
||||
return False, Response({'code': 403, 'msg': '身份验证失败', 'data': None})
|
||||
|
||||
if REQUIRED_PERMISSION not in perms_or_resp:
|
||||
return False, Response({
|
||||
'code': 403,
|
||||
'msg': '您没有权限访问小程序配置(需权限 8080a)',
|
||||
'data': None,
|
||||
})
|
||||
return True, None
|
||||
|
||||
|
||||
@@ -38,13 +75,13 @@ def _mask_secret(val):
|
||||
|
||||
|
||||
class XcxSysPeizhiQueryView(APIView):
|
||||
"""POST /peizhi/xcxsyshq — 管理员获取小程序系统配置"""
|
||||
"""POST /peizhi/xcxsyshq — 获取小程序系统配置"""
|
||||
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
parser_classes = [JSONParser]
|
||||
|
||||
def post(self, request):
|
||||
ok, err = _verify_admin(request, request.data.get('zhanghao'))
|
||||
ok, err = _authorize_miniapp_config(request)
|
||||
if not ok:
|
||||
return err
|
||||
row = XcxSysPeizhi.objects.first()
|
||||
@@ -82,7 +119,7 @@ class XcxSysPeizhiQueryView(APIView):
|
||||
|
||||
|
||||
class XcxSysPeizhiUpdateView(APIView):
|
||||
"""POST /peizhi/xcxsysgx — 管理员更新小程序系统配置"""
|
||||
"""POST /peizhi/xcxsysgx — 更新小程序系统配置"""
|
||||
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
parser_classes = [JSONParser]
|
||||
@@ -96,7 +133,7 @@ class XcxSysPeizhiUpdateView(APIView):
|
||||
})
|
||||
|
||||
def post(self, request):
|
||||
ok, err = _verify_admin(request, request.data.get('zhanghao'))
|
||||
ok, err = _authorize_miniapp_config(request)
|
||||
if not ok:
|
||||
return err
|
||||
payload = request.data.get('config') or request.data
|
||||
@@ -120,13 +157,13 @@ class XcxSysPeizhiUpdateView(APIView):
|
||||
|
||||
|
||||
class XcxPageZiyuanQueryView(APIView):
|
||||
"""POST /peizhi/xcxyzycx — 管理员查询页面资源列表"""
|
||||
"""POST /peizhi/xcxyzycx — 查询页面资源列表"""
|
||||
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
parser_classes = [JSONParser]
|
||||
|
||||
def post(self, request):
|
||||
ok, err = _verify_admin(request, request.data.get('zhanghao'))
|
||||
ok, err = _authorize_miniapp_config(request)
|
||||
if not ok:
|
||||
return err
|
||||
zu = (request.data.get('ziyuan_zu') or '').strip()
|
||||
@@ -148,18 +185,18 @@ class XcxPageZiyuanQueryView(APIView):
|
||||
|
||||
|
||||
class XcxPageZiyuanUpdateView(APIView):
|
||||
"""POST /peizhi/xcxyzgx — 管理员更新页面资源路径"""
|
||||
"""POST /peizhi/xcxyzgx — 更新页面资源路径"""
|
||||
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
parser_classes = [JSONParser]
|
||||
|
||||
def post(self, request):
|
||||
ok, err = _verify_admin(request, request.data.get('zhanghao'))
|
||||
ok, err = _authorize_miniapp_config(request)
|
||||
if not ok:
|
||||
return err
|
||||
items = request.data.get('items') or []
|
||||
if not isinstance(items, list) or not items:
|
||||
return Response({'code': 1, 'msg': 'items 不能为空', 'data': None}, status=400)
|
||||
return Response({'code': 1, 'msg': 'items 不能为空', 'data': None})
|
||||
with transaction.atomic():
|
||||
for item in items:
|
||||
zu = (item.get('ziyuan_zu') or '').strip()
|
||||
@@ -176,25 +213,25 @@ class XcxPageZiyuanUpdateView(APIView):
|
||||
|
||||
|
||||
class XcxPageZiyuanUploadView(APIView):
|
||||
"""POST /peizhi/xcxyzsc — 管理员上传页面资源图片"""
|
||||
"""POST /peizhi/xcxyzsc — 上传页面资源图片"""
|
||||
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
parser_classes = (MultiPartParser, FormParser)
|
||||
|
||||
def post(self, request):
|
||||
ok, err = _verify_admin(request, request.data.get('zhanghao'))
|
||||
ok, err = _authorize_miniapp_config(request)
|
||||
if not ok:
|
||||
return err
|
||||
zu = (request.data.get('ziyuan_zu') or '').strip()
|
||||
key = (request.data.get('ziyuan_key') or '').strip()
|
||||
image_file = request.FILES.get('image')
|
||||
if not zu or not key:
|
||||
return Response({'code': 1, 'msg': 'ziyuan_zu/ziyuan_key 必填', 'data': None}, status=400)
|
||||
return Response({'code': 1, 'msg': 'ziyuan_zu/ziyuan_key 必填', 'data': None})
|
||||
if not image_file:
|
||||
return Response({'code': 2, 'msg': '请选择图片', 'data': None}, status=400)
|
||||
return Response({'code': 2, 'msg': '请选择图片', 'data': None})
|
||||
is_valid, error_msg = validate_image(image_file)
|
||||
if not is_valid:
|
||||
return Response({'code': 3, 'msg': error_msg, 'data': None}, status=400)
|
||||
return Response({'code': 3, 'msg': error_msg, 'data': None})
|
||||
|
||||
default_key = f'{zu}.{key}'
|
||||
default_path = DEFAULT_ZIYUAN.get(default_key, f'beijing/{zu}/{key}.png')
|
||||
@@ -202,7 +239,7 @@ class XcxPageZiyuanUploadView(APIView):
|
||||
image_file.seek(0)
|
||||
full_url = upload_to_oss(image_file, oss_path)
|
||||
if not full_url:
|
||||
return Response({'code': 4, 'msg': '上传失败', 'data': None}, status=500)
|
||||
return Response({'code': 4, 'msg': '上传失败', 'data': None})
|
||||
|
||||
with transaction.atomic():
|
||||
XcxPageZiyuan.objects.update_or_create(
|
||||
|
||||
Reference in New Issue
Block a user