更新查询方式
This commit is contained in:
@@ -1,9 +1,72 @@
|
|||||||
from typing import Annotated
|
"""
|
||||||
|
FastAPI 依赖注入
|
||||||
|
|
||||||
from fastapi import Depends
|
包含 HTTP 端点的通用依赖:
|
||||||
|
- SessionDep: 数据库会话依赖
|
||||||
|
- TimeFilterRequestDep: 时间筛选查询依赖(用于 count 等统计接口)
|
||||||
|
- TableViewRequestDep: 分页排序查询依赖(包含时间筛选 + 分页排序)
|
||||||
|
"""
|
||||||
|
from datetime import datetime
|
||||||
|
from typing import Annotated, Literal, TypeAlias
|
||||||
|
|
||||||
|
from fastapi import Depends, Query
|
||||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||||
|
|
||||||
from models.database import get_session
|
from models.database import get_session
|
||||||
|
from models.mixin import TimeFilterRequest, TableViewRequest
|
||||||
|
|
||||||
SessionDep = Annotated[AsyncSession, Depends(get_session)]
|
|
||||||
|
# --- 数据库会话依赖 ---
|
||||||
|
|
||||||
|
SessionDep: TypeAlias = Annotated[AsyncSession, Depends(get_session)]
|
||||||
"""数据库会话依赖,用于路由函数中获取数据库会话"""
|
"""数据库会话依赖,用于路由函数中获取数据库会话"""
|
||||||
|
|
||||||
|
|
||||||
|
# --- 时间筛选依赖 ---
|
||||||
|
|
||||||
|
async def _get_time_filter_queries(
|
||||||
|
created_after_datetime: Annotated[datetime | None, Query()] = None,
|
||||||
|
created_before_datetime: Annotated[datetime | None, Query()] = None,
|
||||||
|
updated_after_datetime: Annotated[datetime | None, Query()] = None,
|
||||||
|
updated_before_datetime: Annotated[datetime | None, Query()] = None,
|
||||||
|
) -> TimeFilterRequest:
|
||||||
|
"""解析时间筛选查询参数"""
|
||||||
|
return TimeFilterRequest(
|
||||||
|
created_after_datetime=created_after_datetime,
|
||||||
|
created_before_datetime=created_before_datetime,
|
||||||
|
updated_after_datetime=updated_after_datetime,
|
||||||
|
updated_before_datetime=updated_before_datetime,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
TimeFilterRequestDep: TypeAlias = Annotated[TimeFilterRequest, Depends(_get_time_filter_queries)]
|
||||||
|
"""获取时间筛选参数的依赖(用于 count 等统计接口)"""
|
||||||
|
|
||||||
|
|
||||||
|
# --- 分页排序依赖 ---
|
||||||
|
|
||||||
|
async def _get_table_view_queries(
|
||||||
|
offset: Annotated[int | None, Query(ge=0)] = 0,
|
||||||
|
limit: Annotated[int | None, Query(ge=1, le=100)] = 20,
|
||||||
|
desc: bool | None = True,
|
||||||
|
order: Literal["created_at", "updated_at"] | None = "created_at",
|
||||||
|
created_after_datetime: Annotated[datetime | None, Query()] = None,
|
||||||
|
created_before_datetime: Annotated[datetime | None, Query()] = None,
|
||||||
|
updated_after_datetime: Annotated[datetime | None, Query()] = None,
|
||||||
|
updated_before_datetime: Annotated[datetime | None, Query()] = None,
|
||||||
|
) -> TableViewRequest:
|
||||||
|
"""解析分页排序和时间筛选查询参数"""
|
||||||
|
return TableViewRequest(
|
||||||
|
offset=offset,
|
||||||
|
limit=limit,
|
||||||
|
desc=desc,
|
||||||
|
order=order,
|
||||||
|
created_after_datetime=created_after_datetime,
|
||||||
|
created_before_datetime=created_before_datetime,
|
||||||
|
updated_after_datetime=updated_after_datetime,
|
||||||
|
updated_before_datetime=updated_before_datetime,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
TableViewRequestDep: TypeAlias = Annotated[TableViewRequest, Depends(_get_table_view_queries)]
|
||||||
|
"""获取分页排序和时间筛选参数的依赖"""
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ from .object import (
|
|||||||
)
|
)
|
||||||
from .physical_file import PhysicalFile, PhysicalFileBase
|
from .physical_file import PhysicalFile, PhysicalFileBase
|
||||||
from .order import Order, OrderStatus, OrderType
|
from .order import Order, OrderStatus, OrderType
|
||||||
from .policy import Policy, PolicyBase, PolicyOptions, PolicyOptionsBase, PolicyType
|
from .policy import Policy, PolicyBase, PolicyOptions, PolicyOptionsBase, PolicyType, PolicySummary
|
||||||
from .redeem import Redeem, RedeemType
|
from .redeem import Redeem, RedeemType
|
||||||
from .report import Report, ReportReason
|
from .report import Report, ReportReason
|
||||||
from .setting import (
|
from .setting import (
|
||||||
@@ -75,11 +75,11 @@ from .setting import (
|
|||||||
# 管理员DTO
|
# 管理员DTO
|
||||||
SettingItem, SettingsListResponse, SettingsUpdateRequest, SettingsUpdateResponse,
|
SettingItem, SettingsListResponse, SettingsUpdateRequest, SettingsUpdateResponse,
|
||||||
)
|
)
|
||||||
from .share import Share, ShareBase, ShareCreateRequest, ShareResponse
|
from .share import Share, ShareBase, ShareCreateRequest, ShareResponse, AdminShareListItem
|
||||||
from .source_link import SourceLink
|
from .source_link import SourceLink
|
||||||
from .storage_pack import StoragePack
|
from .storage_pack import StoragePack
|
||||||
from .tag import Tag, TagType
|
from .tag import Tag, TagType
|
||||||
from .task import Task, TaskProps, TaskPropsBase, TaskStatus, TaskType
|
from .task import Task, TaskProps, TaskPropsBase, TaskStatus, TaskType, TaskSummary
|
||||||
from .webdav import WebDAV
|
from .webdav import WebDAV
|
||||||
|
|
||||||
from .database import engine, get_session
|
from .database import engine, get_session
|
||||||
@@ -97,3 +97,6 @@ from .model_base import (
|
|||||||
AdminSummaryData,
|
AdminSummaryData,
|
||||||
AdminSummaryResponse,
|
AdminSummaryResponse,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# mixin 中的通用分页模型
|
||||||
|
from .mixin import ListResponse
|
||||||
@@ -124,8 +124,8 @@ class GroupUpdateRequest(SQLModelBase):
|
|||||||
"""关联的存储策略UUID列表"""
|
"""关联的存储策略UUID列表"""
|
||||||
|
|
||||||
|
|
||||||
class GroupDetailResponse(GroupAllOptionsBase):
|
class GroupCoreBase(SQLModelBase):
|
||||||
"""用户组详情响应 DTO"""
|
"""用户组核心字段(从 Group 模型提取)"""
|
||||||
|
|
||||||
id: UUID
|
id: UUID
|
||||||
"""用户组UUID"""
|
"""用户组UUID"""
|
||||||
@@ -148,12 +148,35 @@ class GroupDetailResponse(GroupAllOptionsBase):
|
|||||||
speed_limit: int = 0
|
speed_limit: int = 0
|
||||||
"""速度限制 (KB/s)"""
|
"""速度限制 (KB/s)"""
|
||||||
|
|
||||||
|
|
||||||
|
class GroupDetailResponse(GroupCoreBase, GroupAllOptionsBase):
|
||||||
|
"""用户组详情响应 DTO"""
|
||||||
|
|
||||||
user_count: int = 0
|
user_count: int = 0
|
||||||
"""用户数量"""
|
"""用户数量"""
|
||||||
|
|
||||||
policy_ids: list[UUID] = []
|
policy_ids: list[UUID] = []
|
||||||
"""关联的存储策略UUID列表"""
|
"""关联的存储策略UUID列表"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_group(
|
||||||
|
cls,
|
||||||
|
group: "Group",
|
||||||
|
user_count: int,
|
||||||
|
policies: list["Policy"],
|
||||||
|
) -> "GroupDetailResponse":
|
||||||
|
"""从 Group ORM 对象构建"""
|
||||||
|
opts = group.options
|
||||||
|
return cls(
|
||||||
|
# GroupCoreBase 字段(从 Group 模型提取)
|
||||||
|
**GroupCoreBase.model_validate(group, from_attributes=True).model_dump(),
|
||||||
|
# GroupAllOptionsBase 字段(从 GroupOptions 提取)
|
||||||
|
**(GroupAllOptionsBase.model_validate(opts, from_attributes=True).model_dump() if opts else {}),
|
||||||
|
# 计算字段
|
||||||
|
user_count=user_count,
|
||||||
|
policy_ids=[p.id for p in policies],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class GroupListResponse(SQLModelBase):
|
class GroupListResponse(SQLModelBase):
|
||||||
"""用户组列表响应 DTO"""
|
"""用户组列表响应 DTO"""
|
||||||
|
|||||||
@@ -695,6 +695,32 @@ class AdminFileResponse(ObjectResponse):
|
|||||||
ban_reason: str | None = None
|
ban_reason: str | None = None
|
||||||
"""封禁原因"""
|
"""封禁原因"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_object(
|
||||||
|
cls,
|
||||||
|
obj: "Object",
|
||||||
|
owner: "User | None",
|
||||||
|
policy: "Policy | None",
|
||||||
|
) -> "AdminFileResponse":
|
||||||
|
"""从 Object ORM 对象构建"""
|
||||||
|
return cls(
|
||||||
|
# ObjectBase 字段
|
||||||
|
**ObjectBase.model_validate(obj, from_attributes=True).model_dump(),
|
||||||
|
# ObjectResponse 字段
|
||||||
|
id=obj.id,
|
||||||
|
thumb=False,
|
||||||
|
date=obj.updated_at,
|
||||||
|
create_date=obj.created_at,
|
||||||
|
source_enabled=False,
|
||||||
|
# AdminFileResponse 字段
|
||||||
|
owner_id=obj.owner_id,
|
||||||
|
owner_username=owner.username if owner else "unknown",
|
||||||
|
policy_name=policy.name if policy else "unknown",
|
||||||
|
is_banned=obj.is_banned,
|
||||||
|
banned_at=obj.banned_at,
|
||||||
|
ban_reason=obj.ban_reason,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class FileBanRequest(SQLModelBase):
|
class FileBanRequest(SQLModelBase):
|
||||||
"""文件封禁请求 DTO"""
|
"""文件封禁请求 DTO"""
|
||||||
|
|||||||
@@ -78,6 +78,34 @@ class PolicyBase(SQLModelBase):
|
|||||||
"""是否开启源链接访问"""
|
"""是否开启源链接访问"""
|
||||||
|
|
||||||
|
|
||||||
|
# ==================== DTO 模型 ====================
|
||||||
|
|
||||||
|
|
||||||
|
class PolicySummary(SQLModelBase):
|
||||||
|
"""策略摘要,用于列表展示"""
|
||||||
|
|
||||||
|
id: UUID
|
||||||
|
"""策略UUID"""
|
||||||
|
|
||||||
|
name: str
|
||||||
|
"""策略名称"""
|
||||||
|
|
||||||
|
type: PolicyType
|
||||||
|
"""策略类型"""
|
||||||
|
|
||||||
|
server: str | None
|
||||||
|
"""服务器地址"""
|
||||||
|
|
||||||
|
max_size: int
|
||||||
|
"""最大文件尺寸"""
|
||||||
|
|
||||||
|
is_private: bool
|
||||||
|
"""是否私有"""
|
||||||
|
|
||||||
|
|
||||||
|
# ==================== 数据库模型 ====================
|
||||||
|
|
||||||
|
|
||||||
class PolicyOptionsBase(SQLModelBase):
|
class PolicyOptionsBase(SQLModelBase):
|
||||||
"""存储策略选项的基础模型"""
|
"""存储策略选项的基础模型"""
|
||||||
|
|
||||||
|
|||||||
@@ -160,3 +160,61 @@ class ShareResponse(SQLModelBase):
|
|||||||
|
|
||||||
has_password: bool
|
has_password: bool
|
||||||
"""是否有密码"""
|
"""是否有密码"""
|
||||||
|
|
||||||
|
|
||||||
|
class ShareListItemBase(SQLModelBase):
|
||||||
|
"""分享列表项基础字段"""
|
||||||
|
|
||||||
|
id: int
|
||||||
|
"""分享ID"""
|
||||||
|
|
||||||
|
code: str
|
||||||
|
"""分享码"""
|
||||||
|
|
||||||
|
views: int
|
||||||
|
"""浏览次数"""
|
||||||
|
|
||||||
|
downloads: int
|
||||||
|
"""下载次数"""
|
||||||
|
|
||||||
|
remain_downloads: int | None
|
||||||
|
"""剩余下载次数"""
|
||||||
|
|
||||||
|
expires: datetime | None
|
||||||
|
"""过期时间"""
|
||||||
|
|
||||||
|
preview_enabled: bool
|
||||||
|
"""是否允许预览"""
|
||||||
|
|
||||||
|
score: int
|
||||||
|
"""积分"""
|
||||||
|
|
||||||
|
user_id: UUID
|
||||||
|
"""用户UUID"""
|
||||||
|
|
||||||
|
created_at: datetime
|
||||||
|
"""创建时间"""
|
||||||
|
|
||||||
|
|
||||||
|
class AdminShareListItem(ShareListItemBase):
|
||||||
|
"""管理员分享列表项 DTO,添加关联字段"""
|
||||||
|
|
||||||
|
username: str | None
|
||||||
|
"""用户名"""
|
||||||
|
|
||||||
|
object_name: str | None
|
||||||
|
"""对象名称"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_share(
|
||||||
|
cls,
|
||||||
|
share: "Share",
|
||||||
|
user: "User | None",
|
||||||
|
obj: "Object | None",
|
||||||
|
) -> "AdminShareListItem":
|
||||||
|
"""从 Share ORM 对象构建"""
|
||||||
|
return cls(
|
||||||
|
**ShareListItemBase.model_validate(share, from_attributes=True).model_dump(),
|
||||||
|
username=user.username if user else None,
|
||||||
|
object_name=obj.name if obj else None,
|
||||||
|
)
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ from .base import SQLModelBase
|
|||||||
from .mixin import TableBaseMixin
|
from .mixin import TableBaseMixin
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .user import User
|
|
||||||
from .download import Download
|
from .download import Download
|
||||||
|
from .user import User
|
||||||
|
|
||||||
|
|
||||||
class TaskStatus(StrEnum):
|
class TaskStatus(StrEnum):
|
||||||
@@ -31,6 +31,55 @@ class TaskType(StrEnum):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# ==================== DTO 模型 ====================
|
||||||
|
|
||||||
|
|
||||||
|
class TaskSummaryBase(SQLModelBase):
|
||||||
|
"""任务摘要基础字段"""
|
||||||
|
|
||||||
|
id: int
|
||||||
|
"""任务ID"""
|
||||||
|
|
||||||
|
type: int
|
||||||
|
"""任务类型"""
|
||||||
|
|
||||||
|
status: TaskStatus
|
||||||
|
"""任务状态"""
|
||||||
|
|
||||||
|
progress: int
|
||||||
|
"""进度(0-100)"""
|
||||||
|
|
||||||
|
error: str | None
|
||||||
|
"""错误信息"""
|
||||||
|
|
||||||
|
user_id: UUID
|
||||||
|
"""用户UUID"""
|
||||||
|
|
||||||
|
created_at: datetime
|
||||||
|
"""创建时间"""
|
||||||
|
|
||||||
|
updated_at: datetime
|
||||||
|
"""更新时间"""
|
||||||
|
|
||||||
|
|
||||||
|
class TaskSummary(TaskSummaryBase):
|
||||||
|
"""任务摘要,用于管理员列表展示"""
|
||||||
|
|
||||||
|
username: str | None
|
||||||
|
"""用户名"""
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_task(cls, task: "Task", user: "User | None") -> "TaskSummary":
|
||||||
|
"""从 Task ORM 对象构建"""
|
||||||
|
return cls(
|
||||||
|
**TaskSummaryBase.model_validate(task, from_attributes=True).model_dump(),
|
||||||
|
username=user.username if user else None,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ==================== 数据库模型 ====================
|
||||||
|
|
||||||
|
|
||||||
class TaskPropsBase(SQLModelBase):
|
class TaskPropsBase(SQLModelBase):
|
||||||
"""任务属性基础模型"""
|
"""任务属性基础模型"""
|
||||||
|
|
||||||
|
|||||||
@@ -8,11 +8,10 @@ from loguru import logger as l
|
|||||||
from sqlalchemy import and_
|
from sqlalchemy import and_
|
||||||
|
|
||||||
from middleware.auth import admin_required
|
from middleware.auth import admin_required
|
||||||
from middleware.dependencies import SessionDep
|
from middleware.dependencies import SessionDep, TableViewRequestDep
|
||||||
from models import (
|
from models import (
|
||||||
Policy, PolicyType, User, ResponseBase,
|
Policy, PolicyType, User, ResponseBase, ListResponse,
|
||||||
Object, ObjectType, )
|
Object, ObjectType, AdminFileResponse, FileBanRequest, )
|
||||||
from models.object import AdminFileResponse, FileBanRequest
|
|
||||||
from service.storage import LocalStorageService
|
from service.storage import LocalStorageService
|
||||||
|
|
||||||
admin_file_router = APIRouter(
|
admin_file_router = APIRouter(
|
||||||
@@ -28,25 +27,21 @@ admin_file_router = APIRouter(
|
|||||||
)
|
)
|
||||||
async def router_admin_get_file_list(
|
async def router_admin_get_file_list(
|
||||||
session: SessionDep,
|
session: SessionDep,
|
||||||
|
table_view: TableViewRequestDep,
|
||||||
user_id: UUID | None = None,
|
user_id: UUID | None = None,
|
||||||
is_banned: bool | None = None,
|
is_banned: bool | None = None,
|
||||||
keyword: str | None = None,
|
keyword: str | None = None,
|
||||||
page: int = 1,
|
) -> ListResponse[AdminFileResponse]:
|
||||||
page_size: int = 20,
|
|
||||||
) -> ResponseBase:
|
|
||||||
"""
|
"""
|
||||||
获取系统中的文件列表,支持筛选。
|
获取系统中的文件列表,支持筛选。
|
||||||
|
|
||||||
:param session: 数据库会话
|
:param session: 数据库会话
|
||||||
|
:param table_view: 分页排序参数依赖
|
||||||
:param user_id: 按用户筛选
|
:param user_id: 按用户筛选
|
||||||
:param is_banned: 按封禁状态筛选
|
:param is_banned: 按封禁状态筛选
|
||||||
:param keyword: 按文件名搜索
|
:param keyword: 按文件名搜索
|
||||||
:param page: 页码
|
:return: 分页文件列表
|
||||||
:param page_size: 每页数量
|
|
||||||
:return: 文件列表
|
|
||||||
"""
|
"""
|
||||||
offset = (page - 1) * page_size
|
|
||||||
|
|
||||||
# 构建查询条件
|
# 构建查询条件
|
||||||
conditions = [Object.type == ObjectType.FILE]
|
conditions = [Object.type == ObjectType.FILE]
|
||||||
if user_id:
|
if user_id:
|
||||||
@@ -56,42 +51,17 @@ async def router_admin_get_file_list(
|
|||||||
if keyword:
|
if keyword:
|
||||||
conditions.append(Object.name.ilike(f"%{keyword}%"))
|
conditions.append(Object.name.ilike(f"%{keyword}%"))
|
||||||
|
|
||||||
combined_condition = and_(*conditions) if len(conditions) > 1 else conditions[0]
|
condition = and_(*conditions) if len(conditions) > 1 else conditions[0]
|
||||||
|
result = await Object.get_with_count(session, condition, table_view=table_view, load=Object.owner)
|
||||||
files = await Object.get(
|
|
||||||
session,
|
|
||||||
combined_condition,
|
|
||||||
fetch_mode="all",
|
|
||||||
offset=offset,
|
|
||||||
limit=page_size,
|
|
||||||
load=Object.owner,
|
|
||||||
)
|
|
||||||
|
|
||||||
total = await Object.count(session, combined_condition)
|
|
||||||
|
|
||||||
# 构建响应
|
# 构建响应
|
||||||
file_list = []
|
items: list[AdminFileResponse] = []
|
||||||
for f in files:
|
for f in result.items:
|
||||||
owner = await f.awaitable_attrs.owner
|
owner = await f.awaitable_attrs.owner
|
||||||
policy = await f.awaitable_attrs.policy
|
policy = await f.awaitable_attrs.policy
|
||||||
file_list.append(AdminFileResponse(
|
items.append(AdminFileResponse.from_object(f, owner, policy))
|
||||||
id=f.id,
|
|
||||||
name=f.name,
|
|
||||||
type=f.type,
|
|
||||||
size=f.size,
|
|
||||||
thumb=False,
|
|
||||||
date=f.updated_at,
|
|
||||||
create_date=f.created_at,
|
|
||||||
source_enabled=False,
|
|
||||||
owner_id=f.owner_id,
|
|
||||||
owner_username=owner.username if owner else "unknown",
|
|
||||||
policy_name=policy.name if policy else "unknown",
|
|
||||||
is_banned=f.is_banned,
|
|
||||||
banned_at=f.banned_at,
|
|
||||||
ban_reason=f.ban_reason,
|
|
||||||
).model_dump())
|
|
||||||
|
|
||||||
return ResponseBase(data={"files": file_list, "total": total})
|
return ListResponse(items=items, count=result.count)
|
||||||
|
|
||||||
|
|
||||||
@admin_file_router.get(
|
@admin_file_router.get(
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ from fastapi import APIRouter, Depends, HTTPException
|
|||||||
from loguru import logger as l
|
from loguru import logger as l
|
||||||
|
|
||||||
from middleware.auth import admin_required
|
from middleware.auth import admin_required
|
||||||
from middleware.dependencies import SessionDep
|
from middleware.dependencies import SessionDep, TableViewRequestDep
|
||||||
from models import (
|
from models import (
|
||||||
User, ResponseBase,
|
User, ResponseBase, UserPublic, ListResponse,
|
||||||
Group, GroupOptions, )
|
Group, GroupOptions, )
|
||||||
from models.group import (
|
from models.group import (
|
||||||
GroupCreateRequest, GroupUpdateRequest, GroupDetailResponse, )
|
GroupCreateRequest, GroupUpdateRequest, GroupDetailResponse, )
|
||||||
@@ -25,61 +25,25 @@ admin_group_router = APIRouter(
|
|||||||
)
|
)
|
||||||
async def router_admin_get_groups(
|
async def router_admin_get_groups(
|
||||||
session: SessionDep,
|
session: SessionDep,
|
||||||
page: int = 1,
|
table_view: TableViewRequestDep,
|
||||||
page_size: int = 20,
|
) -> ListResponse[GroupDetailResponse]:
|
||||||
) -> ResponseBase:
|
|
||||||
"""
|
"""
|
||||||
获取用户组列表,支持分页。
|
获取用户组列表,支持分页、排序和时间筛选。
|
||||||
|
|
||||||
:param session: 数据库会话
|
:param session: 数据库会话
|
||||||
:param page: 页码
|
:param table_view: 分页排序参数依赖
|
||||||
:param page_size: 每页数量
|
:return: 分页用户组列表
|
||||||
:return: 用户组列表
|
|
||||||
"""
|
"""
|
||||||
offset = (page - 1) * page_size
|
result = await Group.get_with_count(session, table_view=table_view, load=Group.options)
|
||||||
|
|
||||||
groups = await Group.get(
|
|
||||||
session,
|
|
||||||
None,
|
|
||||||
fetch_mode="all",
|
|
||||||
offset=offset,
|
|
||||||
limit=page_size,
|
|
||||||
load=Group.options,
|
|
||||||
)
|
|
||||||
|
|
||||||
total = await Group.count(session, None)
|
|
||||||
|
|
||||||
# 构建响应
|
# 构建响应
|
||||||
group_list = []
|
items: list[GroupDetailResponse] = []
|
||||||
for g in groups:
|
for g in result.items:
|
||||||
opts = g.options
|
|
||||||
policies = await g.awaitable_attrs.policies
|
policies = await g.awaitable_attrs.policies
|
||||||
user_count = await User.count(session, User.group_id == g.id)
|
user_count = await User.count(session, User.group_id == g.id)
|
||||||
|
items.append(GroupDetailResponse.from_group(g, user_count, policies))
|
||||||
|
|
||||||
group_list.append(GroupDetailResponse(
|
return ListResponse(items=items, count=result.count)
|
||||||
id=g.id,
|
|
||||||
name=g.name,
|
|
||||||
max_storage=g.max_storage,
|
|
||||||
share_enabled=g.share_enabled,
|
|
||||||
web_dav_enabled=g.web_dav_enabled,
|
|
||||||
admin=g.admin,
|
|
||||||
speed_limit=g.speed_limit,
|
|
||||||
user_count=user_count,
|
|
||||||
policy_ids=[p.id for p in policies],
|
|
||||||
share_download=opts.share_download if opts else False,
|
|
||||||
share_free=opts.share_free if opts else False,
|
|
||||||
relocate=opts.relocate if opts else False,
|
|
||||||
source_batch=opts.source_batch if opts else 0,
|
|
||||||
select_node=opts.select_node if opts else False,
|
|
||||||
advance_delete=opts.advance_delete if opts else False,
|
|
||||||
archive_download=opts.archive_download if opts else False,
|
|
||||||
archive_task=opts.archive_task if opts else False,
|
|
||||||
webdav_proxy=opts.webdav_proxy if opts else False,
|
|
||||||
aria2=opts.aria2 if opts else False,
|
|
||||||
redirected_source=opts.redirected_source if opts else False,
|
|
||||||
).model_dump())
|
|
||||||
|
|
||||||
return ResponseBase(data={"groups": group_list, "total": total})
|
|
||||||
|
|
||||||
|
|
||||||
@admin_group_router.get(
|
@admin_group_router.get(
|
||||||
@@ -104,32 +68,9 @@ async def router_admin_get_group(
|
|||||||
if not group:
|
if not group:
|
||||||
raise HTTPException(status_code=404, detail="用户组不存在")
|
raise HTTPException(status_code=404, detail="用户组不存在")
|
||||||
|
|
||||||
opts = group.options
|
|
||||||
policies = await group.awaitable_attrs.policies
|
policies = await group.awaitable_attrs.policies
|
||||||
user_count = await User.count(session, User.group_id == group_id)
|
user_count = await User.count(session, User.group_id == group_id)
|
||||||
|
response = GroupDetailResponse.from_group(group, user_count, policies)
|
||||||
response = GroupDetailResponse(
|
|
||||||
id=group.id,
|
|
||||||
name=group.name,
|
|
||||||
max_storage=group.max_storage,
|
|
||||||
share_enabled=group.share_enabled,
|
|
||||||
web_dav_enabled=group.web_dav_enabled,
|
|
||||||
admin=group.admin,
|
|
||||||
speed_limit=group.speed_limit,
|
|
||||||
user_count=user_count,
|
|
||||||
policy_ids=[p.id for p in policies],
|
|
||||||
share_download=opts.share_download if opts else False,
|
|
||||||
share_free=opts.share_free if opts else False,
|
|
||||||
relocate=opts.relocate if opts else False,
|
|
||||||
source_batch=opts.source_batch if opts else 0,
|
|
||||||
select_node=opts.select_node if opts else False,
|
|
||||||
advance_delete=opts.advance_delete if opts else False,
|
|
||||||
archive_download=opts.archive_download if opts else False,
|
|
||||||
archive_task=opts.archive_task if opts else False,
|
|
||||||
webdav_proxy=opts.webdav_proxy if opts else False,
|
|
||||||
aria2=opts.aria2 if opts else False,
|
|
||||||
redirected_source=opts.redirected_source if opts else False,
|
|
||||||
)
|
|
||||||
|
|
||||||
return ResponseBase(data=response.model_dump())
|
return ResponseBase(data=response.model_dump())
|
||||||
|
|
||||||
@@ -143,40 +84,28 @@ async def router_admin_get_group(
|
|||||||
async def router_admin_get_group_members(
|
async def router_admin_get_group_members(
|
||||||
session: SessionDep,
|
session: SessionDep,
|
||||||
group_id: UUID,
|
group_id: UUID,
|
||||||
page: int = 1,
|
table_view: TableViewRequestDep,
|
||||||
page_size: int = 20,
|
) -> ListResponse[UserPublic]:
|
||||||
) -> ResponseBase:
|
|
||||||
"""
|
"""
|
||||||
根据用户组ID获取用户组成员列表。
|
根据用户组ID获取用户组成员列表。
|
||||||
|
|
||||||
:param session: 数据库会话
|
:param session: 数据库会话
|
||||||
:param group_id: 用户组UUID
|
:param group_id: 用户组UUID
|
||||||
:param page: 页码
|
:param table_view: 分页排序参数依赖
|
||||||
:param page_size: 每页数量
|
:return: 分页成员列表
|
||||||
:return: 成员列表
|
|
||||||
"""
|
"""
|
||||||
# 验证组存在
|
# 验证组存在
|
||||||
group = await Group.get(session, Group.id == group_id)
|
group = await Group.get(session, Group.id == group_id)
|
||||||
if not group:
|
if not group:
|
||||||
raise HTTPException(status_code=404, detail="用户组不存在")
|
raise HTTPException(status_code=404, detail="用户组不存在")
|
||||||
|
|
||||||
offset = (page - 1) * page_size
|
result = await User.get_with_count(session, User.group_id == group_id, table_view=table_view)
|
||||||
|
|
||||||
users = await User.get(
|
return ListResponse(
|
||||||
session,
|
items=[u.to_public() for u in result.items],
|
||||||
User.group_id == group_id,
|
count=result.count,
|
||||||
fetch_mode="all",
|
|
||||||
offset=offset,
|
|
||||||
limit=page_size,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
total = await User.count(session, User.group_id == group_id)
|
|
||||||
|
|
||||||
return ResponseBase(data={
|
|
||||||
"members": [u.to_public().model_dump() for u in users],
|
|
||||||
"total": total,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
@admin_group_router.post(
|
@admin_group_router.post(
|
||||||
path='/',
|
path='/',
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ from loguru import logger as l
|
|||||||
from sqlmodel import Field
|
from sqlmodel import Field
|
||||||
|
|
||||||
from middleware.auth import admin_required
|
from middleware.auth import admin_required
|
||||||
from middleware.dependencies import SessionDep
|
from middleware.dependencies import SessionDep, TableViewRequestDep
|
||||||
from models import (
|
from models import (
|
||||||
Policy, PolicyBase, PolicyType, ResponseBase,
|
Policy, PolicyBase, PolicyType, PolicySummary, ResponseBase,
|
||||||
Object, )
|
ListResponse, Object, )
|
||||||
from models.base import SQLModelBase
|
from models.base import SQLModelBase
|
||||||
from service.storage import DirectoryCreationError, LocalStorageService
|
from service.storage import DirectoryCreationError, LocalStorageService
|
||||||
|
|
||||||
@@ -45,44 +45,22 @@ class PolicyCreateRequest(PolicyBase):
|
|||||||
)
|
)
|
||||||
async def router_policy_list(
|
async def router_policy_list(
|
||||||
session: SessionDep,
|
session: SessionDep,
|
||||||
page: int = 1,
|
table_view: TableViewRequestDep,
|
||||||
page_size: int = 20,
|
) -> ListResponse[PolicySummary]:
|
||||||
) -> ResponseBase:
|
|
||||||
"""
|
"""
|
||||||
获取所有存储策略列表。
|
获取所有存储策略列表。
|
||||||
|
|
||||||
:param session: 数据库会话
|
:param session: 数据库会话
|
||||||
:param page: 页码
|
:param table_view: 分页排序参数依赖
|
||||||
:param page_size: 每页数量
|
:return: 分页策略列表
|
||||||
:return: 策略列表
|
|
||||||
"""
|
"""
|
||||||
offset = (page - 1) * page_size
|
result = await Policy.get_with_count(session, table_view=table_view)
|
||||||
|
|
||||||
policies = await Policy.get(
|
return ListResponse(
|
||||||
session,
|
items=[PolicySummary.model_validate(p, from_attributes=True) for p in result.items],
|
||||||
None,
|
count=result.count,
|
||||||
fetch_mode="all",
|
|
||||||
offset=offset,
|
|
||||||
limit=page_size,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
total = await Policy.count(session, None)
|
|
||||||
|
|
||||||
return ResponseBase(data={
|
|
||||||
"policies": [
|
|
||||||
{
|
|
||||||
"id": str(p.id),
|
|
||||||
"name": p.name,
|
|
||||||
"type": p.type.value,
|
|
||||||
"server": p.server,
|
|
||||||
"max_size": p.max_size,
|
|
||||||
"is_private": p.is_private,
|
|
||||||
}
|
|
||||||
for p in policies
|
|
||||||
],
|
|
||||||
"total": total,
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
@admin_policy_router.post(
|
@admin_policy_router.post(
|
||||||
path='/test/path',
|
path='/test/path',
|
||||||
|
|||||||
@@ -4,10 +4,10 @@ from fastapi import APIRouter, Depends, HTTPException
|
|||||||
from loguru import logger as l
|
from loguru import logger as l
|
||||||
|
|
||||||
from middleware.auth import admin_required
|
from middleware.auth import admin_required
|
||||||
from middleware.dependencies import SessionDep
|
from middleware.dependencies import SessionDep, TableViewRequestDep
|
||||||
from models import (
|
from models import (
|
||||||
ResponseBase,
|
ResponseBase, ListResponse,
|
||||||
Share, )
|
Share, AdminShareListItem, )
|
||||||
|
|
||||||
admin_share_router = APIRouter(
|
admin_share_router = APIRouter(
|
||||||
prefix='/share',
|
prefix='/share',
|
||||||
@@ -22,53 +22,27 @@ admin_share_router = APIRouter(
|
|||||||
)
|
)
|
||||||
async def router_admin_get_share_list(
|
async def router_admin_get_share_list(
|
||||||
session: SessionDep,
|
session: SessionDep,
|
||||||
|
table_view: TableViewRequestDep,
|
||||||
user_id: UUID | None = None,
|
user_id: UUID | None = None,
|
||||||
page: int = 1,
|
) -> ListResponse[AdminShareListItem]:
|
||||||
page_size: int = 20,
|
|
||||||
) -> ResponseBase:
|
|
||||||
"""
|
"""
|
||||||
获取分享列表。
|
获取分享列表。
|
||||||
|
|
||||||
:param session: 数据库会话
|
:param session: 数据库会话
|
||||||
|
:param table_view: 分页排序参数依赖
|
||||||
:param user_id: 按用户筛选
|
:param user_id: 按用户筛选
|
||||||
:param page: 页码
|
:return: 分页分享列表
|
||||||
:param page_size: 每页数量
|
|
||||||
:return: 分享列表
|
|
||||||
"""
|
"""
|
||||||
offset = (page - 1) * page_size
|
|
||||||
condition = Share.user_id == user_id if user_id else None
|
condition = Share.user_id == user_id if user_id else None
|
||||||
|
result = await Share.get_with_count(session, condition, table_view=table_view, load=Share.user)
|
||||||
|
|
||||||
shares = await Share.get(
|
items: list[AdminShareListItem] = []
|
||||||
session,
|
for s in result.items:
|
||||||
condition,
|
|
||||||
fetch_mode="all",
|
|
||||||
offset=offset,
|
|
||||||
limit=page_size,
|
|
||||||
load=Share.user,
|
|
||||||
)
|
|
||||||
|
|
||||||
total = await Share.count(session, condition)
|
|
||||||
|
|
||||||
share_list = []
|
|
||||||
for s in shares:
|
|
||||||
user = await s.awaitable_attrs.user
|
user = await s.awaitable_attrs.user
|
||||||
obj = await s.awaitable_attrs.object
|
obj = await s.awaitable_attrs.object
|
||||||
share_list.append({
|
items.append(AdminShareListItem.from_share(s, user, obj))
|
||||||
"id": s.id,
|
|
||||||
"code": s.code,
|
|
||||||
"views": s.views,
|
|
||||||
"downloads": s.downloads,
|
|
||||||
"remain_downloads": s.remain_downloads,
|
|
||||||
"expires": s.expires.isoformat() if s.expires else None,
|
|
||||||
"preview_enabled": s.preview_enabled,
|
|
||||||
"score": s.score,
|
|
||||||
"user_id": str(s.user_id),
|
|
||||||
"username": user.username if user else None,
|
|
||||||
"object_name": obj.name if obj else None,
|
|
||||||
"created_at": s.created_at.isoformat(),
|
|
||||||
})
|
|
||||||
|
|
||||||
return ResponseBase(data={"shares": share_list, "total": total})
|
return ListResponse(items=items, count=result.count)
|
||||||
|
|
||||||
|
|
||||||
@admin_share_router.get(
|
@admin_share_router.get(
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ from loguru import logger as l
|
|||||||
from sqlalchemy import and_
|
from sqlalchemy import and_
|
||||||
|
|
||||||
from middleware.auth import admin_required
|
from middleware.auth import admin_required
|
||||||
from middleware.dependencies import SessionDep
|
from middleware.dependencies import SessionDep, TableViewRequestDep
|
||||||
from models import (
|
from models import (
|
||||||
ResponseBase,
|
ResponseBase, ListResponse,
|
||||||
Task,
|
Task, TaskSummary,
|
||||||
)
|
)
|
||||||
|
|
||||||
admin_task_router = APIRouter(
|
admin_task_router = APIRouter(
|
||||||
@@ -24,23 +24,19 @@ admin_task_router = APIRouter(
|
|||||||
)
|
)
|
||||||
async def router_admin_get_task_list(
|
async def router_admin_get_task_list(
|
||||||
session: SessionDep,
|
session: SessionDep,
|
||||||
|
table_view: TableViewRequestDep,
|
||||||
user_id: UUID | None = None,
|
user_id: UUID | None = None,
|
||||||
status: str | None = None,
|
status: str | None = None,
|
||||||
page: int = 1,
|
) -> ListResponse[TaskSummary]:
|
||||||
page_size: int = 20,
|
|
||||||
) -> ResponseBase:
|
|
||||||
"""
|
"""
|
||||||
获取任务列表。
|
获取任务列表。
|
||||||
|
|
||||||
:param session: 数据库会话
|
:param session: 数据库会话
|
||||||
|
:param table_view: 分页排序参数依赖
|
||||||
:param user_id: 按用户筛选
|
:param user_id: 按用户筛选
|
||||||
:param status: 按状态筛选
|
:param status: 按状态筛选
|
||||||
:param page: 页码
|
:return: 分页任务列表
|
||||||
:param page_size: 每页数量
|
|
||||||
:return: 任务列表
|
|
||||||
"""
|
"""
|
||||||
offset = (page - 1) * page_size
|
|
||||||
|
|
||||||
conditions = []
|
conditions = []
|
||||||
if user_id:
|
if user_id:
|
||||||
conditions.append(Task.user_id == user_id)
|
conditions.append(Task.user_id == user_id)
|
||||||
@@ -48,34 +44,14 @@ async def router_admin_get_task_list(
|
|||||||
conditions.append(Task.status == status)
|
conditions.append(Task.status == status)
|
||||||
|
|
||||||
condition = and_(*conditions) if conditions else None
|
condition = and_(*conditions) if conditions else None
|
||||||
|
result = await Task.get_with_count(session, condition, table_view=table_view, load=Task.user)
|
||||||
|
|
||||||
tasks = await Task.get(
|
items: list[TaskSummary] = []
|
||||||
session,
|
for t in result.items:
|
||||||
condition,
|
|
||||||
fetch_mode="all",
|
|
||||||
offset=offset,
|
|
||||||
limit=page_size,
|
|
||||||
load=Task.user,
|
|
||||||
)
|
|
||||||
|
|
||||||
total = await Task.count(session, condition)
|
|
||||||
|
|
||||||
task_list = []
|
|
||||||
for t in tasks:
|
|
||||||
user = await t.awaitable_attrs.user
|
user = await t.awaitable_attrs.user
|
||||||
task_list.append({
|
items.append(TaskSummary.from_task(t, user))
|
||||||
"id": t.id,
|
|
||||||
"status": t.status,
|
|
||||||
"type": t.type,
|
|
||||||
"progress": t.progress,
|
|
||||||
"error": t.error,
|
|
||||||
"user_id": str(t.user_id),
|
|
||||||
"username": user.username if user else None,
|
|
||||||
"created_at": t.created_at.isoformat(),
|
|
||||||
"updated_at": t.updated_at.isoformat(),
|
|
||||||
})
|
|
||||||
|
|
||||||
return ResponseBase(data={"tasks": task_list, "total": total})
|
return ListResponse(items=items, count=result.count)
|
||||||
|
|
||||||
|
|
||||||
@admin_task_router.get(
|
@admin_task_router.get(
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ from loguru import logger as l
|
|||||||
from sqlalchemy import func, and_
|
from sqlalchemy import func, and_
|
||||||
|
|
||||||
from middleware.auth import admin_required
|
from middleware.auth import admin_required
|
||||||
from middleware.dependencies import SessionDep
|
from middleware.dependencies import SessionDep, TableViewRequestDep
|
||||||
from models import (
|
from models import (
|
||||||
User, ResponseBase,
|
User, ResponseBase, UserPublic, ListResponse,
|
||||||
Group, Object, ObjectType, )
|
Group, Object, ObjectType, )
|
||||||
from models.user import (
|
from models.user import (
|
||||||
UserAdminUpdateRequest, UserCalibrateResponse,
|
UserAdminUpdateRequest, UserCalibrateResponse,
|
||||||
@@ -49,30 +49,19 @@ async def router_admin_get_user(session: SessionDep, user_id: int) -> ResponseBa
|
|||||||
)
|
)
|
||||||
async def router_admin_get_users(
|
async def router_admin_get_users(
|
||||||
session: SessionDep,
|
session: SessionDep,
|
||||||
page: int = 1,
|
table_view: TableViewRequestDep,
|
||||||
page_size: int = 20
|
) -> ListResponse[UserPublic]:
|
||||||
) -> ResponseBase:
|
|
||||||
"""
|
"""
|
||||||
获取用户列表,支持分页。
|
获取用户列表,支持分页、排序和时间筛选。
|
||||||
|
|
||||||
Args:
|
:param session: 数据库会话依赖项
|
||||||
session: 数据库会话依赖项。
|
:param table_view: 分页排序参数依赖
|
||||||
page (int): 页码,默认为1。
|
:return: 分页用户列表
|
||||||
page_size (int): 每页显示的用户数量,默认为20。
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
ResponseBase: 包含用户列表的响应模型。
|
|
||||||
"""
|
"""
|
||||||
offset = (page - 1) * page_size
|
result = await User.get_with_count(session, table_view=table_view)
|
||||||
users: list[User] = await User.get(
|
return ListResponse(
|
||||||
session,
|
items=[user.to_public() for user in result.items],
|
||||||
None,
|
count=result.count,
|
||||||
fetch_mode="all",
|
|
||||||
offset=offset,
|
|
||||||
limit=page_size
|
|
||||||
)
|
|
||||||
return ResponseBase(
|
|
||||||
data=[user.to_public().model_dump() for user in users]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user