修复(权限管理):统一 PM 系统导航与权限校验

This commit is contained in:
Cheng Zhou
2026-07-10 22:12:59 +08:00
parent f11a5c84d9
commit f2fa10c178
47 changed files with 4071 additions and 1555 deletions
+32 -4
View File
@@ -1,7 +1,8 @@
from __future__ import annotations
import uuid
from typing import Sequence
from datetime import datetime, timedelta, timezone
from typing import Literal, Sequence
from sqlalchemy import delete, func, or_, select, update
from sqlalchemy.ext.asyncio import AsyncSession
@@ -11,12 +12,14 @@ from app.core.config import (
PROTECTED_ADMIN_DEFAULT_PASSWORD,
PROTECTED_ADMIN_EMAIL,
PROTECTED_ADMIN_FULL_NAME,
settings,
)
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.models.user_login_session import UserLoginSession
from app.schemas.user import UserCreate, UserRegisterRequest, UserUpdate
@@ -81,7 +84,13 @@ async def update_user(db: AsyncSession, user: User, user_in: UserUpdate) -> User
return user
def _apply_user_filters(query, *, keyword: str | None = None, status: UserStatus | None = None):
def _apply_user_filters(
query,
*,
keyword: str | None = None,
status: UserStatus | None = None,
login_status: Literal["ONLINE", "OFFLINE"] | None = None,
):
if keyword:
pattern = f"%{keyword.strip()}%"
query = query.where(
@@ -93,6 +102,18 @@ def _apply_user_filters(query, *, keyword: str | None = None, status: UserStatus
)
if status is not None:
query = query.where(User.status == status)
if login_status is not None:
cutoff = datetime.now(timezone.utc) - timedelta(seconds=settings.USER_SESSION_ONLINE_SECONDS)
has_online_session = (
select(UserLoginSession.id)
.where(
UserLoginSession.user_id == User.id,
UserLoginSession.ended_at.is_(None),
UserLoginSession.last_seen_at >= cutoff,
)
.exists()
)
query = query.where(has_online_session if login_status == "ONLINE" else ~has_online_session)
return query
@@ -103,8 +124,9 @@ async def list_users(
*,
keyword: str | None = None,
status: UserStatus | None = None,
login_status: Literal["ONLINE", "OFFLINE"] | None = None,
) -> Sequence[User]:
query = _apply_user_filters(select(User), keyword=keyword, status=status)
query = _apply_user_filters(select(User), keyword=keyword, status=status, login_status=login_status)
result = await db.execute(query.order_by(User.created_at.desc()).offset(skip).limit(limit))
return result.scalars().all()
@@ -114,8 +136,14 @@ async def count_users(
*,
keyword: str | None = None,
status: UserStatus | None = None,
login_status: Literal["ONLINE", "OFFLINE"] | None = None,
) -> int:
query = _apply_user_filters(select(func.count()).select_from(User), keyword=keyword, status=status)
query = _apply_user_filters(
select(func.count()).select_from(User),
keyword=keyword,
status=status,
login_status=login_status,
)
result = await db.execute(query)
return int(result.scalar_one() or 0)