feat(admin): Phase 1 注册邀请码逻辑 — 码必填 + kind 分流 + validate-invite + 注册页码优先重做
后端:Invitation.kind(join_team/create_team)+ team 可空 + migration;RegisterSerializer 邀请码必填, create_team 开新团队当 owner+发试用额度、join_team 幂等入伙不重复发额度,删「空码开团队」; validate-invite 免登录验码端点;团管发码恒 join_team;InvitationSerializer 增 kind/team_name。 前端:注册页码优先(验过才展开;create_team 填团队名 / join_team 只读团队名,不暴露团队列表)、 邀请链接 ?invite= 自动验码、api.validateInvite、验码行/状态提示样式(仅 token)。 测试:accounts 22/22 单测过(RegisterInviteCodeTests + ValidateInviteTests); 无头 e2e _admin-p1.mjs 全断言过 + 0 console error;tsc+build 绿。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
443023a1a9
commit
3f8547564c
@@ -83,6 +83,34 @@ def register(request):
|
||||
return Response(auth_payload(data["user"], data["team"], token), status=status.HTTP_201_CREATED)
|
||||
|
||||
|
||||
@api_view(["POST"])
|
||||
@permission_classes([])
|
||||
def validate_invite(request):
|
||||
"""免登录验码:注册页「填码 → 验过才展开表单」用。
|
||||
返回 {valid, kind, team_name?, role?};无效返回 {valid:false, detail}(始终 200,不暴露是否存在以外的细节)。"""
|
||||
from django.utils import timezone
|
||||
|
||||
code = str(request.data.get("code") or request.data.get("invite_code") or "").strip().upper()
|
||||
if not code:
|
||||
return Response({"valid": False, "detail": "请填写邀请码"})
|
||||
inv = Invitation.objects.select_related("team").filter(code=code).first()
|
||||
if inv is None:
|
||||
return Response({"valid": False, "detail": "邀请码不存在"})
|
||||
if inv.status != Invitation.Status.PENDING:
|
||||
return Response({"valid": False, "detail": "邀请码已被使用或已撤销"})
|
||||
if inv.expires_at <= timezone.now():
|
||||
return Response({"valid": False, "detail": "邀请码已过期"})
|
||||
return Response(
|
||||
{
|
||||
"valid": True,
|
||||
"kind": inv.kind,
|
||||
"role": inv.role,
|
||||
# join_team 回团队名(只读展示);create_team 无团队
|
||||
"team_name": inv.team.name if inv.team_id else None,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@api_view(["POST"])
|
||||
@permission_classes([])
|
||||
def login(request):
|
||||
@@ -265,6 +293,7 @@ def team_invitations(request):
|
||||
role = TeamMember.Role.ADMIN # 邀请不允许 OWNER(Team.owner 唯一)
|
||||
invite = Invitation.objects.create(
|
||||
team=team,
|
||||
kind=Invitation.Kind.JOIN_TEAM, # 团管发的码恒为「加入本团队」
|
||||
role=role,
|
||||
email=str(request.data.get("email") or "").strip(),
|
||||
monthly_credit_limit=request.data.get("monthly_credit_limit") or request.data.get("monthly") or 0,
|
||||
|
||||
Reference in New Issue
Block a user