""" 邀请成员到 Workspace Use Case """ import secrets from datetime import datetime, timedelta, timezone from typing import Optional from uuid import uuid4 from packages.domain.entities import ( WorkspaceInvitation, WorkspaceMemberRole, InvitationStatus, ) from packages.domain.auth import email_service class InviteMemberRequest: """邀请成员请求""" def __init__( self, workspace_id: str, inviter_user_id: str, invitee_email: str, role: str, ): self.workspace_id = workspace_id self.inviter_user_id = inviter_user_id self.invitee_email = invitee_email.strip().lower() self.role = role class InviteMemberResponse: """邀请成员响应""" def __init__( self, invitation_id: str, invitee_email: str, role: str, expires_at: datetime, ): self.invitation_id = invitation_id self.invitee_email = invitee_email self.role = role self.expires_at = expires_at class InviteMemberUseCase: """邀请成员用例""" VALID_ROLES = [ WorkspaceMemberRole.ADMIN, WorkspaceMemberRole.MEMBER, WorkspaceMemberRole.VIEWER, ] def __init__( self, workspace_repository, workspace_member_repository, workspace_invitation_repository, user_repository, base_url: str = "http://localhost:3000", invitation_expire_days: int = 7, ): self.workspace_repository = workspace_repository self.workspace_member_repository = workspace_member_repository self.workspace_invitation_repository = workspace_invitation_repository self.user_repository = user_repository self.base_url = base_url self.invitation_expire_days = invitation_expire_days def execute(self, request: InviteMemberRequest) -> tuple[Optional[InviteMemberResponse], Optional[str]]: """ 执行邀请成员 Args: request: 邀请请求 Returns: (响应, 错误信息) """ try: # 1. 验证输入 if not request.workspace_id: return None, "Workspace ID is required" if not request.inviter_user_id: return None, "Inviter user ID is required" if not request.invitee_email: return None, "Invitee email is required" if not request.role: return None, "Role is required" # 2. 验证角色(不能邀请 owner) if request.role not in self.VALID_ROLES: return None, f"Invalid role: {request.role}. Cannot invite as owner." # 3. 验证 Workspace 存在 workspace = self.workspace_repository.find_by_id(request.workspace_id) if not workspace: return None, "Workspace not found" # 4. 验证邀请人是成员且有权限(owner 或 admin) inviter_member = self.workspace_member_repository.find_by_workspace_and_user( request.workspace_id, request.inviter_user_id, ) if not inviter_member: return None, "You are not a member of this workspace" if inviter_member.role not in [WorkspaceMemberRole.OWNER, WorkspaceMemberRole.ADMIN]: return None, "Only owners and admins can invite members" # 5. 检查被邀请人是否已经是成员 invitee_user = self.user_repository.find_by_email(request.invitee_email) if invitee_user: existing_member = self.workspace_member_repository.find_by_workspace_and_user( request.workspace_id, invitee_user.id, ) if existing_member: return None, "User is already a member of this workspace" # 6. 检查是否已有待处理的邀请 existing_invitation = self.workspace_invitation_repository.find_pending_by_workspace_and_email( request.workspace_id, request.invitee_email, ) if existing_invitation: return None, "An invitation has already been sent to this email" # 7. 生成邀请令牌 invitation_token = secrets.token_urlsafe(32) expires_at = datetime.now(timezone.utc) + timedelta(days=self.invitation_expire_days) # 8. 创建邀请记录 invitation = WorkspaceInvitation( id=uuid4().hex, workspace_id=request.workspace_id, inviter_user_id=request.inviter_user_id, invitee_email=request.invitee_email, role=request.role, invitation_token=invitation_token, status=InvitationStatus.PENDING, expires_at=expires_at, created_at=datetime.now(timezone.utc), ) # 9. 保存邀请 self.workspace_invitation_repository.save(invitation) # 10. 发送邀请邮件 invitation_url = f"{self.base_url}/invitations/{invitation_token}/accept" try: inviter = self.user_repository.find_by_id(request.inviter_user_id) inviter_name = inviter.display_name if inviter else "Someone" success, error = email_service.send_workspace_invitation_email( to_email=request.invitee_email, inviter_name=inviter_name, workspace_name=workspace.name, role=request.role, invitation_url=invitation_url, ) if not success: print(f"Failed to send invitation email: {error}") except Exception as e: print(f"Email service error: {e}") # 11. 返回响应 return InviteMemberResponse( invitation_id=invitation.id, invitee_email=invitation.invitee_email, role=invitation.role, expires_at=invitation.expires_at, ), None except Exception as e: return None, f"Failed to invite member: {str(e)}"