Files
ai-contract-platform/backend/app/services/user_service.py
T
2026-04-24 13:44:17 +08:00

112 lines
4.3 KiB
Python

from typing import Any, Dict, Optional
from uuid import uuid4
from fastapi import HTTPException, status
from sqlalchemy.orm import Session
from app.core.security import get_password_hash
from app.models.user import User
from app.repositories.user_repository import UserRepository
class UserService:
def __init__(self, db: Session):
self.db = db
self.repository = UserRepository(db)
def list_users(self, current_user: Dict[str, Any], page: int = 1, page_size: int = 20) -> dict:
self._require_admin(current_user)
tenant_id = current_user["tenant_id"]
normalized_page = max(page, 1)
normalized_page_size = max(min(page_size, 100), 1)
users = self.repository.list_paginated(tenant_id, normalized_page, normalized_page_size)
return {
"items": [self._to_item(item) for item in users],
"page": normalized_page,
"page_size": normalized_page_size,
"total": self.repository.count(tenant_id),
}
def create_user(
self,
current_user: Dict[str, Any],
username: str,
display_name: str,
password: str,
role: str,
status_value: str,
) -> dict:
self._require_admin(current_user)
normalized_username = username.strip().lower()
if self.repository.get_by_username(normalized_username) is not None:
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="username already exists")
user = User(
id=f"user-{uuid4().hex[:12]}",
tenant_id=current_user["tenant_id"],
username=normalized_username,
display_name=display_name.strip(),
password_hash=get_password_hash(password),
role=self._normalize_role(role),
status=self._normalize_status(status_value),
)
created = self.repository.create(user)
return self._to_item(created)
def update_user(
self,
current_user: Dict[str, Any],
user_id: str,
username: str,
display_name: str,
password: Optional[str],
role: str,
status_value: str,
) -> dict:
self._require_admin(current_user)
user = self._require_user(current_user, user_id)
normalized_username = username.strip().lower()
existing = self.repository.get_by_username(normalized_username)
if existing is not None and existing.id != user.id:
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="username already exists")
user.username = normalized_username
user.display_name = display_name.strip()
user.role = self._normalize_role(role)
user.status = self._normalize_status(status_value)
if password and password.strip():
user.password_hash = get_password_hash(password.strip())
updated = self.repository.update(user)
return self._to_item(updated)
def _require_user(self, current_user: Dict[str, Any], user_id: str) -> User:
user = self.repository.get_by_id(user_id)
if user is None or user.tenant_id != current_user["tenant_id"]:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="user not found")
return user
def _require_admin(self, current_user: Dict[str, Any]):
if current_user.get("role") != "admin":
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="forbidden")
def _normalize_role(self, role: str) -> str:
normalized_role = role.strip().lower()
if normalized_role not in {"admin", "member"}:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="invalid role")
return normalized_role
def _normalize_status(self, status_value: str) -> str:
normalized_status = status_value.strip().lower()
if normalized_status not in {"active", "disabled"}:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="invalid status")
return normalized_status
def _to_item(self, user: User) -> dict:
return {
"id": user.id,
"tenant_id": user.tenant_id,
"username": user.username,
"display_name": user.display_name,
"role": user.role,
"status": user.status,
"created_at": user.created_at.isoformat(),
}