feat: add theme preset system with admin CRUD, public listing, and user theme settings
- Add ChromaticColor (17 Tailwind colors) and NeutralColor (5 grays) enums - Add ThemePreset table with flat color columns and unique name constraint - Add admin theme endpoints (CRUD + set default) at /api/v1/admin/theme - Add public theme listing at /api/v1/site/themes - Add user theme settings (PATCH /theme) with color snapshot on User model - User.color_* columns store per-user overrides; fallback to default preset then builtin - Initialize default theme preset in migration - Remove legacy defaultTheme/themes settings Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -13,14 +13,21 @@ from .user import (
|
||||
UserPublic,
|
||||
UserResponse,
|
||||
UserSettingResponse,
|
||||
UserThemeUpdateRequest,
|
||||
WebAuthnInfo,
|
||||
UserTwoFactorResponse,
|
||||
# 管理员DTO
|
||||
UserAdminUpdateRequest,
|
||||
UserCalibrateResponse,
|
||||
UserAdminDetailResponse,
|
||||
)
|
||||
from .user_authn import AuthnResponse, UserAuthn
|
||||
from .color import ThemeResponse
|
||||
from .color import ChromaticColor, NeutralColor, ThemeColorsBase, BUILTIN_DEFAULT_COLORS
|
||||
from .theme_preset import (
|
||||
ThemePreset, ThemePresetBase,
|
||||
ThemePresetCreateRequest, ThemePresetUpdateRequest,
|
||||
ThemePresetResponse, ThemePresetListResponse,
|
||||
)
|
||||
|
||||
from .download import (
|
||||
Download,
|
||||
@@ -68,6 +75,10 @@ from .object import (
|
||||
AdminFileResponse,
|
||||
AdminFileListResponse,
|
||||
FileBanRequest,
|
||||
# 回收站DTO
|
||||
TrashItemResponse,
|
||||
TrashRestoreRequest,
|
||||
TrashDeleteRequest,
|
||||
)
|
||||
from .physical_file import PhysicalFile, PhysicalFileBase
|
||||
from .uri import DiskNextURI, FileSystemNamespace
|
||||
@@ -80,7 +91,11 @@ from .setting import (
|
||||
# 管理员DTO
|
||||
SettingItem, SettingsListResponse, SettingsUpdateRequest, SettingsUpdateResponse,
|
||||
)
|
||||
from .share import Share, ShareBase, ShareCreateRequest, ShareResponse, AdminShareListItem
|
||||
from .share import (
|
||||
Share, ShareBase, ShareCreateRequest, CreateShareResponse, ShareResponse,
|
||||
ShareOwnerInfo, ShareObjectItem, ShareDetailResponse,
|
||||
AdminShareListItem,
|
||||
)
|
||||
from .source_link import SourceLink
|
||||
from .storage_pack import StoragePack
|
||||
from .tag import Tag, TagType
|
||||
|
||||
@@ -1,7 +1,71 @@
|
||||
from enum import StrEnum
|
||||
|
||||
from .base import SQLModelBase
|
||||
|
||||
class ThemeResponse(SQLModelBase):
|
||||
"""主题响应 DTO"""
|
||||
|
||||
pass
|
||||
|
||||
class ChromaticColor(StrEnum):
|
||||
"""有彩色枚举(17种 Tailwind 调色板颜色)"""
|
||||
|
||||
RED = "red"
|
||||
ORANGE = "orange"
|
||||
AMBER = "amber"
|
||||
YELLOW = "yellow"
|
||||
LIME = "lime"
|
||||
GREEN = "green"
|
||||
EMERALD = "emerald"
|
||||
TEAL = "teal"
|
||||
CYAN = "cyan"
|
||||
SKY = "sky"
|
||||
BLUE = "blue"
|
||||
INDIGO = "indigo"
|
||||
VIOLET = "violet"
|
||||
PURPLE = "purple"
|
||||
FUCHSIA = "fuchsia"
|
||||
PINK = "pink"
|
||||
ROSE = "rose"
|
||||
|
||||
|
||||
class NeutralColor(StrEnum):
|
||||
"""无彩色枚举(5种灰色调)"""
|
||||
|
||||
SLATE = "slate"
|
||||
GRAY = "gray"
|
||||
ZINC = "zinc"
|
||||
NEUTRAL = "neutral"
|
||||
STONE = "stone"
|
||||
|
||||
|
||||
class ThemeColorsBase(SQLModelBase):
|
||||
"""嵌套颜色 DTO,API 请求/响应层使用"""
|
||||
|
||||
primary: ChromaticColor
|
||||
"""主色调"""
|
||||
|
||||
secondary: ChromaticColor
|
||||
"""辅助色"""
|
||||
|
||||
success: ChromaticColor
|
||||
"""成功色"""
|
||||
|
||||
info: ChromaticColor
|
||||
"""信息色"""
|
||||
|
||||
warning: ChromaticColor
|
||||
"""警告色"""
|
||||
|
||||
error: ChromaticColor
|
||||
"""错误色"""
|
||||
|
||||
neutral: NeutralColor
|
||||
"""中性色"""
|
||||
|
||||
|
||||
BUILTIN_DEFAULT_COLORS = ThemeColorsBase(
|
||||
primary=ChromaticColor.GREEN,
|
||||
secondary=ChromaticColor.BLUE,
|
||||
success=ChromaticColor.GREEN,
|
||||
info=ChromaticColor.BLUE,
|
||||
warning=ChromaticColor.YELLOW,
|
||||
error=ChromaticColor.RED,
|
||||
neutral=NeutralColor.ZINC,
|
||||
)
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -5,7 +5,7 @@ from uuid import UUID
|
||||
|
||||
from enum import StrEnum
|
||||
from sqlalchemy import BigInteger
|
||||
from sqlmodel import Field, Relationship, UniqueConstraint, CheckConstraint, Index, text
|
||||
from sqlmodel import Field, Relationship, CheckConstraint, Index, text
|
||||
|
||||
from .base import SQLModelBase
|
||||
from .mixin import UUIDTableBaseMixin
|
||||
@@ -195,8 +195,13 @@ class Object(ObjectBase, UUIDTableBaseMixin):
|
||||
"""
|
||||
|
||||
__table_args__ = (
|
||||
# 同一父目录下名称唯一(包括 parent_id 为 NULL 的情况)
|
||||
UniqueConstraint("owner_id", "parent_id", "name", name="uq_object_parent_name"),
|
||||
# 同一父目录下名称唯一(仅对未删除记录生效)
|
||||
Index(
|
||||
"uq_object_parent_name_active",
|
||||
"owner_id", "parent_id", "name",
|
||||
unique=True,
|
||||
postgresql_where=text("deleted_at IS NULL"),
|
||||
),
|
||||
# 名称不能包含斜杠(根目录 parent_id IS NULL 除外,因为根目录 name="/")
|
||||
CheckConstraint(
|
||||
"parent_id IS NULL OR (name NOT LIKE '%/%' AND name NOT LIKE '%\\%')",
|
||||
@@ -207,6 +212,8 @@ class Object(ObjectBase, UUIDTableBaseMixin):
|
||||
Index("ix_object_parent_updated", "parent_id", "updated_at"),
|
||||
Index("ix_object_owner_type", "owner_id", "type"),
|
||||
Index("ix_object_owner_size", "owner_id", "size"),
|
||||
# 回收站查询索引
|
||||
Index("ix_object_owner_deleted", "owner_id", "deleted_at"),
|
||||
)
|
||||
|
||||
# ==================== 基础字段 ====================
|
||||
@@ -280,6 +287,18 @@ class Object(ObjectBase, UUIDTableBaseMixin):
|
||||
ban_reason: str | None = Field(default=None, max_length=500)
|
||||
"""封禁原因"""
|
||||
|
||||
# ==================== 软删除相关字段 ====================
|
||||
|
||||
deleted_at: datetime | None = Field(default=None, index=True)
|
||||
"""软删除时间戳,NULL 表示未删除"""
|
||||
|
||||
deleted_original_parent_id: UUID | None = Field(
|
||||
default=None,
|
||||
foreign_key="object.id",
|
||||
ondelete="SET NULL",
|
||||
)
|
||||
"""软删除前的原始父目录UUID(恢复时用于还原位置)"""
|
||||
|
||||
# ==================== 关系 ====================
|
||||
|
||||
owner: "User" = Relationship(
|
||||
@@ -299,13 +318,19 @@ class Object(ObjectBase, UUIDTableBaseMixin):
|
||||
# 自引用关系
|
||||
parent: "Object" = Relationship(
|
||||
back_populates="children",
|
||||
sa_relationship_kwargs={"remote_side": "Object.id"},
|
||||
sa_relationship_kwargs={
|
||||
"remote_side": "Object.id",
|
||||
"foreign_keys": "[Object.parent_id]",
|
||||
},
|
||||
)
|
||||
"""父目录"""
|
||||
|
||||
children: list["Object"] = Relationship(
|
||||
back_populates="parent",
|
||||
sa_relationship_kwargs={"cascade": "all, delete-orphan"}
|
||||
sa_relationship_kwargs={
|
||||
"cascade": "all, delete-orphan",
|
||||
"foreign_keys": "[Object.parent_id]",
|
||||
},
|
||||
)
|
||||
"""子对象(文件和子目录)"""
|
||||
|
||||
@@ -367,7 +392,7 @@ class Object(ObjectBase, UUIDTableBaseMixin):
|
||||
"""
|
||||
return await cls.get(
|
||||
session,
|
||||
(cls.owner_id == user_id) & (cls.parent_id == None)
|
||||
(cls.owner_id == user_id) & (cls.parent_id == None) & (cls.deleted_at == None)
|
||||
)
|
||||
|
||||
@classmethod
|
||||
@@ -416,7 +441,8 @@ class Object(ObjectBase, UUIDTableBaseMixin):
|
||||
session,
|
||||
(cls.owner_id == user_id) &
|
||||
(cls.parent_id == current.id) &
|
||||
(cls.name == part)
|
||||
(cls.name == part) &
|
||||
(cls.deleted_at == None)
|
||||
)
|
||||
|
||||
return current
|
||||
@@ -424,7 +450,23 @@ class Object(ObjectBase, UUIDTableBaseMixin):
|
||||
@classmethod
|
||||
async def get_children(cls, session, user_id: UUID, parent_id: UUID) -> list["Object"]:
|
||||
"""
|
||||
获取目录下的所有子对象
|
||||
获取目录下的所有子对象(不包含已软删除的)
|
||||
|
||||
:param session: 数据库会话
|
||||
:param user_id: 用户UUID
|
||||
:param parent_id: 父目录UUID
|
||||
:return: 子对象列表
|
||||
"""
|
||||
return await cls.get(
|
||||
session,
|
||||
(cls.owner_id == user_id) & (cls.parent_id == parent_id) & (cls.deleted_at == None),
|
||||
fetch_mode="all"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def get_all_children(cls, session, user_id: UUID, parent_id: UUID) -> list["Object"]:
|
||||
"""
|
||||
获取目录下的所有子对象(包含已软删除的,用于永久删除场景)
|
||||
|
||||
:param session: 数据库会话
|
||||
:param user_id: 用户UUID
|
||||
@@ -437,6 +479,24 @@ class Object(ObjectBase, UUIDTableBaseMixin):
|
||||
fetch_mode="all"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def get_trash_items(cls, session, user_id: UUID) -> list["Object"]:
|
||||
"""
|
||||
获取用户回收站中的顶层对象
|
||||
|
||||
只返回被直接软删除的顶层对象(deleted_at 非 NULL),
|
||||
不返回其子对象(子对象的 deleted_at 为 NULL,通过 parent 关系间接处于回收站中)。
|
||||
|
||||
:param session: 数据库会话
|
||||
:param user_id: 用户UUID
|
||||
:return: 回收站顶层对象列表
|
||||
"""
|
||||
return await cls.get(
|
||||
session,
|
||||
(cls.owner_id == user_id) & (cls.deleted_at != None),
|
||||
fetch_mode="all"
|
||||
)
|
||||
|
||||
@classmethod
|
||||
async def resolve_uri(
|
||||
cls,
|
||||
@@ -805,3 +865,41 @@ class AdminFileListResponse(SQLModelBase):
|
||||
|
||||
total: int = 0
|
||||
"""总数"""
|
||||
|
||||
|
||||
# ==================== 回收站相关 DTO ====================
|
||||
|
||||
class TrashItemResponse(SQLModelBase):
|
||||
"""回收站对象响应 DTO"""
|
||||
|
||||
id: UUID
|
||||
"""对象UUID"""
|
||||
|
||||
name: str
|
||||
"""对象名称"""
|
||||
|
||||
type: ObjectType
|
||||
"""对象类型"""
|
||||
|
||||
size: int
|
||||
"""文件大小(字节)"""
|
||||
|
||||
deleted_at: datetime
|
||||
"""删除时间"""
|
||||
|
||||
original_parent_id: UUID | None
|
||||
"""原始父目录UUID"""
|
||||
|
||||
|
||||
class TrashRestoreRequest(SQLModelBase):
|
||||
"""恢复对象请求 DTO"""
|
||||
|
||||
ids: list[UUID]
|
||||
"""待恢复对象UUID列表"""
|
||||
|
||||
|
||||
class TrashDeleteRequest(SQLModelBase):
|
||||
"""永久删除对象请求 DTO"""
|
||||
|
||||
ids: list[UUID]
|
||||
"""待永久删除对象UUID列表"""
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from enum import StrEnum
|
||||
from typing import TYPE_CHECKING
|
||||
from uuid import UUID
|
||||
|
||||
from sqlmodel import Field, Relationship
|
||||
|
||||
@@ -24,7 +25,7 @@ class Report(SQLModelBase, TableBaseMixin):
|
||||
description: str | None = Field(default=None, max_length=255, description="补充描述")
|
||||
|
||||
# 外键
|
||||
share_id: int = Field(
|
||||
share_id: UUID = Field(
|
||||
foreign_key="share.id",
|
||||
index=True,
|
||||
ondelete="CASCADE"
|
||||
|
||||
@@ -6,7 +6,9 @@ from uuid import UUID
|
||||
from sqlmodel import Field, Relationship, text, UniqueConstraint, Index
|
||||
|
||||
from .base import SQLModelBase
|
||||
from .mixin import TableBaseMixin
|
||||
from .model_base import ResponseBase
|
||||
from .mixin import UUIDTableBaseMixin
|
||||
from .object import ObjectType
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .user import User
|
||||
@@ -34,13 +36,13 @@ class ShareBase(SQLModelBase):
|
||||
preview_enabled: bool = True
|
||||
"""是否允许预览"""
|
||||
|
||||
score: int = 0
|
||||
score: int = Field(default=0, ge=0)
|
||||
"""兑换此分享所需的积分"""
|
||||
|
||||
|
||||
# ==================== 数据库模型 ====================
|
||||
|
||||
class Share(SQLModelBase, TableBaseMixin):
|
||||
class Share(SQLModelBase, UUIDTableBaseMixin):
|
||||
"""分享模型"""
|
||||
|
||||
__table_args__ = (
|
||||
@@ -81,7 +83,7 @@ class Share(SQLModelBase, TableBaseMixin):
|
||||
source_name: str | None = Field(default=None, max_length=255)
|
||||
"""源名称(冗余字段,便于展示)"""
|
||||
|
||||
score: int = Field(default=0, sa_column_kwargs={"server_default": "0"})
|
||||
score: int = Field(default=0, ge=0)
|
||||
"""兑换此分享所需的积分"""
|
||||
|
||||
# 外键
|
||||
@@ -119,10 +121,17 @@ class ShareCreateRequest(ShareBase):
|
||||
pass
|
||||
|
||||
|
||||
class ShareResponse(SQLModelBase):
|
||||
"""分享响应 DTO"""
|
||||
class CreateShareResponse(ResponseBase):
|
||||
"""创建分享响应 DTO"""
|
||||
|
||||
id: int
|
||||
share_id: UUID
|
||||
"""新创建的分享记录 ID"""
|
||||
|
||||
|
||||
class ShareResponse(SQLModelBase):
|
||||
"""查看分享响应 DTO"""
|
||||
|
||||
id: UUID
|
||||
"""分享ID"""
|
||||
|
||||
code: str
|
||||
@@ -162,10 +171,67 @@ class ShareResponse(SQLModelBase):
|
||||
"""是否有密码"""
|
||||
|
||||
|
||||
class ShareOwnerInfo(SQLModelBase):
|
||||
"""分享者公开信息 DTO"""
|
||||
|
||||
nickname: str | None
|
||||
"""昵称"""
|
||||
|
||||
avatar: str
|
||||
"""头像"""
|
||||
|
||||
|
||||
class ShareObjectItem(SQLModelBase):
|
||||
"""分享中的文件/文件夹信息 DTO"""
|
||||
|
||||
id: UUID
|
||||
"""对象UUID"""
|
||||
|
||||
name: str
|
||||
"""名称"""
|
||||
|
||||
type: ObjectType
|
||||
"""类型:file 或 folder"""
|
||||
|
||||
size: int
|
||||
"""文件大小(字节),目录为 0"""
|
||||
|
||||
created_at: datetime
|
||||
"""创建时间"""
|
||||
|
||||
updated_at: datetime
|
||||
"""修改时间"""
|
||||
|
||||
|
||||
class ShareDetailResponse(SQLModelBase):
|
||||
"""获取分享详情响应 DTO(面向访客,隐藏内部统计数据)"""
|
||||
|
||||
expires: datetime | None
|
||||
"""过期时间"""
|
||||
|
||||
preview_enabled: bool
|
||||
"""是否允许预览"""
|
||||
|
||||
score: int
|
||||
"""积分"""
|
||||
|
||||
created_at: datetime
|
||||
"""创建时间"""
|
||||
|
||||
owner: ShareOwnerInfo
|
||||
"""分享者信息"""
|
||||
|
||||
object: ShareObjectItem
|
||||
"""分享的根对象"""
|
||||
|
||||
children: list[ShareObjectItem]
|
||||
"""子文件/文件夹列表(仅目录分享有内容)"""
|
||||
|
||||
|
||||
class ShareListItemBase(SQLModelBase):
|
||||
"""分享列表项基础字段"""
|
||||
|
||||
id: int
|
||||
id: UUID
|
||||
"""分享ID"""
|
||||
|
||||
code: str
|
||||
|
||||
117
sqlmodels/theme_preset.py
Normal file
117
sqlmodels/theme_preset.py
Normal file
@@ -0,0 +1,117 @@
|
||||
from datetime import datetime
|
||||
from uuid import UUID
|
||||
|
||||
from sqlmodel import Field
|
||||
|
||||
from .base import SQLModelBase
|
||||
from .color import ChromaticColor, NeutralColor, ThemeColorsBase
|
||||
from .mixin import UUIDTableBaseMixin
|
||||
|
||||
|
||||
class ThemePresetBase(SQLModelBase):
|
||||
"""主题预设基础字段"""
|
||||
|
||||
name: str = Field(max_length=100)
|
||||
"""预设名称"""
|
||||
|
||||
is_default: bool = False
|
||||
"""是否为默认预设"""
|
||||
|
||||
primary: ChromaticColor
|
||||
"""主色调"""
|
||||
|
||||
secondary: ChromaticColor
|
||||
"""辅助色"""
|
||||
|
||||
success: ChromaticColor
|
||||
"""成功色"""
|
||||
|
||||
info: ChromaticColor
|
||||
"""信息色"""
|
||||
|
||||
warning: ChromaticColor
|
||||
"""警告色"""
|
||||
|
||||
error: ChromaticColor
|
||||
"""错误色"""
|
||||
|
||||
neutral: NeutralColor
|
||||
"""中性色"""
|
||||
|
||||
|
||||
class ThemePreset(ThemePresetBase, UUIDTableBaseMixin):
|
||||
"""主题预设表"""
|
||||
|
||||
name: str = Field(max_length=100, unique=True)
|
||||
"""预设名称(唯一约束)"""
|
||||
|
||||
|
||||
# ==================== DTO ====================
|
||||
|
||||
class ThemePresetCreateRequest(SQLModelBase):
|
||||
"""创建主题预设请求 DTO"""
|
||||
|
||||
name: str = Field(max_length=100)
|
||||
"""预设名称"""
|
||||
|
||||
colors: ThemeColorsBase
|
||||
"""颜色配置"""
|
||||
|
||||
|
||||
class ThemePresetUpdateRequest(SQLModelBase):
|
||||
"""更新主题预设请求 DTO"""
|
||||
|
||||
name: str | None = Field(default=None, max_length=100)
|
||||
"""预设名称(可选)"""
|
||||
|
||||
colors: ThemeColorsBase | None = None
|
||||
"""颜色配置(可选)"""
|
||||
|
||||
|
||||
class ThemePresetResponse(SQLModelBase):
|
||||
"""主题预设响应 DTO"""
|
||||
|
||||
id: UUID
|
||||
"""预设UUID"""
|
||||
|
||||
name: str
|
||||
"""预设名称"""
|
||||
|
||||
is_default: bool
|
||||
"""是否为默认预设"""
|
||||
|
||||
colors: ThemeColorsBase
|
||||
"""颜色配置"""
|
||||
|
||||
created_at: datetime
|
||||
"""创建时间"""
|
||||
|
||||
updated_at: datetime
|
||||
"""更新时间"""
|
||||
|
||||
@classmethod
|
||||
def from_preset(cls, preset: ThemePreset) -> 'ThemePresetResponse':
|
||||
"""从数据库模型转换为响应 DTO(平铺列 → 嵌套 colors 对象)"""
|
||||
return cls(
|
||||
id=preset.id,
|
||||
name=preset.name,
|
||||
is_default=preset.is_default,
|
||||
colors=ThemeColorsBase(
|
||||
primary=preset.primary,
|
||||
secondary=preset.secondary,
|
||||
success=preset.success,
|
||||
info=preset.info,
|
||||
warning=preset.warning,
|
||||
error=preset.error,
|
||||
neutral=preset.neutral,
|
||||
),
|
||||
created_at=preset.created_at,
|
||||
updated_at=preset.updated_at,
|
||||
)
|
||||
|
||||
|
||||
class ThemePresetListResponse(SQLModelBase):
|
||||
"""主题预设列表响应 DTO"""
|
||||
|
||||
themes: list[ThemePresetResponse]
|
||||
"""主题预设列表"""
|
||||
@@ -10,6 +10,7 @@ from sqlmodel.ext.asyncio.session import AsyncSession
|
||||
from sqlmodel.main import RelationshipInfo
|
||||
|
||||
from .base import SQLModelBase
|
||||
from .color import ChromaticColor, NeutralColor, ThemeColorsBase
|
||||
from .model_base import ResponseBase
|
||||
from .mixin import UUIDTableBaseMixin, TableViewRequest, ListResponse
|
||||
|
||||
@@ -34,13 +35,6 @@ class AvatarType(StrEnum):
|
||||
GRAVATAR = "gravatar"
|
||||
FILE = "file"
|
||||
|
||||
class ThemeType(StrEnum):
|
||||
"""主题类型枚举"""
|
||||
|
||||
LIGHT = "light"
|
||||
DARK = "dark"
|
||||
SYSTEM = "system"
|
||||
|
||||
class UserStatus(StrEnum):
|
||||
"""用户状态枚举"""
|
||||
|
||||
@@ -99,7 +93,7 @@ class LoginRequest(SQLModelBase):
|
||||
captcha: str | None = None
|
||||
"""验证码"""
|
||||
|
||||
two_fa_code: int | None = Field(default=None, min_length=6, max_length=6)
|
||||
two_fa_code: str | None = Field(default=None, min_length=6, max_length=6)
|
||||
"""两步验证代码"""
|
||||
|
||||
|
||||
@@ -297,6 +291,29 @@ class UserSettingResponse(SQLModelBase):
|
||||
two_factor: bool = False
|
||||
"""是否启用两步验证"""
|
||||
|
||||
theme_preset_id: UUID | None = None
|
||||
"""选用的主题预设UUID"""
|
||||
|
||||
theme_colors: ThemeColorsBase | None = None
|
||||
"""当前生效的颜色配置"""
|
||||
|
||||
|
||||
class UserThemeUpdateRequest(SQLModelBase):
|
||||
"""用户更新主题请求 DTO"""
|
||||
|
||||
theme_preset_id: UUID | None = None
|
||||
"""主题预设UUID"""
|
||||
|
||||
theme_colors: ThemeColorsBase | None = None
|
||||
"""颜色配置"""
|
||||
|
||||
|
||||
class UserTwoFactorResponse(SQLModelBase):
|
||||
"""用户两步验证信息 DTO"""
|
||||
|
||||
two_factor_key: str
|
||||
"""两步验证密钥"""
|
||||
|
||||
|
||||
# ==================== 管理员用户管理 DTO ====================
|
||||
|
||||
@@ -428,8 +445,31 @@ class User(UserBase, UUIDTableBaseMixin):
|
||||
"""当前用户组过期时间"""
|
||||
|
||||
# Option 相关字段
|
||||
# theme: ThemeType = Field(default=ThemeType.SYSTEM)
|
||||
# """主题类型: light/dark/system"""
|
||||
theme_preset_id: UUID | None = Field(
|
||||
default=None, foreign_key="themepreset.id", ondelete="SET NULL"
|
||||
)
|
||||
"""选用的主题预设UUID"""
|
||||
|
||||
color_primary: ChromaticColor | None = None
|
||||
"""颜色快照:主色调"""
|
||||
|
||||
color_secondary: ChromaticColor | None = None
|
||||
"""颜色快照:辅助色"""
|
||||
|
||||
color_success: ChromaticColor | None = None
|
||||
"""颜色快照:成功色"""
|
||||
|
||||
color_info: ChromaticColor | None = None
|
||||
"""颜色快照:信息色"""
|
||||
|
||||
color_warning: ChromaticColor | None = None
|
||||
"""颜色快照:警告色"""
|
||||
|
||||
color_error: ChromaticColor | None = None
|
||||
"""颜色快照:错误色"""
|
||||
|
||||
color_neutral: NeutralColor | None = None
|
||||
"""颜色快照:中性色"""
|
||||
|
||||
language: str = Field(default="zh-CN", max_length=5)
|
||||
"""语言偏好"""
|
||||
|
||||
Reference in New Issue
Block a user