style: normalize python formatting gates

This commit is contained in:
Xiaoxia AI
2026-06-21 06:52:19 +08:00
parent 0809a079c5
commit bfbaddbd9a
129 changed files with 3024 additions and 2485 deletions
+35 -34
View File
@@ -1,53 +1,54 @@
"""Workspace 相关 Use Cases"""
from packages.application.workspace.create_workspace_use_case import (
CreateWorkspaceUseCase,
CreateWorkspaceRequest,
CreateWorkspaceResponse,
)
from packages.application.workspace.invite_member_use_case import (
InviteMemberUseCase,
InviteMemberRequest,
InviteMemberResponse,
)
from packages.application.workspace.accept_invitation_use_case import (
AcceptInvitationUseCase,
AcceptInvitationRequest,
AcceptInvitationResponse,
DeclineInvitationUseCase,
AcceptInvitationUseCase,
DeclineInvitationRequest,
DeclineInvitationUseCase,
)
from packages.application.workspace.remove_member_use_case import (
RemoveMemberUseCase,
RemoveMemberRequest,
LeaveWorkspaceUseCase,
LeaveWorkspaceRequest,
from packages.application.workspace.create_workspace_use_case import (
CreateWorkspaceRequest,
CreateWorkspaceResponse,
CreateWorkspaceUseCase,
)
from packages.application.workspace.update_member_role_use_case import (
UpdateMemberRoleUseCase,
UpdateMemberRoleRequest,
UpdateMemberRoleResponse,
)
from packages.application.workspace.list_workspaces_use_case import (
ListWorkspacesUseCase,
ListWorkspacesRequest,
ListWorkspacesResponse,
GetWorkspaceDetailUseCase,
GetWorkspaceDetailRequest,
WorkspaceInfo,
WorkspaceDetailInfo,
from packages.application.workspace.invite_member_use_case import (
InviteMemberRequest,
InviteMemberResponse,
InviteMemberUseCase,
)
from packages.application.workspace.list_members_use_case import (
ListMembersUseCase,
ListMembersRequest,
ListMembersResponse,
ListMembersUseCase,
MemberInfo,
)
from packages.application.workspace.list_workspaces_use_case import (
GetWorkspaceDetailRequest,
GetWorkspaceDetailUseCase,
ListWorkspacesRequest,
ListWorkspacesResponse,
ListWorkspacesUseCase,
WorkspaceDetailInfo,
WorkspaceInfo,
)
from packages.application.workspace.remove_member_use_case import (
LeaveWorkspaceRequest,
LeaveWorkspaceUseCase,
RemoveMemberRequest,
RemoveMemberUseCase,
)
from packages.application.workspace.subscription_use_case import (
UpgradeSubscriptionUseCase,
CancelSubscriptionRequest,
CancelSubscriptionUseCase,
UpgradeSubscriptionRequest,
UpgradeSubscriptionResponse,
CancelSubscriptionUseCase,
CancelSubscriptionRequest,
UpgradeSubscriptionUseCase,
)
from packages.application.workspace.update_member_role_use_case import (
UpdateMemberRoleRequest,
UpdateMemberRoleResponse,
UpdateMemberRoleUseCase,
)
__all__ = [
@@ -1,19 +1,17 @@
"""
接受/拒绝邀请 Use Case
"""
from datetime import datetime, timezone
from typing import Optional
from uuid import uuid4
from packages.domain.entities import (
WorkspaceMember,
InvitationStatus,
)
from packages.domain.entities import InvitationStatus, WorkspaceMember
class AcceptInvitationRequest:
"""接受邀请请求"""
def __init__(self, invitation_token: str, user_id: str):
self.invitation_token = invitation_token
self.user_id = user_id
@@ -21,7 +19,7 @@ class AcceptInvitationRequest:
class AcceptInvitationResponse:
"""接受邀请响应"""
def __init__(
self,
workspace_id: str,
@@ -35,7 +33,7 @@ class AcceptInvitationResponse:
class AcceptInvitationUseCase:
"""接受邀请用例"""
def __init__(
self,
workspace_repository,
@@ -47,14 +45,14 @@ class AcceptInvitationUseCase:
self.workspace_member_repository = workspace_member_repository
self.workspace_invitation_repository = workspace_invitation_repository
self.user_repository = user_repository
def execute(self, request: AcceptInvitationRequest) -> tuple[Optional[AcceptInvitationResponse], Optional[str]]:
"""
执行接受邀请
Args:
request: 接受请求
Returns:
(响应, 错误信息)
"""
@@ -62,40 +60,40 @@ class AcceptInvitationUseCase:
# 1. 验证输入
if not request.invitation_token:
return None, "Invitation token is required"
if not request.user_id:
return None, "User ID is required"
# 2. 查找邀请
invitation = self.workspace_invitation_repository.find_by_token(request.invitation_token)
if not invitation:
return None, "Invalid invitation token"
# 3. 检查邀请状态
if invitation.status != InvitationStatus.PENDING:
return None, f"Invitation has already been {invitation.status}"
# 4. 检查是否过期
if invitation.expires_at and datetime.now(timezone.utc) > invitation.expires_at:
# 更新状态为过期
invitation.status = InvitationStatus.EXPIRED
self.workspace_invitation_repository.save(invitation)
return None, "Invitation has expired"
# 5. 验证用户存在
user = self.user_repository.find_by_id(request.user_id)
if not user:
return None, "User not found"
# 6. 验证用户邮箱匹配
if user.email.lower() != invitation.invitee_email.lower():
return None, "This invitation is for a different email address"
# 7. 验证 Workspace 存在
workspace = self.workspace_repository.find_by_id(invitation.workspace_id)
if not workspace:
return None, "Workspace not found"
# 8. 检查用户是否已经是成员
existing_member = self.workspace_member_repository.find_by_workspace_and_user(
invitation.workspace_id,
@@ -106,13 +104,16 @@ class AcceptInvitationUseCase:
invitation.status = InvitationStatus.ACCEPTED
invitation.accepted_at = datetime.now(timezone.utc)
self.workspace_invitation_repository.save(invitation)
return AcceptInvitationResponse(
workspace_id=workspace.id,
workspace_name=workspace.name,
role=existing_member.role,
), None
return (
AcceptInvitationResponse(
workspace_id=workspace.id,
workspace_name=workspace.name,
role=existing_member.role,
),
None,
)
# 9. 创建成员记录
member = WorkspaceMember(
id=uuid4().hex,
@@ -122,45 +123,48 @@ class AcceptInvitationUseCase:
invited_by=invitation.inviter_user_id,
joined_at=datetime.now(timezone.utc),
)
self.workspace_member_repository.save(member)
# 10. 更新邀请状态
invitation.status = InvitationStatus.ACCEPTED
invitation.accepted_at = datetime.now(timezone.utc)
self.workspace_invitation_repository.save(invitation)
# 11. 返回响应
return AcceptInvitationResponse(
workspace_id=workspace.id,
workspace_name=workspace.name,
role=member.role,
), None
return (
AcceptInvitationResponse(
workspace_id=workspace.id,
workspace_name=workspace.name,
role=member.role,
),
None,
)
except Exception as e:
return None, f"Failed to accept invitation: {str(e)}"
class DeclineInvitationRequest:
"""拒绝邀请请求"""
def __init__(self, invitation_token: str):
self.invitation_token = invitation_token
class DeclineInvitationUseCase:
"""拒绝邀请用例"""
def __init__(self, workspace_invitation_repository):
self.workspace_invitation_repository = workspace_invitation_repository
def execute(self, request: DeclineInvitationRequest) -> tuple[bool, Optional[str]]:
"""
执行拒绝邀请
Args:
request: 拒绝请求
Returns:
(是否成功, 错误信息)
"""
@@ -168,21 +172,21 @@ class DeclineInvitationUseCase:
# 1. 验证输入
if not request.invitation_token:
return False, "Invitation token is required"
# 2. 查找邀请
invitation = self.workspace_invitation_repository.find_by_token(request.invitation_token)
if not invitation:
return False, "Invalid invitation token"
# 3. 检查邀请状态
if invitation.status != InvitationStatus.PENDING:
return False, f"Invitation has already been {invitation.status}"
# 4. 更新状态为已拒绝
invitation.status = InvitationStatus.DECLINED
self.workspace_invitation_repository.save(invitation)
return True, None
except Exception as e:
return False, f"Failed to decline invitation: {str(e)}"
@@ -1,16 +1,17 @@
"""
创建 Workspace Use Case
"""
from datetime import datetime, timezone
from typing import Optional
from uuid import uuid4
from datetime import datetime, timezone
from packages.domain.entities import Workspace, WorkspaceMember, WorkspaceMemberRole
class CreateWorkspaceRequest:
"""创建工作空间请求"""
def __init__(
self,
name: str,
@@ -24,7 +25,7 @@ class CreateWorkspaceRequest:
class CreateWorkspaceResponse:
"""创建工作空间响应"""
def __init__(
self,
workspace_id: str,
@@ -42,14 +43,14 @@ class CreateWorkspaceResponse:
class CreateWorkspaceUseCase:
"""创建工作空间用例"""
# 订阅计划配额配置
PLAN_QUOTAS = {
"free": {"max_projects": 3, "max_storage_gb": 10},
"pro": {"max_projects": 999999, "max_storage_gb": 100}, # 999999 表示无限
"enterprise": {"max_projects": 999999, "max_storage_gb": 1000},
}
def __init__(
self,
workspace_repository,
@@ -59,14 +60,14 @@ class CreateWorkspaceUseCase:
self.workspace_repository = workspace_repository
self.workspace_member_repository = workspace_member_repository
self.user_repository = user_repository
def execute(self, request: CreateWorkspaceRequest) -> tuple[Optional[CreateWorkspaceResponse], Optional[str]]:
"""
执行创建工作空间
Args:
request: 创建请求
Returns:
(响应, 错误信息)
"""
@@ -74,25 +75,25 @@ class CreateWorkspaceUseCase:
# 1. 验证输入
if not request.name:
return None, "Workspace name is required"
if len(request.name) > 100:
return None, "Workspace name is too long (max 100 characters)"
if not request.owner_user_id:
return None, "Owner user ID is required"
# 2. 验证用户存在
owner = self.user_repository.find_by_id(request.owner_user_id)
if not owner:
return None, "Owner user not found"
# 3. 验证订阅计划
if request.subscription_plan not in self.PLAN_QUOTAS:
return None, f"Invalid subscription plan: {request.subscription_plan}"
# 4. 获取配额
quota = self.PLAN_QUOTAS[request.subscription_plan]
# 5. 创建 Workspace
workspace = Workspace(
id=uuid4().hex,
@@ -105,10 +106,10 @@ class CreateWorkspaceUseCase:
used_storage_gb=0.0,
created_at=datetime.now(timezone.utc),
)
# 6. 保存 Workspace
self.workspace_repository.save(workspace)
# 7. 创建 Owner 成员记录
owner_member = WorkspaceMember(
id=uuid4().hex,
@@ -118,17 +119,20 @@ class CreateWorkspaceUseCase:
invited_by=None, # Owner 不需要邀请
joined_at=datetime.now(timezone.utc),
)
self.workspace_member_repository.save(owner_member)
# 8. 返回响应
return CreateWorkspaceResponse(
workspace_id=workspace.id,
name=workspace.name,
subscription_plan=workspace.subscription_plan,
max_projects=workspace.max_projects,
max_storage_gb=workspace.max_storage_gb,
), None
return (
CreateWorkspaceResponse(
workspace_id=workspace.id,
name=workspace.name,
subscription_plan=workspace.subscription_plan,
max_projects=workspace.max_projects,
max_storage_gb=workspace.max_storage_gb,
),
None,
)
except Exception as e:
return None, f"Failed to create workspace: {str(e)}"
@@ -1,6 +1,7 @@
"""
邀请成员到 Workspace Use Case
"""
import secrets
from datetime import datetime, timedelta, timezone
from typing import Optional
@@ -8,15 +9,15 @@ from uuid import uuid4
from packages.adapters.smtp import get_email_service
from packages.domain.entities import (
InvitationStatus,
WorkspaceInvitation,
WorkspaceMemberRole,
InvitationStatus,
)
class InviteMemberRequest:
"""邀请成员请求"""
def __init__(
self,
workspace_id: str,
@@ -32,7 +33,7 @@ class InviteMemberRequest:
class InviteMemberResponse:
"""邀请成员响应"""
def __init__(
self,
invitation_id: str,
@@ -48,13 +49,13 @@ class InviteMemberResponse:
class InviteMemberUseCase:
"""邀请成员用例"""
VALID_ROLES = [
WorkspaceMemberRole.ADMIN,
WorkspaceMemberRole.MEMBER,
WorkspaceMemberRole.VIEWER,
]
def __init__(
self,
workspace_repository,
@@ -72,14 +73,14 @@ class InviteMemberUseCase:
self.base_url = base_url
self.invitation_expire_days = invitation_expire_days
self.email_service = email_service or get_email_service()
def execute(self, request: InviteMemberRequest) -> tuple[Optional[InviteMemberResponse], Optional[str]]:
"""
执行邀请成员
Args:
request: 邀请请求
Returns:
(响应, 错误信息)
"""
@@ -87,25 +88,25 @@ class InviteMemberUseCase:
# 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,
@@ -113,10 +114,13 @@ class InviteMemberUseCase:
)
if not inviter_member:
return None, "You are not a member of this workspace"
if inviter_member.role not in [WorkspaceMemberRole.OWNER, WorkspaceMemberRole.ADMIN]:
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:
@@ -126,7 +130,7 @@ class InviteMemberUseCase:
)
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,
@@ -134,11 +138,11 @@ class InviteMemberUseCase:
)
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,
@@ -151,17 +155,17 @@ class InviteMemberUseCase:
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 = self.email_service.send_workspace_invitation_email(
to_email=request.invitee_email,
inviter_name=inviter_name,
@@ -169,19 +173,22 @@ class InviteMemberUseCase:
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
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)}"
@@ -1,13 +1,14 @@
"""
获取成员列表 Use Case
"""
from typing import Optional, List
from datetime import datetime
from typing import List, Optional
class MemberInfo:
"""成员信息"""
def __init__(
self,
member_id: str,
@@ -31,7 +32,7 @@ class MemberInfo:
class ListMembersRequest:
"""获取成员列表请求"""
def __init__(self, workspace_id: str, requester_user_id: str):
self.workspace_id = workspace_id
self.requester_user_id = requester_user_id
@@ -39,14 +40,14 @@ class ListMembersRequest:
class ListMembersResponse:
"""获取成员列表响应"""
def __init__(self, members: List[MemberInfo]):
self.members = members
class ListMembersUseCase:
"""获取成员列表用例"""
def __init__(
self,
workspace_repository,
@@ -56,14 +57,14 @@ class ListMembersUseCase:
self.workspace_repository = workspace_repository
self.workspace_member_repository = workspace_member_repository
self.user_repository = user_repository
def execute(self, request: ListMembersRequest) -> tuple[Optional[ListMembersResponse], Optional[str]]:
"""
执行获取成员列表
Args:
request: 请求
Returns:
(响应, 错误信息)
"""
@@ -71,15 +72,15 @@ class ListMembersUseCase:
# 1. 验证输入
if not request.workspace_id:
return None, "Workspace ID is required"
if not request.requester_user_id:
return None, "Requester user ID is required"
# 2. 验证工作空间存在
workspace = self.workspace_repository.find_by_id(request.workspace_id)
if not workspace:
return None, "Workspace not found"
# 3. 验证请求者是成员(只有成员才能查看成员列表)
requester_member = self.workspace_member_repository.find_by_workspace_and_user(
request.workspace_id,
@@ -87,17 +88,17 @@ class ListMembersUseCase:
)
if not requester_member:
return None, "You are not a member of this workspace"
# 4. 获取所有成员
members = self.workspace_member_repository.find_by_workspace(request.workspace_id)
# 5. 获取每个成员的用户信息
member_infos = []
for member in members:
user = self.user_repository.find_by_id(member.user_id)
if not user:
continue # 跳过不存在的用户
member_info = MemberInfo(
member_id=member.id,
user_id=user.id,
@@ -109,9 +110,9 @@ class ListMembersUseCase:
joined_at=member.joined_at,
)
member_infos.append(member_info)
# 6. 返回响应
return ListMembersResponse(members=member_infos), None
except Exception as e:
return None, f"Failed to list members: {str(e)}"
@@ -1,13 +1,14 @@
"""
获取工作空间列表和详情 Use Case
"""
from typing import Optional, List
from datetime import datetime
from typing import List, Optional
class WorkspaceInfo:
"""工作空间信息"""
def __init__(
self,
workspace_id: str,
@@ -37,21 +38,21 @@ class WorkspaceInfo:
class ListWorkspacesRequest:
"""获取工作空间列表请求"""
def __init__(self, user_id: str):
self.user_id = user_id
class ListWorkspacesResponse:
"""获取工作空间列表响应"""
def __init__(self, workspaces: List[WorkspaceInfo]):
self.workspaces = workspaces
class ListWorkspacesUseCase:
"""获取工作空间列表用例"""
def __init__(
self,
workspace_repository,
@@ -59,14 +60,14 @@ class ListWorkspacesUseCase:
):
self.workspace_repository = workspace_repository
self.workspace_member_repository = workspace_member_repository
def execute(self, request: ListWorkspacesRequest) -> tuple[Optional[ListWorkspacesResponse], Optional[str]]:
"""
执行获取工作空间列表
Args:
request: 请求
Returns:
(响应, 错误信息)
"""
@@ -74,20 +75,20 @@ class ListWorkspacesUseCase:
# 1. 验证输入
if not request.user_id:
return None, "User ID is required"
# 2. 获取用户所有的成员记录
memberships = self.workspace_member_repository.find_by_user(request.user_id)
# 3. 获取每个工作空间的信息
workspace_infos = []
for membership in memberships:
workspace = self.workspace_repository.find_by_id(membership.workspace_id)
if not workspace:
continue
# 获取成员数量
member_count = self.workspace_member_repository.count_by_workspace(membership.workspace_id)
workspace_info = WorkspaceInfo(
workspace_id=workspace.id,
name=workspace.name,
@@ -102,17 +103,17 @@ class ListWorkspacesUseCase:
created_at=workspace.created_at,
)
workspace_infos.append(workspace_info)
# 4. 返回响应
return ListWorkspacesResponse(workspaces=workspace_infos), None
except Exception as e:
return None, f"Failed to list workspaces: {str(e)}"
class GetWorkspaceDetailRequest:
"""获取工作空间详情请求"""
def __init__(self, workspace_id: str, user_id: str):
self.workspace_id = workspace_id
self.user_id = user_id
@@ -120,7 +121,7 @@ class GetWorkspaceDetailRequest:
class WorkspaceDetailInfo:
"""工作空间详情信息"""
def __init__(
self,
workspace_id: str,
@@ -152,7 +153,7 @@ class WorkspaceDetailInfo:
class GetWorkspaceDetailUseCase:
"""获取工作空间详情用例"""
def __init__(
self,
workspace_repository,
@@ -160,14 +161,14 @@ class GetWorkspaceDetailUseCase:
):
self.workspace_repository = workspace_repository
self.workspace_member_repository = workspace_member_repository
def execute(self, request: GetWorkspaceDetailRequest) -> tuple[Optional[WorkspaceDetailInfo], Optional[str]]:
"""
执行获取工作空间详情
Args:
request: 请求
Returns:
(详情信息, 错误信息)
"""
@@ -175,15 +176,15 @@ class GetWorkspaceDetailUseCase:
# 1. 验证输入
if not request.workspace_id:
return None, "Workspace ID is required"
if not request.user_id:
return None, "User ID is required"
# 2. 验证工作空间存在
workspace = self.workspace_repository.find_by_id(request.workspace_id)
if not workspace:
return None, "Workspace not found"
# 3. 验证用户是成员
membership = self.workspace_member_repository.find_by_workspace_and_user(
request.workspace_id,
@@ -191,10 +192,10 @@ class GetWorkspaceDetailUseCase:
)
if not membership:
return None, "You are not a member of this workspace"
# 4. 获取成员数量
member_count = self.workspace_member_repository.count_by_workspace(request.workspace_id)
# 5. 构建详情信息
detail_info = WorkspaceDetailInfo(
workspace_id=workspace.id,
@@ -210,8 +211,8 @@ class GetWorkspaceDetailUseCase:
user_role=membership.role,
created_at=workspace.created_at,
)
return detail_info, None
except Exception as e:
return None, f"Failed to get workspace detail: {str(e)}"
@@ -1,6 +1,7 @@
"""
移除成员 Use Case
"""
from typing import Optional
from packages.domain.entities import WorkspaceMemberRole
@@ -8,7 +9,7 @@ from packages.domain.entities import WorkspaceMemberRole
class RemoveMemberRequest:
"""移除成员请求"""
def __init__(
self,
workspace_id: str,
@@ -22,7 +23,7 @@ class RemoveMemberRequest:
class RemoveMemberUseCase:
"""移除成员用例"""
def __init__(
self,
workspace_repository,
@@ -30,14 +31,14 @@ class RemoveMemberUseCase:
):
self.workspace_repository = workspace_repository
self.workspace_member_repository = workspace_member_repository
def execute(self, request: RemoveMemberRequest) -> tuple[bool, Optional[str]]:
"""
执行移除成员
Args:
request: 移除请求
Returns:
(是否成功, 错误信息)
"""
@@ -45,18 +46,18 @@ class RemoveMemberUseCase:
# 1. 验证输入
if not request.workspace_id:
return False, "Workspace ID is required"
if not request.requester_user_id:
return False, "Requester user ID is required"
if not request.target_user_id:
return False, "Target user ID is required"
# 2. 验证 Workspace 存在
workspace = self.workspace_repository.find_by_id(request.workspace_id)
if not workspace:
return False, "Workspace not found"
# 3. 验证请求者是成员且有权限
requester_member = self.workspace_member_repository.find_by_workspace_and_user(
request.workspace_id,
@@ -64,10 +65,13 @@ class RemoveMemberUseCase:
)
if not requester_member:
return False, "You are not a member of this workspace"
if requester_member.role not in [WorkspaceMemberRole.OWNER, WorkspaceMemberRole.ADMIN]:
if requester_member.role not in [
WorkspaceMemberRole.OWNER,
WorkspaceMemberRole.ADMIN,
]:
return False, "Only owners and admins can remove members"
# 4. 验证目标成员存在
target_member = self.workspace_member_repository.find_by_workspace_and_user(
request.workspace_id,
@@ -75,34 +79,33 @@ class RemoveMemberUseCase:
)
if not target_member:
return False, "Target user is not a member of this workspace"
# 5. 不能移除自己(应该用离开 workspace 的功能)
if request.requester_user_id == request.target_user_id:
return False, "Cannot remove yourself. Use leave workspace instead."
# 6. 不能移除 owner
if target_member.role == WorkspaceMemberRole.OWNER:
return False, "Cannot remove the workspace owner"
# 7. Admin 不能移除另一个 Admin(只有 owner 可以)
if (requester_member.role == WorkspaceMemberRole.ADMIN and
target_member.role == WorkspaceMemberRole.ADMIN):
if requester_member.role == WorkspaceMemberRole.ADMIN and target_member.role == WorkspaceMemberRole.ADMIN:
return False, "Admins cannot remove other admins"
# 8. 删除成员记录
success = self.workspace_member_repository.delete(target_member.id)
if not success:
return False, "Failed to remove member"
return True, None
except Exception as e:
return False, f"Failed to remove member: {str(e)}"
class LeaveWorkspaceRequest:
"""离开 Workspace 请求"""
def __init__(self, workspace_id: str, user_id: str):
self.workspace_id = workspace_id
self.user_id = user_id
@@ -110,7 +113,7 @@ class LeaveWorkspaceRequest:
class LeaveWorkspaceUseCase:
"""离开 Workspace 用例"""
def __init__(
self,
workspace_repository,
@@ -118,14 +121,14 @@ class LeaveWorkspaceUseCase:
):
self.workspace_repository = workspace_repository
self.workspace_member_repository = workspace_member_repository
def execute(self, request: LeaveWorkspaceRequest) -> tuple[bool, Optional[str]]:
"""
执行离开 Workspace
Args:
request: 离开请求
Returns:
(是否成功, 错误信息)
"""
@@ -133,15 +136,15 @@ class LeaveWorkspaceUseCase:
# 1. 验证输入
if not request.workspace_id:
return False, "Workspace ID is required"
if not request.user_id:
return False, "User ID is required"
# 2. 验证 Workspace 存在
workspace = self.workspace_repository.find_by_id(request.workspace_id)
if not workspace:
return False, "Workspace not found"
# 3. 验证用户是成员
member = self.workspace_member_repository.find_by_workspace_and_user(
request.workspace_id,
@@ -149,17 +152,20 @@ class LeaveWorkspaceUseCase:
)
if not member:
return False, "You are not a member of this workspace"
# 4. Owner 不能离开(需要先转移 ownership 或删除 workspace
if member.role == WorkspaceMemberRole.OWNER:
return False, "Owner cannot leave workspace. Transfer ownership or delete workspace first."
return (
False,
"Owner cannot leave workspace. Transfer ownership or delete workspace first.",
)
# 5. 删除成员记录
success = self.workspace_member_repository.delete(member.id)
if not success:
return False, "Failed to leave workspace"
return True, None
except Exception as e:
return False, f"Failed to leave workspace: {str(e)}"
@@ -1,15 +1,16 @@
"""
Subscription 管理 Use Case
"""
from typing import Optional
from datetime import datetime, timedelta, timezone
from typing import Optional
from packages.domain.entities import WorkspaceMemberRole
class UpgradeSubscriptionRequest:
"""升级订阅请求"""
def __init__(
self,
workspace_id: str,
@@ -23,7 +24,7 @@ class UpgradeSubscriptionRequest:
class UpgradeSubscriptionResponse:
"""升级订阅响应"""
def __init__(
self,
workspace_id: str,
@@ -41,21 +42,21 @@ class UpgradeSubscriptionResponse:
class UpgradeSubscriptionUseCase:
"""升级订阅用例"""
# 订阅计划配额
PLAN_QUOTAS = {
"free": {"max_projects": 3, "max_storage_gb": 10, "price": 0},
"pro": {"max_projects": 999999, "max_storage_gb": 100, "price": 99},
"enterprise": {"max_projects": 999999, "max_storage_gb": 1000, "price": 999},
}
# 计划等级
PLAN_LEVELS = {
"free": 0,
"pro": 1,
"enterprise": 2,
}
def __init__(
self,
workspace_repository,
@@ -63,14 +64,16 @@ class UpgradeSubscriptionUseCase:
):
self.workspace_repository = workspace_repository
self.workspace_member_repository = workspace_member_repository
def execute(self, request: UpgradeSubscriptionRequest) -> tuple[Optional[UpgradeSubscriptionResponse], Optional[str]]:
def execute(
self, request: UpgradeSubscriptionRequest
) -> tuple[Optional[UpgradeSubscriptionResponse], Optional[str]]:
"""
执行升级订阅
Args:
request: 升级请求
Returns:
(响应, 错误信息)
"""
@@ -78,22 +81,22 @@ class UpgradeSubscriptionUseCase:
# 1. 验证输入
if not request.workspace_id:
return None, "Workspace ID is required"
if not request.requester_user_id:
return None, "Requester user ID is required"
if not request.new_plan:
return None, "New plan is required"
# 2. 验证新计划有效
if request.new_plan not in self.PLAN_QUOTAS:
return None, f"Invalid plan: {request.new_plan}"
# 3. 验证工作空间存在
workspace = self.workspace_repository.find_by_id(request.workspace_id)
if not workspace:
return None, "Workspace not found"
# 4. 验证权限(只有 Owner 可以管理订阅)
member = self.workspace_member_repository.find_by_workspace_and_user(
request.workspace_id,
@@ -101,50 +104,56 @@ class UpgradeSubscriptionUseCase:
)
if not member:
return None, "You are not a member of this workspace"
if member.role != WorkspaceMemberRole.OWNER:
return None, "Only workspace owner can manage subscription"
# 5. 检查是否是升级(不能降级到免费计划,需要用取消订阅)
current_level = self.PLAN_LEVELS.get(workspace.subscription_plan, 0)
new_level = self.PLAN_LEVELS.get(request.new_plan, 0)
if new_level < current_level:
return None, "Cannot downgrade plan. Use cancel subscription to return to free plan."
return (
None,
"Cannot downgrade plan. Use cancel subscription to return to free plan.",
)
if new_level == current_level:
return None, f"Workspace is already on {request.new_plan} plan"
# 6. 更新订阅
old_plan = workspace.subscription_plan
quota = self.PLAN_QUOTAS[request.new_plan]
workspace.subscription_plan = request.new_plan
workspace.subscription_status = "active"
workspace.max_projects = quota["max_projects"]
workspace.max_storage_gb = quota["max_storage_gb"]
# 设置过期时间(假设按月订阅)
workspace.subscription_expires_at = datetime.now(timezone.utc) + timedelta(days=30)
self.workspace_repository.save(workspace)
# 7. 返回响应
return UpgradeSubscriptionResponse(
workspace_id=workspace.id,
old_plan=old_plan,
new_plan=workspace.subscription_plan,
max_projects=workspace.max_projects,
max_storage_gb=workspace.max_storage_gb,
), None
return (
UpgradeSubscriptionResponse(
workspace_id=workspace.id,
old_plan=old_plan,
new_plan=workspace.subscription_plan,
max_projects=workspace.max_projects,
max_storage_gb=workspace.max_storage_gb,
),
None,
)
except Exception as e:
return None, f"Failed to upgrade subscription: {str(e)}"
class CancelSubscriptionRequest:
"""取消订阅请求"""
def __init__(self, workspace_id: str, requester_user_id: str):
self.workspace_id = workspace_id
self.requester_user_id = requester_user_id
@@ -152,7 +161,7 @@ class CancelSubscriptionRequest:
class CancelSubscriptionUseCase:
"""取消订阅用例"""
def __init__(
self,
workspace_repository,
@@ -160,14 +169,14 @@ class CancelSubscriptionUseCase:
):
self.workspace_repository = workspace_repository
self.workspace_member_repository = workspace_member_repository
def execute(self, request: CancelSubscriptionRequest) -> tuple[bool, Optional[str]]:
"""
执行取消订阅
Args:
request: 取消请求
Returns:
(是否成功, 错误信息)
"""
@@ -175,15 +184,15 @@ class CancelSubscriptionUseCase:
# 1. 验证输入
if not request.workspace_id:
return False, "Workspace ID is required"
if not request.requester_user_id:
return False, "Requester user ID is required"
# 2. 验证工作空间存在
workspace = self.workspace_repository.find_by_id(request.workspace_id)
if not workspace:
return False, "Workspace not found"
# 3. 验证权限(只有 Owner 可以管理订阅)
member = self.workspace_member_repository.find_by_workspace_and_user(
request.workspace_id,
@@ -191,24 +200,24 @@ class CancelSubscriptionUseCase:
)
if not member:
return False, "You are not a member of this workspace"
if member.role != WorkspaceMemberRole.OWNER:
return False, "Only workspace owner can manage subscription"
# 4. 检查当前计划
if workspace.subscription_plan == "free":
return False, "Workspace is already on free plan"
# 5. 降级到 free 计划
workspace.subscription_plan = "free"
workspace.subscription_status = "active"
workspace.subscription_expires_at = None
workspace.max_projects = 3
workspace.max_storage_gb = 10
self.workspace_repository.save(workspace)
return True, None
except Exception as e:
return False, f"Failed to cancel subscription: {str(e)}"
@@ -1,6 +1,7 @@
"""
修改成员角色 Use Case
"""
from typing import Optional
from packages.domain.entities import WorkspaceMemberRole
@@ -8,7 +9,7 @@ from packages.domain.entities import WorkspaceMemberRole
class UpdateMemberRoleRequest:
"""修改成员角色请求"""
def __init__(
self,
workspace_id: str,
@@ -24,7 +25,7 @@ class UpdateMemberRoleRequest:
class UpdateMemberRoleResponse:
"""修改成员角色响应"""
def __init__(self, user_id: str, old_role: str, new_role: str):
self.user_id = user_id
self.old_role = old_role
@@ -33,13 +34,13 @@ class UpdateMemberRoleResponse:
class UpdateMemberRoleUseCase:
"""修改成员角色用例"""
VALID_ROLES = [
WorkspaceMemberRole.ADMIN,
WorkspaceMemberRole.MEMBER,
WorkspaceMemberRole.VIEWER,
]
def __init__(
self,
workspace_repository,
@@ -47,14 +48,14 @@ class UpdateMemberRoleUseCase:
):
self.workspace_repository = workspace_repository
self.workspace_member_repository = workspace_member_repository
def execute(self, request: UpdateMemberRoleRequest) -> tuple[Optional[UpdateMemberRoleResponse], Optional[str]]:
"""
执行修改成员角色
Args:
request: 修改请求
Returns:
(响应, 错误信息)
"""
@@ -62,25 +63,28 @@ class UpdateMemberRoleUseCase:
# 1. 验证输入
if not request.workspace_id:
return None, "Workspace ID is required"
if not request.requester_user_id:
return None, "Requester user ID is required"
if not request.target_user_id:
return None, "Target user ID is required"
if not request.new_role:
return None, "New role is required"
# 2. 验证新角色(不能修改为 owner)
if request.new_role not in self.VALID_ROLES:
return None, f"Invalid role: {request.new_role}. Cannot change to owner."
return (
None,
f"Invalid role: {request.new_role}. Cannot change to owner.",
)
# 3. 验证 Workspace 存在
workspace = self.workspace_repository.find_by_id(request.workspace_id)
if not workspace:
return None, "Workspace not found"
# 4. 验证请求者是成员且有权限(只有 owner 和 admin 可以修改角色)
requester_member = self.workspace_member_repository.find_by_workspace_and_user(
request.workspace_id,
@@ -88,10 +92,13 @@ class UpdateMemberRoleUseCase:
)
if not requester_member:
return None, "You are not a member of this workspace"
if requester_member.role not in [WorkspaceMemberRole.OWNER, WorkspaceMemberRole.ADMIN]:
if requester_member.role not in [
WorkspaceMemberRole.OWNER,
WorkspaceMemberRole.ADMIN,
]:
return None, "Only owners and admins can change member roles"
# 5. 验证目标成员存在
target_member = self.workspace_member_repository.find_by_workspace_and_user(
request.workspace_id,
@@ -99,35 +106,37 @@ class UpdateMemberRoleUseCase:
)
if not target_member:
return None, "Target user is not a member of this workspace"
# 6. 不能修改自己的角色
if request.requester_user_id == request.target_user_id:
return None, "Cannot change your own role"
# 7. 不能修改 owner 的角色
if target_member.role == WorkspaceMemberRole.OWNER:
return None, "Cannot change the owner's role"
# 8. Admin 不能修改另一个 Admin 的角色(只有 owner 可以)
if (requester_member.role == WorkspaceMemberRole.ADMIN and
target_member.role == WorkspaceMemberRole.ADMIN):
if requester_member.role == WorkspaceMemberRole.ADMIN and target_member.role == WorkspaceMemberRole.ADMIN:
return None, "Admins cannot change other admins' roles"
# 9. 检查角色是否相同
if target_member.role == request.new_role:
return None, f"User already has the {request.new_role} role"
# 10. 更新角色
old_role = target_member.role
target_member.role = request.new_role
self.workspace_member_repository.save(target_member)
# 11. 返回响应
return UpdateMemberRoleResponse(
user_id=request.target_user_id,
old_role=old_role,
new_role=request.new_role,
), None
return (
UpdateMemberRoleResponse(
user_id=request.target_user_id,
old_role=old_role,
new_role=request.new_role,
),
None,
)
except Exception as e:
return None, f"Failed to update member role: {str(e)}"