P1 通用查询:viewsets / app / menu / mail / gitea / mc / stats / sync_apps 等视图与命令的裸查询改委托 SDK Manager(Role/Company/Department/Position/Menu/Tenant/App)。 P2 App 体系:app_manager.py 与 app_manifests.py 实现零裸 ORM,职务多值/虚拟职务/公司下拉全部走 SDK。 P3 工具下沉:money/json_encoder/celery_utils/exception_handler/redis_lock 改为纯转发展开;ip_filter/storage 转发并注入 app.ini 配置源;common 下 DateTime/String/Naming/UUID 转发 SDK。 删除死代码 commission/strategies/engine.py(SDK 已有同物且零引用)与 payment/models.py 的 FromTenantAndPlatform;单测 patch 目标同步为 gvsdsdk.*,test_attack_detect fixture 补齐限频去重状态。
133 lines
6.2 KiB
Python
133 lines
6.2 KiB
Python
"""模块:MC 视图
|
||
|
||
- ``UserBindView``:POST /API/UserBind.api,已登录的 GVSDS 用户通过游戏账号密码
|
||
绑定 MC 玩家身份,自动创建 UserRole 关联(枢机协律玩家角色 + 枢机协律政府公司),
|
||
并在 easyauth 表写入 UserRoleUUID 作为绑定标记。
|
||
|
||
鉴权依赖全局 DRF 配置(CustomJWTAuthentication + IsAuthenticated)。
|
||
所有底层 ORM 操作均委托给 gvsdsdk 管理器,避免直接操作表。"""
|
||
|
||
from django.db import transaction
|
||
from rest_framework import status
|
||
from rest_framework.permissions import IsAuthenticated
|
||
from rest_framework.response import Response
|
||
from rest_framework.views import APIView
|
||
|
||
from gvsds.apps.mc.manager import EasyAuthManager
|
||
from gvsdsdk.auth import RoleManager, CompanyManager
|
||
|
||
|
||
class UserBindView(APIView):
|
||
"""游戏账号绑定视图——已登录的 GVSDS 用户输入游戏名+密码绑定 MC 身份
|
||
|
||
流程:
|
||
1. 校验游戏账号存在性(EasyAuthManager.GetByUsername 查 easyauth 表)
|
||
2. bcrypt 校验密码(EasyAuthManager.VerifyPassword,password 存在 data JSON 中)
|
||
3. 检查 easyauth.UserRoleUUID 是否已绑定(EasyAuthManager.IsBound)
|
||
4. 检查当前 GVSDS 用户是否已绑定过该角色(RoleManager.HasRole 防重复)
|
||
5. 创建 UserRole 关联(RoleManager.AssignRole):
|
||
UserUUID + RoleUUID(枢机协律玩家) + CompanyUUID(枢机协律政府)
|
||
6. UPDATE easyauth.UserRoleUUID = 新生成的 UserRoleUUID(EasyAuthManager.MarkBound)
|
||
7. 返回 redirect 跳转地址
|
||
|
||
请求体:``{"PlayerName": "游戏名", "PlayerPasswd": "游戏密码"}``
|
||
响应:``{"status": 0, "redirect": "..."}`` 或 ``{"status": 1, "msg": "..."}``
|
||
"""
|
||
|
||
# 角色名与公司名(按用户原话硬编码,便于将来重命名时一处修改)
|
||
PLAYER_ROLE_NAME = '枢机协律玩家'
|
||
PLAYER_COMPANY_NAME = '枢机协律政府'
|
||
REDIRECT_URL = 'https://gerp.gvsds.com/'
|
||
|
||
def post(self, request):
|
||
# 1. 鉴权由全局 DRF 保证;从 request.user 取 GVSDS UserUUID
|
||
user = request.user
|
||
if user is None or not getattr(user, 'is_authenticated', False):
|
||
return Response({'status': 1, 'msg': '请先登录 GVSDS 账户'},
|
||
status=status.HTTP_401_UNAUTHORIZED)
|
||
|
||
gvsds_user_uuid = user.UserUUID
|
||
|
||
# 2. 解析请求体
|
||
body = request.data or {}
|
||
player_name = (body.get('PlayerName') or '').strip()
|
||
player_passwd = body.get('PlayerPasswd') or ''
|
||
if not player_name or not player_passwd:
|
||
return Response({'status': 1, 'msg': '游戏名或密码不能为空'})
|
||
|
||
# 3. 查 easyauth 行
|
||
easyauth = EasyAuthManager.GetByUsername(player_name)
|
||
if easyauth is None:
|
||
return Response({'status': 1, 'msg': '游戏账户不存在'})
|
||
|
||
# 4. 校验密码(data.password 是 bcrypt hash)
|
||
if not EasyAuthManager.VerifyPassword(easyauth, player_passwd):
|
||
return Response({'status': 1, 'msg': '游戏密码错误'})
|
||
|
||
# 5. 检查该游戏账号是否已被绑定
|
||
if EasyAuthManager.IsBound(easyauth):
|
||
return Response({'status': 1, 'msg': '该游戏账号已绑定其他 GVSDS 用户'})
|
||
|
||
# 6. 查询角色与公司 UUID
|
||
role = RoleManager.GetRoleByName(self.PLAYER_ROLE_NAME)
|
||
if role is None:
|
||
return Response({'status': 1, 'msg': f'系统未找到角色:{self.PLAYER_ROLE_NAME}'})
|
||
company = CompanyManager.GetCompanyByName(self.PLAYER_COMPANY_NAME)
|
||
if company is None:
|
||
return Response({'status': 1, 'msg': f'系统未找到公司:{self.PLAYER_COMPANY_NAME}'})
|
||
|
||
# 7. 检查当前 GVSDS 用户是否已绑定过该游戏账号(防止重复绑定同一玩家)
|
||
# 允许一个用户绑定多个不同玩家(多对多),但不允许重复绑定同一玩家。
|
||
from gvsds.apps.mc.models import EasyAuth as _EasyAuthModel
|
||
_user_role_uuids = RoleManager.ListUserRoleUUIDs(gvsds_user_uuid)
|
||
if _user_role_uuids and _EasyAuthModel.objects.filter(
|
||
AuthID=easyauth.AuthID,
|
||
UserRoleUUID__in=_user_role_uuids,
|
||
).exists():
|
||
return Response({'status': 1, 'msg': '你已绑定过该游戏账号,请勿重复绑定'})
|
||
|
||
# 8. 创建 UserRole 关联(default 库)
|
||
# RoleName 覆盖:让该用户在 GERP 中显示为 "枢机协律玩家 - {游戏名}",
|
||
# 而非全局共享的 Role.RoleName;为空时前端回退到 Role.RoleName。
|
||
display_role_name = f'{self.PLAYER_ROLE_NAME} - {player_name}'
|
||
try:
|
||
with transaction.atomic(using='default'):
|
||
user_role = RoleManager.AssignRole(
|
||
UserUUID=gvsds_user_uuid,
|
||
RoleUUID=role.RoleUUID,
|
||
CompanyUUID=company.CompanyUUID,
|
||
RoleName=display_role_name,
|
||
)
|
||
except Exception as e:
|
||
return Response({'status': 1, 'msg': f'创建角色关联失败:{e}'})
|
||
|
||
# 9. UPDATE easyauth 写入 UserRoleUUID 作为绑定标记(跨库 minecraft)
|
||
# 乐观锁:UserRoleUUID__isnull=True 防止并发重复绑定
|
||
try:
|
||
with transaction.atomic(using='minecraft'):
|
||
updated = EasyAuthManager.MarkBound(easyauth, user_role.UserRoleUUID)
|
||
except Exception as e:
|
||
# 回滚刚创建的 UserRole
|
||
RoleManager.RemoveRole(
|
||
UserUUID=gvsds_user_uuid,
|
||
RoleUUID=role.RoleUUID,
|
||
CompanyUUID=company.CompanyUUID,
|
||
)
|
||
return Response({'status': 1, 'msg': f'更新游戏账号绑定状态失败:{e}'})
|
||
|
||
if updated == 0:
|
||
# 并发竞争:另一个请求已绑定,回滚刚创建的 UserRole
|
||
RoleManager.RemoveRole(
|
||
UserUUID=gvsds_user_uuid,
|
||
RoleUUID=role.RoleUUID,
|
||
CompanyUUID=company.CompanyUUID,
|
||
)
|
||
return Response({'status': 1, 'msg': '该游戏账号已被其他请求绑定,请重试'})
|
||
|
||
# 10. 成功
|
||
return Response({
|
||
'status': 0,
|
||
'redirect': self.REDIRECT_URL,
|
||
'PlayerUUID': easyauth.PlayerUUID,
|
||
})
|