"""用户摘要、组织解析与列表查询。""" from __future__ import annotations from sqlalchemy import func, or_, select from dms.common.enums import AllowedModule, EnabledStatus, RoleCode from dms.common.errors import InvalidArgumentError, ResourceNotFoundError from dms.common.pagination import PageRequest, page_result from dms.common.response import serialize_id from dms.extensions import db from dms.models import Organization, User ALLOWED_MODULES = { RoleCode.USER: [AllowedModule.DOCUMENT_BROWSER.value], RoleCode.ADMIN: [ AllowedModule.DOCUMENT_BROWSER.value, AllowedModule.BACKEND_MANAGEMENT.value, ], RoleCode.AUDITOR: [AllowedModule.AUDIT_LOG.value], } def effective_organization(user: User) -> tuple[int | None, str | None]: if user.organization_id is not None: organization = db.session.scalar( select(Organization).where( Organization.id == user.organization_id, Organization.is_deleted.is_(False), Organization.status == EnabledStatus.ENABLED.value, ) ) if organization is not None: return organization.id, organization.org_name return user.organization_id, user.organization_name def user_summary(user: User) -> dict[str, object]: organization_id, organization_name = effective_organization(user) return { "id": serialize_id(user.id), "username": user.username, "realName": user.real_name, "organizationId": serialize_id(organization_id), "organizationName": organization_name, "roleCode": user.role_code, "securityLevel": user.security_level, "status": user.status, "allowedModules": ALLOWED_MODULES[RoleCode(user.role_code)], } def parse_string_id(raw_value: str | None, *, name: str) -> int | None: if raw_value is None or raw_value == "": return None if not raw_value.isdecimal() or int(raw_value) <= 0: raise InvalidArgumentError(f"{name}必须是正整数形式的字符串ID") return int(raw_value) def _descendant_ids(root_id: int) -> set[int]: organizations = db.session.scalars( select(Organization).where(Organization.is_deleted.is_(False)) ).all() if not any(item.id == root_id for item in organizations): raise ResourceNotFoundError("组织不存在") children: dict[int | None, list[int]] = {} for item in organizations: if item.status == EnabledStatus.ENABLED.value: children.setdefault(item.parent_id, []).append(item.id) found = {root_id} pending = [root_id] while pending: current = pending.pop() for child_id in children.get(current, []): if child_id not in found: found.add(child_id) pending.append(child_id) return found def list_users( *, organization_id: int | None, include_descendants: bool, keyword: str | None, status: str | None, page_request: PageRequest, ) -> dict[str, object]: filters = [User.is_deleted.is_(False)] if organization_id is not None: organization_ids = ( _descendant_ids(organization_id) if include_descendants else {organization_id} ) filters.append(User.organization_id.in_(organization_ids)) if keyword: pattern = f"%{keyword.strip()}%" filters.append(or_(User.username.like(pattern), User.real_name.like(pattern))) if status: try: normalized_status = EnabledStatus(status).value except ValueError as exc: raise InvalidArgumentError("status必须是ENABLED或DISABLED") from exc filters.append(User.status == normalized_status) total = db.session.scalar(select(func.count(User.id)).where(*filters)) or 0 users = db.session.scalars( select(User) .where(*filters) .order_by(User.real_name.asc(), User.id.asc()) .offset(page_request.offset) .limit(page_request.page_size) ).all() return page_result( [user_summary(user) for user in users], page=page_request.page, page_size=page_request.page_size, total=total, )