Files
TermiNexus 31b0ab885f refactor(orm): 手写 ORM 下沉 gvsdsdk Manager,通用工具改为 SDK 转发
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 补齐限频去重状态。
2026-09-13 21:58:06 +08:00

133 lines
6.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""模块: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,
})