调整账号注册与删除流程
This commit is contained in:
@@ -1,62 +0,0 @@
|
||||
import uuid
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.deps import get_db_session, is_system_admin, require_roles
|
||||
from app.crud import user as user_crud
|
||||
from app.models.user import User, UserStatus
|
||||
from app.schemas.user import AdminUserListResponse, UserAdminReviewRequest, UserResponse
|
||||
|
||||
router = APIRouter(prefix="/users")
|
||||
|
||||
|
||||
@router.get("/", response_model=AdminUserListResponse)
|
||||
async def list_users_for_review(
|
||||
status_filter: UserStatus = Query(default=UserStatus.PENDING, alias="status"),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
current_user=Depends(require_roles(["ADMIN"])),
|
||||
) -> AdminUserListResponse:
|
||||
users = await user_crud.list_users_by_status(db, status=status_filter)
|
||||
return AdminUserListResponse(items=list(users), total=len(users))
|
||||
|
||||
|
||||
async def _get_review_user(db: AsyncSession, user_id: uuid.UUID) -> User:
|
||||
user = await user_crud.get_by_id(db, user_id)
|
||||
if not user:
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="用户不存在")
|
||||
if is_system_admin(user):
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="管理员账号不允许审核")
|
||||
return user
|
||||
|
||||
|
||||
@router.post("/{user_id}/approve", response_model=UserResponse)
|
||||
async def approve_user(
|
||||
user_id: uuid.UUID,
|
||||
review: UserAdminReviewRequest,
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
current_user=Depends(require_roles(["ADMIN"])),
|
||||
) -> UserResponse:
|
||||
if review.action != "approve":
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="非法操作")
|
||||
user = await _get_review_user(db, user_id)
|
||||
if user.status != UserStatus.PENDING:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="仅允许审核待审核用户")
|
||||
user = await user_crud.approve_user(db, user, admin_id=current_user.id)
|
||||
return user
|
||||
|
||||
|
||||
@router.post("/{user_id}/reject", response_model=UserResponse)
|
||||
async def reject_user(
|
||||
user_id: uuid.UUID,
|
||||
review: UserAdminReviewRequest,
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
current_user=Depends(require_roles(["ADMIN"])),
|
||||
) -> UserResponse:
|
||||
if review.action not in ("reject", "approve"):
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="非法操作")
|
||||
user = await _get_review_user(db, user_id)
|
||||
if user.status != UserStatus.PENDING:
|
||||
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="仅允许审核待审核用户")
|
||||
user = await user_crud.reject_user(db, user, admin_id=current_user.id)
|
||||
return user
|
||||
@@ -116,8 +116,8 @@ async def register(
|
||||
existing = await user_crud.get_by_email(db, payload.email)
|
||||
if existing:
|
||||
raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail="邮箱已注册")
|
||||
await user_crud.create_pending_user(db, payload)
|
||||
return {"message": "注册成功,等待管理员审核"}
|
||||
await user_crud.create_registered_user(db, payload)
|
||||
return {"message": "注册成功,请登录"}
|
||||
|
||||
|
||||
@router.get("/login-key", response_model=LoginKeyResponse)
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
from fastapi import APIRouter
|
||||
|
||||
from app.api.v1 import auth, users, admin_users, studies, sites, members, attachments, audit_logs, dashboard, subjects, visits, aes, finance_dashboard, fees_contracts, drug_shipments, material_equipments, project_milestones, startup, precautions, subject_histories, subject_pds, study_subject_pds, faq_categories, faqs, documents, etmf, overview, notifications, monitoring_visit_issues, api_permissions, permission_monitoring, permission_templates, system_permissions, study_active_roles
|
||||
from app.api.v1 import auth, users, studies, sites, members, attachments, audit_logs, dashboard, subjects, visits, aes, finance_dashboard, fees_contracts, drug_shipments, material_equipments, project_milestones, startup, precautions, subject_histories, subject_pds, study_subject_pds, faq_categories, faqs, documents, etmf, overview, notifications, monitoring_visit_issues, api_permissions, permission_monitoring, permission_templates, system_permissions, study_active_roles
|
||||
|
||||
|
||||
api_router = APIRouter()
|
||||
api_router.include_router(auth.router, prefix="/auth", tags=["auth"])
|
||||
api_router.include_router(admin_users.router, prefix="/admin", tags=["admin"])
|
||||
api_router.include_router(users.router, prefix="/users", tags=["users"])
|
||||
api_router.include_router(studies.router, prefix="/studies", tags=["studies"])
|
||||
api_router.include_router(overview.router, prefix="/studies/{study_id}", tags=["overview"])
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import uuid
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.core.deps import get_db_session, is_system_admin, require_roles
|
||||
@@ -97,4 +98,16 @@ async def delete_user(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="该账号仍在项目成员中,请先在项目成员配置中移除后再删除",
|
||||
)
|
||||
await user_crud.delete_user(db, db_user)
|
||||
if await user_crud.user_has_retained_history(db, db_user.id):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="该账号已有审计或权限访问记录,请停用账号以保留历史追溯",
|
||||
)
|
||||
try:
|
||||
await user_crud.delete_user(db, db_user)
|
||||
except IntegrityError as exc:
|
||||
await db.rollback()
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail="该账号已有业务记录,请停用账号以保留历史追溯",
|
||||
) from exc
|
||||
|
||||
+17
-24
@@ -13,6 +13,8 @@ from app.core.config import (
|
||||
PROTECTED_ADMIN_FULL_NAME,
|
||||
)
|
||||
from app.core.security import hash_password
|
||||
from app.models.audit_log import AuditLog
|
||||
from app.models.permission_access_log import PermissionAccessLog
|
||||
from app.models.study_member import StudyMember
|
||||
from app.models.user import User, UserStatus
|
||||
from app.schemas.user import UserCreate, UserRegisterRequest, UserUpdate
|
||||
@@ -53,8 +55,8 @@ async def create_user(
|
||||
return user
|
||||
|
||||
|
||||
async def create_pending_user(db: AsyncSession, user_in: UserRegisterRequest) -> User:
|
||||
return await create_user(db, UserCreate(**user_in.model_dump()), status=UserStatus.PENDING)
|
||||
async def create_registered_user(db: AsyncSession, user_in: UserRegisterRequest) -> User:
|
||||
return await create_user(db, UserCreate(**user_in.model_dump()), status=UserStatus.ACTIVE)
|
||||
|
||||
|
||||
async def update_user(db: AsyncSession, user: User, user_in: UserUpdate) -> User:
|
||||
@@ -150,6 +152,19 @@ async def count_active_admins(db: AsyncSession) -> int:
|
||||
return int(result.scalar_one() or 0)
|
||||
|
||||
|
||||
async def user_has_retained_history(db: AsyncSession, user_id: uuid.UUID) -> bool:
|
||||
audit_count = await db.scalar(
|
||||
select(func.count()).select_from(AuditLog).where(AuditLog.operator_id == user_id)
|
||||
)
|
||||
if int(audit_count or 0) > 0:
|
||||
return True
|
||||
|
||||
permission_log_count = await db.scalar(
|
||||
select(func.count()).select_from(PermissionAccessLog).where(PermissionAccessLog.user_id == user_id)
|
||||
)
|
||||
return int(permission_log_count or 0) > 0
|
||||
|
||||
|
||||
async def list_users_by_status(
|
||||
db: AsyncSession, status: UserStatus | None = None, skip: int = 0, limit: int = 100
|
||||
) -> Sequence[User]:
|
||||
@@ -189,25 +204,3 @@ async def delete_user(db: AsyncSession, user: User) -> None:
|
||||
await db.execute(delete(StudyMember).where(StudyMember.user_id == user.id))
|
||||
await db.delete(user)
|
||||
await db.commit()
|
||||
|
||||
|
||||
async def approve_user(db: AsyncSession, user: User, admin_id: uuid.UUID) -> User:
|
||||
await db.execute(
|
||||
update(User)
|
||||
.where(User.id == user.id)
|
||||
.values(status=UserStatus.ACTIVE, approved_by=admin_id, approved_at=func.now())
|
||||
)
|
||||
await db.commit()
|
||||
await db.refresh(user)
|
||||
return user
|
||||
|
||||
|
||||
async def reject_user(db: AsyncSession, user: User, admin_id: uuid.UUID) -> User:
|
||||
await db.execute(
|
||||
update(User)
|
||||
.where(User.id == user.id)
|
||||
.values(status=UserStatus.REJECTED, approved_by=admin_id, approved_at=func.now())
|
||||
)
|
||||
await db.commit()
|
||||
await db.refresh(user)
|
||||
return user
|
||||
|
||||
@@ -78,15 +78,6 @@ class UserResponse(UserRead):
|
||||
pass
|
||||
|
||||
|
||||
class UserAdminReviewRequest(BaseModel):
|
||||
action: Literal["approve", "reject"]
|
||||
|
||||
|
||||
class AdminUserListResponse(BaseModel):
|
||||
items: list[UserResponse]
|
||||
total: int
|
||||
|
||||
|
||||
class UserSelfUpdate(_PasswordValidator):
|
||||
full_name: Optional[str] = None
|
||||
clinical_department: Optional[str] = None
|
||||
|
||||
Reference in New Issue
Block a user