mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-28 23:27:10 +00:00
This commit removes the deprecated frontend/web-old directory, updates various project configuration files including tsconfig, vite config, environment variables, and backend data models. It also fixes several API paths, adds tenant awareness to multiple models, and makes minor UI adjustments.
187 lines
6.9 KiB
Python
187 lines
6.9 KiB
Python
from fastapi import Query
|
|
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
|
|
|
|
from app.common.enums import QueueEnum
|
|
from app.core.base_schema import BaseSchema
|
|
from app.core.validator import DateTimeStr
|
|
|
|
|
|
class TenantCreateSchema(BaseModel):
|
|
"""新增租户"""
|
|
|
|
name: str = Field(..., max_length=100, description="租户名称")
|
|
code: str = Field(..., max_length=100, description="租户编码")
|
|
status: str = Field(default="0", description="状态(0:正常 1:禁用)")
|
|
description: str | None = Field(default=None, max_length=255, description="描述")
|
|
start_time: DateTimeStr | None = Field(default=None, description="开始时间")
|
|
end_time: DateTimeStr | None = Field(default=None, description="结束时间")
|
|
contact_name: str | None = Field(default=None, max_length=64, description="联系人姓名")
|
|
contact_phone: str | None = Field(default=None, max_length=20, description="联系人电话")
|
|
contact_email: str | None = Field(default=None, max_length=128, description="联系人邮箱")
|
|
address: str | None = Field(default=None, max_length=255, description="地址")
|
|
domain: str | None = Field(default=None, max_length=255, description="域名")
|
|
logo_url: str | None = Field(default=None, max_length=500, description="Logo URL")
|
|
sort: int = Field(default=0, description="排序")
|
|
|
|
@field_validator("name")
|
|
@classmethod
|
|
def _validate_name(cls, v: str) -> str:
|
|
v = v.strip()
|
|
if not v:
|
|
raise ValueError("名称不能为空")
|
|
return v
|
|
|
|
@field_validator("code")
|
|
@classmethod
|
|
def _validate_code(cls, v: str) -> str:
|
|
v = v.strip()
|
|
if not v:
|
|
raise ValueError("编码不能为空")
|
|
if not v.isalnum():
|
|
raise ValueError("编码只能包含字母和数字")
|
|
return v
|
|
|
|
@model_validator(mode="after")
|
|
def _validate_time_range(self):
|
|
if self.start_time and self.end_time and self.start_time > self.end_time:
|
|
raise ValueError("结束时间不能早于开始时间")
|
|
return self
|
|
|
|
|
|
class TenantUpdateSchema(BaseModel):
|
|
"""更新租户"""
|
|
|
|
name: str | None = Field(default=None, max_length=100, description="租户名称")
|
|
code: str | None = Field(default=None, max_length=100, description="租户编码")
|
|
status: str | None = Field(default=None, description="状态(0:正常 1:禁用)")
|
|
description: str | None = Field(default=None, max_length=255, description="描述")
|
|
start_time: DateTimeStr | None = Field(default=None, description="开始时间")
|
|
end_time: DateTimeStr | None = Field(default=None, description="结束时间")
|
|
contact_name: str | None = Field(default=None, max_length=64, description="联系人姓名")
|
|
contact_phone: str | None = Field(default=None, max_length=20, description="联系人电话")
|
|
contact_email: str | None = Field(default=None, max_length=128, description="联系人邮箱")
|
|
address: str | None = Field(default=None, max_length=255, description="地址")
|
|
domain: str | None = Field(default=None, max_length=255, description="域名")
|
|
logo_url: str | None = Field(default=None, max_length=500, description="Logo URL")
|
|
sort: int | None = Field(default=None, description="排序")
|
|
|
|
@field_validator("code")
|
|
@classmethod
|
|
def _validate_code(cls, v: str | None) -> str | None:
|
|
if v is None:
|
|
return v
|
|
v = v.strip()
|
|
if not v.isalnum():
|
|
raise ValueError("编码只能包含字母和数字")
|
|
return v
|
|
|
|
@model_validator(mode="after")
|
|
def _validate_time_range(self):
|
|
if self.start_time and self.end_time and self.start_time > self.end_time:
|
|
raise ValueError("结束时间不能早于开始时间")
|
|
return self
|
|
|
|
|
|
class TenantOutSchema(TenantCreateSchema, BaseSchema):
|
|
"""租户响应"""
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
|
|
class TenantQueryParam:
|
|
"""租户查询参数"""
|
|
|
|
def __init__(
|
|
self,
|
|
name: str | None = Query(None, description="租户名称"),
|
|
code: str | None = Query(None, description="租户编码"),
|
|
status: str | None = Query(None, description="状态"),
|
|
created_time: list[DateTimeStr] | None = Query(
|
|
None,
|
|
description="创建时间范围",
|
|
examples=["2025-01-01 00:00:00", "2025-12-31 23:59:59"],
|
|
),
|
|
) -> None:
|
|
if name:
|
|
self.name = (QueueEnum.like.value, name)
|
|
if code:
|
|
self.code = (QueueEnum.like.value, code)
|
|
if status:
|
|
self.status = (QueueEnum.eq.value, status)
|
|
if created_time and len(created_time) == 2:
|
|
self.created_time = (QueueEnum.between.value, (created_time[0], created_time[1]))
|
|
|
|
|
|
class TenantUserAddSchema(BaseModel):
|
|
"""向租户添加用户"""
|
|
|
|
user_id: int = Field(..., description="用户ID")
|
|
role: str = Field(default="member", description="租户内角色(owner/admin/member)")
|
|
is_default: int = Field(default=0, description="是否默认租户(0:否 1:是)")
|
|
|
|
|
|
class TenantUserOutSchema(BaseModel):
|
|
"""租户用户响应"""
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
id: int = Field(..., description="关联ID")
|
|
user_id: int = Field(..., description="用户ID")
|
|
tenant_id: int = Field(..., description="租户ID")
|
|
role: str = Field(..., description="租户内角色")
|
|
is_default: int = Field(..., description="是否默认租户")
|
|
create_time: DateTimeStr | None = Field(default=None, description="创建时间")
|
|
username: str = Field(default="", description="用户名")
|
|
name: str = Field(default="", description="用户姓名")
|
|
|
|
|
|
# ============ P1: 配额管理 ============
|
|
|
|
class TenantQuotaOutSchema(BaseModel):
|
|
"""租户配额响应"""
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
id: int
|
|
tenant_id: int
|
|
max_users: int
|
|
max_roles: int
|
|
max_storage_mb: int
|
|
max_depts: int
|
|
|
|
|
|
class TenantQuotaUpdateSchema(BaseModel):
|
|
"""租户配额更新"""
|
|
|
|
max_users: int | None = Field(default=None, ge=0, description="最大用户数")
|
|
max_roles: int | None = Field(default=None, ge=0, description="最大角色数")
|
|
max_storage_mb: int | None = Field(default=None, ge=0, description="最大存储(MB)")
|
|
max_depts: int | None = Field(default=None, ge=0, description="最大部门数")
|
|
|
|
|
|
# ============ P1: 租户配置 ============
|
|
|
|
class TenantConfigItem(BaseModel):
|
|
"""单个配置项"""
|
|
|
|
config_key: str = Field(..., description="配置键")
|
|
config_value: str = Field(..., description="配置值")
|
|
config_type: str = Field(default="string", description="配置类型")
|
|
|
|
|
|
class TenantConfigOutSchema(TenantConfigItem):
|
|
"""租户配置响应"""
|
|
|
|
model_config = ConfigDict(from_attributes=True)
|
|
|
|
id: int
|
|
tenant_id: int
|
|
|
|
|
|
# ============ P1: 租户菜单 ============
|
|
|
|
class TenantMenuSetSchema(BaseModel):
|
|
"""批量设置租户菜单权限"""
|
|
|
|
menu_ids: list[int] = Field(..., description="菜单ID列表")
|