272 lines
11 KiB
Python
272 lines
11 KiB
Python
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)
|