From f6f50532a6c62c82328eee7528d02b21445e3be8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8E=E5=B0=8F=E4=B8=98?= Date: Thu, 8 Jan 2026 15:48:32 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AE=80=E5=8C=96=E6=95=B0=E6=8D=AE=E5=BA=93?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 6 +- models/__init__.py | 6 +- models/group.py | 88 +++++++------------------ models/policy.py | 70 +++++++++++++------- models/setting.py | 12 +--- models/share.py | 49 ++++++++------ routers/api/v1/admin/policy/__init__.py | 46 ++----------- routers/api/v1/admin/vas/__init__.py | 14 +--- 8 files changed, 114 insertions(+), 177 deletions(-) diff --git a/.gitignore b/.gitignore index a9c401d..1acc9c6 100644 --- a/.gitignore +++ b/.gitignore @@ -64,4 +64,8 @@ node_modules/ *.temp # 文件 -data/ \ No newline at end of file +data/ + +# JB 的运行配置(换设备用不了) +.run/ +.xml diff --git a/models/__init__.py b/models/__init__.py index b3908e7..ee52ac0 100644 --- a/models/__init__.py +++ b/models/__init__.py @@ -34,7 +34,7 @@ from .node import ( NodeType, ) from .group import ( - Group, GroupBase, GroupOptions, GroupOptionsBase, GroupResponse, + Group, GroupBase, GroupOptions, GroupOptionsBase, GroupAllOptionsBase, GroupResponse, # 管理员DTO GroupCreateRequest, GroupUpdateRequest, GroupDetailResponse, GroupListResponse, ) @@ -67,7 +67,7 @@ from .object import ( ) from .physical_file import PhysicalFile, PhysicalFileBase from .order import Order, OrderStatus, OrderType -from .policy import Policy, PolicyOptions, PolicyOptionsBase, PolicyType +from .policy import Policy, PolicyBase, PolicyOptions, PolicyOptionsBase, PolicyType from .redeem import Redeem, RedeemType from .report import Report, ReportReason from .setting import ( @@ -75,7 +75,7 @@ from .setting import ( # 管理员DTO SettingItem, SettingsListResponse, SettingsUpdateRequest, SettingsUpdateResponse, ) -from .share import Share +from .share import Share, ShareBase, ShareCreateRequest, ShareResponse from .source_link import SourceLink from .storage_pack import StoragePack from .tag import Tag, TagType diff --git a/models/group.py b/models/group.py index 5ed576f..c87bd87 100644 --- a/models/group.py +++ b/models/group.py @@ -22,7 +22,7 @@ class GroupBase(SQLModelBase): class GroupOptionsBase(SQLModelBase): - """用户组选项基础字段,供数据库模型和 DTO 共享""" + """用户组基础选项字段""" share_download: bool = False """是否允许分享下载""" @@ -43,9 +43,28 @@ class GroupOptionsBase(SQLModelBase): """是否允许高级删除""" +class GroupAllOptionsBase(GroupOptionsBase): + """用户组完整选项字段,供 DTO 和数据库模型共享""" + + archive_download: bool = False + """是否允许打包下载""" + + archive_task: bool = False + """是否允许创建打包任务""" + + webdav_proxy: bool = False + """是否允许WebDAV代理""" + + aria2: bool = False + """是否允许使用aria2""" + + redirected_source: bool = False + """是否使用重定向源""" + + # ==================== DTO 模型 ==================== -class GroupCreateRequest(SQLModelBase): +class GroupCreateRequest(GroupAllOptionsBase): """创建用户组请求 DTO""" name: str = Field(max_length=255) @@ -63,39 +82,8 @@ class GroupCreateRequest(SQLModelBase): speed_limit: int = Field(default=0, ge=0) """速度限制 (KB/s), 0为不限制""" - # 用户组选项 - share_download: bool = False - """是否允许分享下载""" - - share_free: bool = False - """是否免积分获取需要积分的内容""" - - relocate: bool = False - """是否允许文件重定位""" - source_batch: int = Field(default=0, ge=0) - """批量获取源地址数量""" - - select_node: bool = False - """是否允许选择节点""" - - advance_delete: bool = False - """是否允许高级删除""" - - archive_download: bool = False - """是否允许打包下载""" - - archive_task: bool = False - """是否允许创建打包任务""" - - webdav_proxy: bool = False - """是否允许WebDAV代理""" - - aria2: bool = False - """是否允许使用aria2""" - - redirected_source: bool = False - """是否使用重定向源""" + """批量获取源地址数量(覆盖基类以添加 ge 约束)""" policy_ids: list[UUID] = [] """关联的存储策略UUID列表""" @@ -136,7 +124,7 @@ class GroupUpdateRequest(SQLModelBase): """关联的存储策略UUID列表""" -class GroupDetailResponse(SQLModelBase): +class GroupDetailResponse(GroupAllOptionsBase): """用户组详情响应 DTO""" id: UUID @@ -166,19 +154,6 @@ class GroupDetailResponse(SQLModelBase): policy_ids: list[UUID] = [] """关联的存储策略UUID列表""" - # 选项 - share_download: bool = False - share_free: bool = False - relocate: bool = False - source_batch: int = 0 - select_node: bool = False - advance_delete: bool = False - archive_download: bool = False - archive_task: bool = False - webdav_proxy: bool = False - aria2: bool = False - redirected_source: bool = False - class GroupListResponse(SQLModelBase): """用户组列表响应 DTO""" @@ -221,7 +196,7 @@ class GroupResponse(GroupBase, GroupOptionsBase): from .policy import GroupPolicyLink -class GroupOptions(GroupOptionsBase, TableBaseMixin): +class GroupOptions(GroupAllOptionsBase, TableBaseMixin): """用户组选项模型""" group_id: UUID = Field( @@ -231,21 +206,6 @@ class GroupOptions(GroupOptionsBase, TableBaseMixin): ) """关联的用户组UUID""" - archive_download: bool = False - """是否允许打包下载""" - - archive_task: bool = False - """是否允许创建打包任务""" - - webdav_proxy: bool = False - """是否允许WebDAV代理""" - - aria2: bool = False - """是否允许使用aria2""" - - redirected_source: bool = False - """是否使用重定向源""" - # 反向关系 group: "Group" = Relationship(back_populates="options") diff --git a/models/policy.py b/models/policy.py index 1fd4547..9731054 100644 --- a/models/policy.py +++ b/models/policy.py @@ -35,6 +35,49 @@ class PolicyType(StrEnum): S3 = "s3" +class PolicyBase(SQLModelBase): + """存储策略基础字段,供 DTO 和数据库模型共享""" + + name: str = Field(max_length=255) + """策略名称""" + + type: PolicyType + """存储策略类型""" + + server: str | None = Field(default=None, max_length=255) + """服务器地址(本地策略为绝对路径)""" + + bucket_name: str | None = Field(default=None, max_length=255) + """存储桶名称""" + + is_private: bool = True + """是否为私有空间""" + + base_url: str | None = Field(default=None, max_length=255) + """访问文件的基础URL""" + + access_key: str | None = None + """Access Key""" + + secret_key: str | None = None + """Secret Key""" + + max_size: int = Field(default=0, ge=0) + """允许上传的最大文件尺寸(字节)""" + + auto_rename: bool = False + """是否自动重命名""" + + dir_name_rule: str | None = Field(default=None, max_length=255) + """目录命名规则""" + + file_name_rule: str | None = Field(default=None, max_length=255) + """文件命名规则""" + + is_origin_link_enable: bool = False + """是否开启源链接访问""" + + class PolicyOptionsBase(SQLModelBase): """存储策略选项的基础模型""" @@ -72,45 +115,22 @@ class PolicyOptions(PolicyOptionsBase, UUIDTableBaseMixin): """关联的策略""" -class Policy(SQLModelBase, UUIDTableBaseMixin): +class Policy(PolicyBase, UUIDTableBaseMixin): """存储策略模型""" + # 覆盖基类字段以添加数据库专有配置 name: str = Field(max_length=255, unique=True) """策略名称""" - type: PolicyType - """存储策略类型""" - - server: str | None = Field(default=None, max_length=255) - """服务器地址(本地策略为绝对路径)""" - - bucket_name: str | None = Field(default=None, max_length=255) - """存储桶名称""" - is_private: bool = Field(default=True, sa_column_kwargs={"server_default": text("true")}) """是否为私有空间""" - base_url: str | None = Field(default=None, max_length=255) - """访问文件的基础URL""" - - access_key: str | None = Field(default=None) - """Access Key""" - - secret_key: str | None = Field(default=None) - """Secret Key""" - max_size: int = Field(default=0, sa_column_kwargs={"server_default": "0"}) """允许上传的最大文件尺寸(字节)""" auto_rename: bool = Field(default=False, sa_column_kwargs={"server_default": text("false")}) """是否自动重命名""" - dir_name_rule: str | None = Field(default=None, max_length=255) - """目录命名规则""" - - file_name_rule: str | None = Field(default=None, max_length=255) - """文件命名规则""" - is_origin_link_enable: bool = Field(default=False, sa_column_kwargs={"server_default": text("false")}) """是否开启源链接访问""" diff --git a/models/setting.py b/models/setting.py index be6c28e..10dfeb1 100644 --- a/models/setting.py +++ b/models/setting.py @@ -120,16 +120,10 @@ class SettingsType(StrEnum): WOPI = "wopi" # 数据库模型 -class Setting(SQLModelBase, TableBaseMixin): - """设置模型""" +class Setting(SettingItem, TableBaseMixin): + """设置模型,继承 SettingItem 中的 name 和 value 字段""" __table_args__ = (UniqueConstraint("type", "name", name="uq_setting_type_name"),) type: SettingsType - """设置类型/分组""" - - name: str - """设置项名称""" - - value: str | None - """设置值""" \ No newline at end of file + """设置类型/分组(覆盖基类的 str 类型为枚举类型)""" \ No newline at end of file diff --git a/models/share.py b/models/share.py index a4fc720..72da92c 100644 --- a/models/share.py +++ b/models/share.py @@ -14,6 +14,32 @@ if TYPE_CHECKING: from .object import Object +# ==================== Base 模型 ==================== + +class ShareBase(SQLModelBase): + """分享基础字段,供 DTO 和数据库模型共享""" + + object_id: UUID + """关联的对象UUID""" + + password: str | None = None + """分享密码""" + + expires: datetime | None = None + """过期时间(NULL为永不过期)""" + + remain_downloads: int | None = None + """剩余下载次数(NULL为不限制)""" + + preview_enabled: bool = True + """是否允许预览""" + + score: int = 0 + """兑换此分享所需的积分""" + + +# ==================== 数据库模型 ==================== + class Share(SQLModelBase, TableBaseMixin): """分享模型""" @@ -88,26 +114,9 @@ class Share(SQLModelBase, TableBaseMixin): # ==================== DTO 模型 ==================== -class ShareCreateRequest(SQLModelBase): - """创建分享请求 DTO""" - - object_id: UUID - """要分享的对象UUID""" - - password: str | None = None - """分享密码(可选)""" - - expires: datetime | None = None - """过期时间(可选,NULL为永不过期)""" - - remain_downloads: int | None = None - """剩余下载次数(可选,NULL为不限制)""" - - preview_enabled: bool = True - """是否允许预览""" - - score: int = 0 - """兑换此分享所需的积分""" +class ShareCreateRequest(ShareBase): + """创建分享请求 DTO,继承 ShareBase 中的所有字段""" + pass class ShareResponse(SQLModelBase): diff --git a/routers/api/v1/admin/policy/__init__.py b/routers/api/v1/admin/policy/__init__.py index b0f6cbb..357bcfa 100644 --- a/routers/api/v1/admin/policy/__init__.py +++ b/routers/api/v1/admin/policy/__init__.py @@ -7,7 +7,7 @@ from sqlmodel import Field from middleware.auth import admin_required from middleware.dependencies import SessionDep from models import ( - Policy, PolicyType, ResponseBase, + Policy, PolicyBase, PolicyType, ResponseBase, Object, ) from models.base import SQLModelBase from service.storage import DirectoryCreationError, LocalStorageService @@ -33,47 +33,9 @@ class PolicyTestSlaveRequest(SQLModelBase): secret: str """从机通信密钥""" -class PolicyCreateRequest(SQLModelBase): - """创建存储策略请求 DTO""" - - name: str = Field(max_length=255) - """策略名称""" - - type: PolicyType - """策略类型""" - - server: str | None = Field(default=None, max_length=255) - """服务器地址/本地路径(本地存储必填)""" - - bucket_name: str | None = Field(default=None, max_length=255) - """存储桶名称(S3必填)""" - - is_private: bool = True - """是否为私有空间""" - - base_url: str | None = Field(default=None, max_length=255) - """访问文件的基础URL""" - - access_key: str | None = None - """Access Key""" - - secret_key: str | None = None - """Secret Key""" - - max_size: int = Field(default=0, ge=0) - """允许上传的最大文件尺寸(字节),0表示不限制""" - - auto_rename: bool = False - """是否自动重命名""" - - dir_name_rule: str | None = Field(default=None, max_length=255) - """目录命名规则""" - - file_name_rule: str | None = Field(default=None, max_length=255) - """文件命名规则""" - - is_origin_link_enable: bool = False - """是否开启源链接访问""" +class PolicyCreateRequest(PolicyBase): + """创建存储策略请求 DTO,继承 PolicyBase 中的所有字段""" + pass @admin_policy_router.get( path='/list', diff --git a/routers/api/v1/admin/vas/__init__.py b/routers/api/v1/admin/vas/__init__.py index b379e7c..c112d31 100644 --- a/routers/api/v1/admin/vas/__init__.py +++ b/routers/api/v1/admin/vas/__init__.py @@ -1,24 +1,12 @@ -from datetime import datetime, timedelta from uuid import UUID from fastapi import APIRouter, Depends, HTTPException -from loguru import logger as l -from sqlalchemy import and_ from middleware.auth import admin_required from middleware.dependencies import SessionDep from models import ( - User, ResponseBase, - Setting, Object, ObjectType, Share, AdminSummaryResponse, MetricsSummary, LicenseInfo, VersionInfo, - AdminSummaryData, + ResponseBase, ) -from models.base import SQLModelBase -from models.setting import ( - SettingItem, SettingsListResponse, SettingsUpdateRequest, SettingsUpdateResponse, -) -from models.setting import SettingsType -from utils import http_exceptions -from utils.conf import appmeta admin_vas_router = APIRouter( prefix='/vas',