Files
next-tjwater-server/app/domain/schemas/admin_metadata.py
T
jiang 26fa5d1909
Generic Container CI/CD / test-build-publish (push) Successful in 3m39s
Server CI/CD v2 / build-test-publish-and-deploy (push) Successful in 3m39s
feat(scada): add project monitoring replay
2026-11-04 14:27:21 +08:00

162 lines
4.5 KiB
Python

from datetime import datetime
from typing import Literal
from uuid import UUID
from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
from app.domain.schemas.project_map_config import ProjectMapConfig, ProjectMapConfigUpdate
from app.domain.schemas.project_settings import ProjectSettings
BusinessRole = Literal["admin", "service", "user"]
ProjectRole = Literal["member", "viewer"]
ProjectStatus = Literal["active", "inactive", "archived"]
ProjectDbRole = Literal["biz_data", "iot_data"]
class MetadataUserSyncRequest(BaseModel):
keycloak_id: UUID
username: str = Field(..., min_length=1, max_length=50)
email: str = Field(..., min_length=1, max_length=100)
role: BusinessRole = "user"
is_active: bool = True
class MetadataUsersBatchSyncRequest(BaseModel):
users: list[MetadataUserSyncRequest] = Field(..., min_length=1, max_length=500)
class MetadataUserUpdateRequest(BaseModel):
role: BusinessRole | None = None
is_active: bool | None = None
class MetadataUserResponse(BaseModel):
id: UUID
keycloak_id: UUID
username: str
email: str
role: str
is_active: bool
is_superuser: bool
created_at: datetime
updated_at: datetime
last_login_at: datetime | None = None
model_config = ConfigDict(from_attributes=True)
class MetadataUserSyncResult(BaseModel):
keycloak_id: UUID
user: MetadataUserResponse | None = None
success: bool
error: str | None = None
class ProjectMemberCreateRequest(BaseModel):
user_id: UUID
project_role: ProjectRole = "viewer"
class ProjectMemberUpdateRequest(BaseModel):
project_role: ProjectRole
class ProjectMemberResponse(BaseModel):
id: UUID
user_id: UUID
project_id: UUID
project_role: str
username: str
email: str
is_active: bool
class AdminProjectCreateRequest(BaseModel):
settings: ProjectSettings = Field(default_factory=ProjectSettings)
name: str = Field(..., min_length=1, max_length=100)
code: str = Field(..., min_length=1, max_length=50)
description: str | None = None
gs_workspace: str = Field(..., min_length=1, max_length=100)
map_extent: dict | None = None
map_config: ProjectMapConfig = Field(default_factory=ProjectMapConfig)
status: ProjectStatus = "active"
class AdminProjectUpdateRequest(ProjectMapConfigUpdate):
settings: ProjectSettings | None = Field(
default=None,
description="按顶层键合并项目配置;省略的键保持原值,嵌套对象整体替换",
)
name: str | None = Field(default=None, min_length=1, max_length=100)
code: str | None = Field(default=None, min_length=1, max_length=50)
description: str | None = None
gs_workspace: str | None = Field(default=None, min_length=1, max_length=100)
map_extent: dict | None = None
status: ProjectStatus | None = None
@field_validator("settings")
@classmethod
def reject_null_settings(cls, value: ProjectSettings | None) -> ProjectSettings:
if value is None:
raise ValueError("settings must be an object")
return value
class AdminProjectResponse(BaseModel):
settings: ProjectSettings = Field(default_factory=ProjectSettings)
project_id: UUID
name: str
code: str
description: str | None = None
gs_workspace: str
map_extent: dict | None = None
map_config: ProjectMapConfig = Field(default_factory=ProjectMapConfig)
status: str
created_at: datetime
updated_at: datetime
class ProjectProvisionResponse(BaseModel):
project: AdminProjectResponse
business_database: str
model_template_database: str
timescale_database: str
geoserver_workspace: str
geoserver_layers: list[str]
class ProjectDatabaseUpsertRequest(BaseModel):
db_role: ProjectDbRole
dsn: str | None = Field(default=None, min_length=1)
pool_min_size: int = Field(default=2, ge=1)
pool_max_size: int = Field(default=10, ge=1)
@model_validator(mode="after")
def validate_pool_bounds(self):
if self.pool_max_size < self.pool_min_size:
raise ValueError("pool_max_size must be greater than or equal to pool_min_size")
return self
class ProjectDatabaseResponse(BaseModel):
id: UUID
project_id: UUID
db_role: str
db_type: str
pool_min_size: int
pool_max_size: int
has_dsn: bool
class ProjectDatabaseHealthRequest(BaseModel):
dsn: str | None = Field(default=None, min_length=1)
class ProjectDatabaseHealthResponse(BaseModel):
project_id: UUID
db_role: str
db_type: str
ok: bool
detail: str