Files
MemoryBear/api/app/services/tenant_service.py
2026-03-11 10:45:07 +08:00

272 lines
11 KiB
Python
Raw 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.
from sqlalchemy.orm import Session
from typing import List, Optional
import uuid
from app.core.logging_config import get_business_logger
from app.repositories.tenant_repository import TenantRepository
from app.repositories.user_repository import UserRepository
from app.repositories.workspace_repository import WorkspaceRepository
from app.schemas.tenant_schema import (
TenantCreate, TenantUpdate, Tenant, TenantQuery, TenantList
)
from app.schemas.user_schema import User
from app.schemas.workspace_schema import WorkspaceCreate
from app.models.tenant_model import Tenants
from app.models.user_model import User as UserModel
from app.core.exceptions import BusinessException
from app.core.error_codes import BizCode
# 获取业务逻辑专用日志器
business_logger = get_business_logger()
class TenantService:
"""租户业务逻辑层"""
def __init__(self, db: Session):
self.db = db
self.tenant_repo = TenantRepository(db)
self.user_repo = UserRepository(db)
self.workspace_repo = WorkspaceRepository(db)
def create_tenant(self, tenant_data: TenantCreate) -> Tenants:
"""创建租户"""
# 检查租户名称是否已存在
existing_tenant = self.tenant_repo.get_tenant_by_name(tenant_data.name)
if existing_tenant:
raise BusinessException(f"租户名称 '{tenant_data.name}' 已存在", code=BizCode.DUPLICATE_NAME)
try:
tenant = self.tenant_repo.create_tenant(tenant_data)
business_logger.info(f"创建租户成功: {tenant.name} (ID: {tenant.id})")
return tenant
except Exception as e:
business_logger.error(f"创建租户失败: {str(e)}")
raise BusinessException(f"创建租户失败: {str(e)}", code=BizCode.DB_ERROR)
def create_tenant_and_assign_user(self, tenant_data: TenantCreate, user_id: uuid.UUID) -> Tenants:
"""创建租户并分配用户"""
try:
# 创建租户
tenant = self.create_tenant(tenant_data)
# 将用户分配给租户
success = self.user_repo.assign_user_to_tenant(user_id, tenant.id)
if not success:
raise BusinessException("分配用户到租户失败", code=BizCode.STATE_CONFLICT)
business_logger.info(f"创建租户并分配用户成功: {tenant.name}")
return tenant
except Exception as e:
business_logger.error(f"创建租户和分配用户失败: {str(e)}")
self.db.rollback()
raise BusinessException(f"创建租户失败: {str(e)}", code=BizCode.DB_ERROR)
def get_tenant(self, tenant_id: uuid.UUID) -> Optional[Tenants]:
"""获取租户"""
return self.tenant_repo.get_tenant_by_id(tenant_id)
def get_tenant_by_name(self, name: str) -> Optional[Tenants]:
"""根据名称获取租户"""
return self.tenant_repo.get_tenant_by_name(name)
def get_tenants(self, query: TenantQuery) -> TenantList:
"""获取租户列表"""
skip = (query.page - 1) * query.size
tenants = self.tenant_repo.get_tenants(
skip=skip,
limit=query.size,
is_active=query.is_active,
search=query.search
)
total = self.tenant_repo.count_tenants(
is_active=query.is_active,
search=query.search
)
pages = (total + query.size - 1) // query.size
return TenantList(
items=[Tenant.model_validate(tenant) for tenant in tenants],
total=total,
page=query.page,
size=query.size,
pages=pages
)
def update_tenant(self, tenant_id: uuid.UUID, tenant_data: TenantUpdate) -> Optional[Tenants]:
"""更新租户"""
# 如果更新名称,检查是否重复
if tenant_data.name:
existing_tenant = self.tenant_repo.get_tenant_by_name(tenant_data.name)
if existing_tenant and existing_tenant.id != tenant_id:
raise BusinessException(f"租户名称 '{tenant_data.name}' 已存在", code=BizCode.DUPLICATE_NAME)
try:
tenant = self.tenant_repo.update_tenant(tenant_id, tenant_data)
if tenant:
business_logger.info(f"更新租户成功: {tenant.name} (ID: {tenant.id})")
return tenant
except Exception as e:
business_logger.error(f"更新租户失败: {str(e)}")
raise BusinessException(f"更新租户失败: {str(e)}", code=BizCode.DB_ERROR)
def delete_tenant(self, tenant_id: uuid.UUID) -> bool:
"""删除租户"""
try:
# 检查租户是否存在
tenant = self.tenant_repo.get_tenant_by_id(tenant_id)
if not tenant:
return False
# 检查是否有关联的用户
users = self.tenant_repo.get_tenant_users(tenant_id)
if users:
raise BusinessException("无法删除租户,存在关联的用户", code=BizCode.STATE_CONFLICT)
# 检查是否有关联的工作空间
workspaces = self.workspace_repo.get_workspaces_by_tenant(tenant_id)
if workspaces:
raise BusinessException("无法删除租户,存在关联的工作空间", code=BizCode.STATE_CONFLICT)
success = self.tenant_repo.delete_tenant(tenant_id)
if success:
business_logger.info(f"删除租户成功: {tenant.name} (ID: {tenant.id})")
return success
except Exception as e:
business_logger.error(f"删除租户失败: {str(e)}")
raise BusinessException(f"删除租户失败: {str(e)}", code=BizCode.DB_ERROR)
# 租户用户管理
def get_tenant_users(
self,
tenant_id: uuid.UUID,
skip: int = 0,
limit: int = 100,
is_active: Optional[bool] = None,
search: Optional[str] = None
) -> List[UserModel]:
"""获取租户下的用户列表"""
return self.user_repo.get_users_by_tenant(
tenant_id=tenant_id,
skip=skip,
limit=limit,
is_active=is_active,
search=search
)
def count_tenant_users(
self,
tenant_id: uuid.UUID,
is_active: Optional[bool] = None,
search: Optional[str] = None
) -> int:
"""统计租户下的用户数量"""
return self.user_repo.count_users_by_tenant(
tenant_id=tenant_id,
is_active=is_active,
search=search
)
def assign_user_to_tenant(self, user_id: uuid.UUID, tenant_id: uuid.UUID) -> bool:
"""将用户分配给租户"""
# 检查租户是否存在
tenant = self.tenant_repo.get_tenant_by_id(tenant_id)
if not tenant:
raise BusinessException("租户不存在", code=BizCode.TENANT_NOT_FOUND)
try:
success = self.user_repo.assign_user_to_tenant(user_id, tenant_id)
if success:
business_logger.info(f"分配用户到租户成功: 用户ID {user_id}, 租户ID {tenant_id}")
return success
except Exception as e:
business_logger.error(f"分配用户到租户失败: {str(e)}")
raise BusinessException(f"分配用户到租户失败: {str(e)}", code=BizCode.DB_ERROR)
def get_user_tenant(self, user_id: uuid.UUID) -> Optional[Tenants]:
"""获取用户所属的租户"""
return self.tenant_repo.get_user_tenant(user_id)
def remove_user_from_tenant(self, user_id: uuid.UUID) -> bool:
"""将用户从租户中移除设置tenant_id为None"""
try:
user = self.user_repo.get_user_by_id(user_id)
if not user:
return False
success = self.user_repo.assign_user_to_tenant(user_id, None)
if success:
business_logger.info(f"移除用户租户关联成功: 用户ID {user_id}")
return success
except Exception as e:
business_logger.error(f"移除用户租户关联失败: {str(e)}")
raise BusinessException(f"移除用户租户关联失败: {str(e)}", code=BizCode.DB_ERROR)
def get_users_without_tenant(
self,
skip: int = 0,
limit: int = 100,
is_active: Optional[bool] = None
) -> List[UserModel]:
"""获取没有租户的用户列表"""
return self.user_repo.get_users_without_tenant(
skip=skip,
limit=limit,
is_active=is_active
)
def get_tenant_language_config(self, tenant_id: uuid.UUID) -> Optional[dict]:
"""获取租户语言配置"""
tenant = self.tenant_repo.get_tenant_by_id(tenant_id)
if not tenant:
raise BusinessException("租户不存在", code=BizCode.TENANT_NOT_FOUND)
return {
"default_language": tenant.default_language,
"supported_languages": tenant.supported_languages
}
def update_tenant_language_config(
self,
tenant_id: uuid.UUID,
default_language: str,
supported_languages: list
) -> Optional[dict]:
"""更新租户语言配置"""
# 检查租户是否存在
tenant = self.tenant_repo.get_tenant_by_id(tenant_id)
if not tenant:
raise BusinessException("租户不存在", code=BizCode.TENANT_NOT_FOUND)
# 验证默认语言在支持的语言列表中
if default_language not in supported_languages:
raise BusinessException(
"默认语言必须在支持的语言列表中",
code=BizCode.VALIDATION_FAILED
)
try:
# 更新语言配置
tenant.default_language = default_language
tenant.supported_languages = supported_languages
self.db.commit()
self.db.refresh(tenant)
business_logger.info(
f"更新租户语言配置成功: {tenant.name} (ID: {tenant.id}), "
f"默认语言: {default_language}, 支持语言: {supported_languages}"
)
return {
"default_language": tenant.default_language,
"supported_languages": tenant.supported_languages
}
except Exception as e:
self.db.rollback()
business_logger.error(f"更新租户语言配置失败: {str(e)}")
raise BusinessException(f"更新租户语言配置失败: {str(e)}", code=BizCode.DB_ERROR)