板块 ID 统一按整数校验,避免字符串与数据库整型比较导致误报不存在;板块接口补充 scope 与 bankuai_id 校验。 Co-authored-by: Cursor <cursoragent@cursor.com>
132 lines
5.0 KiB
Python
132 lines
5.0 KiB
Python
"""考核官后台管理(按俱乐部隔离)。"""
|
||
import logging
|
||
|
||
from django.db import transaction
|
||
from rest_framework.permissions import IsAuthenticated
|
||
from rest_framework.response import Response
|
||
from rest_framework.views import APIView
|
||
|
||
from backend.view import verify_kefu_permission
|
||
from jituan.services.club_user import get_user_club_id
|
||
from jituan.services.shenhe_club import user_belongs_to_request_club
|
||
from rank.models import Bankuai, KaoheguanBankuai
|
||
from users.business_models import User
|
||
from users.models import UserShenheguan
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
def _normalize_bankuai_ids(data):
|
||
"""将板块 ID 统一为整数(bankuai_id 为 AutoField)。"""
|
||
ids = data.get('bankuai_ids')
|
||
if ids is None and data.get('bankuai_id') is not None:
|
||
ids = [data.get('bankuai_id')]
|
||
if not isinstance(ids, list):
|
||
return []
|
||
out = []
|
||
for item in ids:
|
||
try:
|
||
bid = int(str(item).strip())
|
||
except (TypeError, ValueError):
|
||
continue
|
||
if bid > 0 and bid not in out:
|
||
out.append(bid)
|
||
return out
|
||
|
||
|
||
class ClubKhgglAddView(APIView):
|
||
"""
|
||
后台添加考核官(升级为考核官并关联板块)。
|
||
POST /jituan/houtai/khggl-add
|
||
Body: {
|
||
"phone": "管理员手机号",
|
||
"yonghuid": "用户ID",
|
||
"bankuai_ids": ["bk001", "bk002"], // 或 bankuai_id 单个
|
||
"is_renzheng": true // 可选,默认 true
|
||
}
|
||
"""
|
||
permission_classes = [IsAuthenticated]
|
||
|
||
def post(self, request):
|
||
try:
|
||
username_from_frontend = request.data.get('phone', None)
|
||
kefu_obj, permissions = verify_kefu_permission(request, username_from_frontend)
|
||
if kefu_obj is None:
|
||
return permissions
|
||
if 'kaohepeizhi' not in permissions:
|
||
return Response({'code': 403, 'msg': '无考核管理权限'}, status=403)
|
||
|
||
yonghuid = str(request.data.get('yonghuid', '')).strip()
|
||
bankuai_ids = _normalize_bankuai_ids(request.data)
|
||
is_renzheng = request.data.get('is_renzheng', True)
|
||
if isinstance(is_renzheng, str):
|
||
is_renzheng = is_renzheng.lower() in ('1', 'true', 'yes')
|
||
|
||
if not yonghuid:
|
||
return Response({'code': 1, 'msg': '请输入用户ID'}, status=400)
|
||
if not bankuai_ids:
|
||
return Response({'code': 1, 'msg': '请选择至少一个板块'}, status=400)
|
||
|
||
try:
|
||
user = User.query.get(UserUID=yonghuid)
|
||
except User.DoesNotExist:
|
||
return Response({'code': 1, 'msg': '用户ID不存在'}, status=404)
|
||
|
||
if not user_belongs_to_request_club(user, request):
|
||
return Response({'code': 403, 'msg': '该用户不属于当前俱乐部,无法添加为考核官'}, status=403)
|
||
|
||
# 一次性查询所有板块是否存在,避免循环内 N+1 exists
|
||
_existing_bids = set(
|
||
Bankuai.query.filter(bankuai_id__in=bankuai_ids)
|
||
.values_list('bankuai_id', flat=True)
|
||
)
|
||
for bid in bankuai_ids:
|
||
if bid not in _existing_bids:
|
||
return Response({'code': 1, 'msg': f'板块不存在: {bid}'}, status=400)
|
||
|
||
created_new = False
|
||
with transaction.atomic():
|
||
kg, created_new = UserShenheguan.query.get_or_create(
|
||
user=user,
|
||
defaults={
|
||
'zhuangtai': 1,
|
||
'shenhe_zhuangtai': 0,
|
||
'is_renzheng': bool(is_renzheng),
|
||
'shenhe_zongshu': 0,
|
||
'tongguo_zongshu': 0,
|
||
'yue': 0,
|
||
'zonge': 0,
|
||
},
|
||
)
|
||
if not created_new:
|
||
if kg.zhuangtai != 1:
|
||
kg.zhuangtai = 1
|
||
if is_renzheng:
|
||
kg.is_renzheng = True
|
||
kg.save()
|
||
|
||
added_bankuai = []
|
||
for bid in bankuai_ids:
|
||
rel, rel_created = KaoheguanBankuai.query.get_or_create(
|
||
kaoheguan_id=yonghuid,
|
||
bankuai_id=bid,
|
||
)
|
||
if rel_created:
|
||
added_bankuai.append(bid)
|
||
|
||
msg = '考核官添加成功' if created_new else '已关联板块(该用户已是考核官)'
|
||
return Response({
|
||
'code': 0,
|
||
'msg': msg,
|
||
'data': {
|
||
'yonghuid': yonghuid,
|
||
'created': created_new,
|
||
'added_bankuai_ids': added_bankuai,
|
||
'club_id': get_user_club_id(user),
|
||
'is_renzheng': bool(kg.is_renzheng),
|
||
},
|
||
})
|
||
except Exception as e:
|
||
logger.exception('ClubKhgglAddView 接口错误')
|
||
return Response({'code': 1, 'msg': f'服务器内部错误: {str(e)}'}, status=500)
|