1
This commit is contained in:
67
alembic/versions/010_desktop_config_name_oem_unique.py
Normal file
67
alembic/versions/010_desktop_config_name_oem_unique.py
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
"""desktop_configs: oem_id + unique (name, oem_id)
|
||||||
|
|
||||||
|
Revision ID: 010
|
||||||
|
Revises: 009
|
||||||
|
Create Date: 2026-05-21
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
|
revision: str = "010"
|
||||||
|
down_revision: Union[str, None] = "009"
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def _drop_name_unique(conn) -> None:
|
||||||
|
inspector = sa.inspect(conn)
|
||||||
|
for uc in inspector.get_unique_constraints("desktop_configs"):
|
||||||
|
cols = uc.get("column_names") or []
|
||||||
|
if cols == ["name"]:
|
||||||
|
op.drop_constraint(uc["name"], "desktop_configs", type_="unique")
|
||||||
|
return
|
||||||
|
|
||||||
|
for idx in inspector.get_indexes("desktop_configs"):
|
||||||
|
if idx.get("column_names") == ["name"] and idx.get("unique"):
|
||||||
|
op.drop_index(idx["name"], table_name="desktop_configs")
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
conn = op.get_bind()
|
||||||
|
inspector = sa.inspect(conn)
|
||||||
|
columns = {c["name"] for c in inspector.get_columns("desktop_configs")}
|
||||||
|
|
||||||
|
if "oem_id" not in columns:
|
||||||
|
op.add_column(
|
||||||
|
"desktop_configs",
|
||||||
|
sa.Column("oem_id", sa.Integer(), nullable=False, server_default="1"),
|
||||||
|
)
|
||||||
|
|
||||||
|
unique_names = {uc["name"] for uc in inspector.get_unique_constraints("desktop_configs")}
|
||||||
|
if "uq_desktop_configs_name_oem_id" not in unique_names:
|
||||||
|
_drop_name_unique(conn)
|
||||||
|
op.create_unique_constraint(
|
||||||
|
"uq_desktop_configs_name_oem_id",
|
||||||
|
"desktop_configs",
|
||||||
|
["name", "oem_id"],
|
||||||
|
)
|
||||||
|
|
||||||
|
inspector = sa.inspect(conn)
|
||||||
|
index_names = {idx["name"] for idx in inspector.get_indexes("desktop_configs")}
|
||||||
|
if "ix_desktop_configs_name" not in index_names:
|
||||||
|
op.create_index("ix_desktop_configs_name", "desktop_configs", ["name"], unique=False)
|
||||||
|
if "ix_desktop_configs_oem_id" not in index_names:
|
||||||
|
op.create_index("ix_desktop_configs_oem_id", "desktop_configs", ["oem_id"], unique=False)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
op.drop_index("ix_desktop_configs_oem_id", table_name="desktop_configs")
|
||||||
|
op.drop_index("ix_desktop_configs_name", table_name="desktop_configs")
|
||||||
|
op.drop_constraint("uq_desktop_configs_name_oem_id", "desktop_configs", type_="unique")
|
||||||
|
op.create_unique_constraint("name", "desktop_configs", ["name"])
|
||||||
|
op.drop_column("desktop_configs", "oem_id")
|
||||||
@@ -9,14 +9,14 @@ from app.schemas.admin_desktop_config import (
|
|||||||
DesktopConfigUpdate,
|
DesktopConfigUpdate,
|
||||||
)
|
)
|
||||||
from app.schemas.common import ApiResponse, PaginatedData
|
from app.schemas.common import ApiResponse, PaginatedData
|
||||||
from app.services import admin_desktop_config as config_service
|
from app.services import oem_desktop_config as config_service
|
||||||
|
|
||||||
router = APIRouter(prefix="/oem/desktop-configs", tags=["管理-桌面配置"])
|
router = APIRouter(prefix="/oem/desktop-configs", tags=["管理-桌面配置"])
|
||||||
|
|
||||||
|
|
||||||
@router.get("", response_model=ApiResponse[PaginatedData[DesktopConfigOut]])
|
@router.get("", response_model=ApiResponse[PaginatedData[DesktopConfigOut]])
|
||||||
async def list_configs(
|
async def list_configs(
|
||||||
_admin: OemUser,
|
_oem: OemUser,
|
||||||
db: DbSession,
|
db: DbSession,
|
||||||
page: int = Query(1, ge=1),
|
page: int = Query(1, ge=1),
|
||||||
page_size: int = Query(
|
page_size: int = Query(
|
||||||
@@ -47,7 +47,7 @@ async def list_configs(
|
|||||||
@router.post("", response_model=ApiResponse[DesktopConfigOut])
|
@router.post("", response_model=ApiResponse[DesktopConfigOut])
|
||||||
async def create_config(
|
async def create_config(
|
||||||
body: DesktopConfigCreate,
|
body: DesktopConfigCreate,
|
||||||
_admin: OemUser,
|
_oem: OemUser,
|
||||||
db: DbSession,
|
db: DbSession,
|
||||||
) -> ApiResponse[DesktopConfigOut]:
|
) -> ApiResponse[DesktopConfigOut]:
|
||||||
try:
|
try:
|
||||||
@@ -66,7 +66,7 @@ async def create_config(
|
|||||||
async def update_config(
|
async def update_config(
|
||||||
config_id: int,
|
config_id: int,
|
||||||
body: DesktopConfigUpdate,
|
body: DesktopConfigUpdate,
|
||||||
_admin: OemUser,
|
_oem: OemUser,
|
||||||
db: DbSession,
|
db: DbSession,
|
||||||
) -> ApiResponse[DesktopConfigOut]:
|
) -> ApiResponse[DesktopConfigOut]:
|
||||||
try:
|
try:
|
||||||
@@ -84,7 +84,7 @@ async def update_config(
|
|||||||
@router.delete("/{config_id}", response_model=ApiResponse[None])
|
@router.delete("/{config_id}", response_model=ApiResponse[None])
|
||||||
async def delete_config(
|
async def delete_config(
|
||||||
config_id: int,
|
config_id: int,
|
||||||
_admin: OemUser,
|
_oem: OemUser,
|
||||||
db: DbSession,
|
db: DbSession,
|
||||||
) -> ApiResponse[None]:
|
) -> ApiResponse[None]:
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ from app.api.v1 import (
|
|||||||
oem_card_keys,
|
oem_card_keys,
|
||||||
agent_card_keys,
|
agent_card_keys,
|
||||||
admin_desktop_configs,
|
admin_desktop_configs,
|
||||||
|
|
||||||
oem_desktop_configs,
|
oem_desktop_configs,
|
||||||
admin_oem_branding,
|
admin_oem_branding,
|
||||||
oem_oem_branding,
|
oem_oem_branding,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from sqlalchemy import DateTime, String, Text, func
|
from sqlalchemy import DateTime, Integer, String, Text, UniqueConstraint, func
|
||||||
from sqlalchemy.orm import Mapped, mapped_column
|
from sqlalchemy.orm import Mapped, mapped_column
|
||||||
|
|
||||||
from app.database import Base
|
from app.database import Base
|
||||||
@@ -8,10 +8,14 @@ from app.database import Base
|
|||||||
|
|
||||||
class DesktopConfig(Base):
|
class DesktopConfig(Base):
|
||||||
__tablename__ = "desktop_configs"
|
__tablename__ = "desktop_configs"
|
||||||
|
__table_args__ = (
|
||||||
|
UniqueConstraint("name", "oem_id", name="uq_desktop_configs_name_oem_id"),
|
||||||
|
)
|
||||||
|
|
||||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||||
name: Mapped[str] = mapped_column(String(64), unique=True)
|
name: Mapped[str] = mapped_column(String(64), index=True)
|
||||||
value: Mapped[str] = mapped_column(Text)
|
value: Mapped[str] = mapped_column(Text)
|
||||||
mark: Mapped[str | None] = mapped_column(String(255), nullable=True, default=None)
|
mark: Mapped[str | None] = mapped_column(String(255), nullable=True, default=None)
|
||||||
|
oem_id: Mapped[int] = mapped_column(Integer, default=1, server_default="1", index=True)
|
||||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||||
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ class DesktopConfigOut(BaseModel):
|
|||||||
name: str
|
name: str
|
||||||
value: str
|
value: str
|
||||||
mark: str | None = None
|
mark: str | None = None
|
||||||
|
oem_id: int = 1
|
||||||
created_at: datetime
|
created_at: datetime
|
||||||
updated_at: datetime
|
updated_at: datetime
|
||||||
|
|
||||||
@@ -18,6 +19,7 @@ class DesktopConfigCreate(BaseModel):
|
|||||||
name: str = Field(min_length=1, max_length=64)
|
name: str = Field(min_length=1, max_length=64)
|
||||||
value: str = Field(min_length=0)
|
value: str = Field(min_length=0)
|
||||||
mark: str | None = Field(default=None, max_length=255)
|
mark: str | None = Field(default=None, max_length=255)
|
||||||
|
oem_id: int = Field(default=1, ge=1)
|
||||||
|
|
||||||
@field_validator("name")
|
@field_validator("name")
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@@ -44,11 +44,21 @@ async def list_configs(
|
|||||||
|
|
||||||
|
|
||||||
async def create_config(db: AsyncSession, body: DesktopConfigCreate) -> DesktopConfig:
|
async def create_config(db: AsyncSession, body: DesktopConfigCreate) -> DesktopConfig:
|
||||||
existing = await db.scalar(select(DesktopConfig).where(DesktopConfig.name == body.name))
|
existing = await db.scalar(
|
||||||
|
select(DesktopConfig).where(
|
||||||
|
DesktopConfig.name == body.name,
|
||||||
|
DesktopConfig.oem_id == body.oem_id,
|
||||||
|
)
|
||||||
|
)
|
||||||
if existing is not None:
|
if existing is not None:
|
||||||
raise AdminDesktopConfigError("配置键名已存在")
|
raise AdminDesktopConfigError("该 OEM 下配置键名已存在")
|
||||||
|
|
||||||
row = DesktopConfig(name=body.name, value=body.value, mark=body.mark)
|
row = DesktopConfig(
|
||||||
|
name=body.name,
|
||||||
|
value=body.value,
|
||||||
|
mark=body.mark,
|
||||||
|
oem_id=body.oem_id,
|
||||||
|
)
|
||||||
db.add(row)
|
db.add(row)
|
||||||
await db.flush()
|
await db.flush()
|
||||||
await db.refresh(row)
|
await db.refresh(row)
|
||||||
@@ -65,9 +75,15 @@ async def update_config(
|
|||||||
raise AdminDesktopConfigError("配置不存在")
|
raise AdminDesktopConfigError("配置不存在")
|
||||||
|
|
||||||
if body.name is not None and body.name != row.name:
|
if body.name is not None and body.name != row.name:
|
||||||
taken = await db.scalar(select(DesktopConfig).where(DesktopConfig.name == body.name))
|
taken = await db.scalar(
|
||||||
|
select(DesktopConfig).where(
|
||||||
|
DesktopConfig.name == body.name,
|
||||||
|
DesktopConfig.oem_id == row.oem_id,
|
||||||
|
DesktopConfig.id != config_id,
|
||||||
|
)
|
||||||
|
)
|
||||||
if taken is not None:
|
if taken is not None:
|
||||||
raise AdminDesktopConfigError("配置键名已存在")
|
raise AdminDesktopConfigError("该 OEM 下配置键名已存在")
|
||||||
row.name = body.name
|
row.name = body.name
|
||||||
|
|
||||||
if body.value is not None:
|
if body.value is not None:
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
|||||||
from app.models.desktopConfig import DesktopConfig
|
from app.models.desktopConfig import DesktopConfig
|
||||||
|
|
||||||
|
|
||||||
async def list_all_as_map(db: AsyncSession) -> dict[str, str]:
|
async def list_all_as_map(db: AsyncSession, oem_id: int = 1) -> dict[str, str]:
|
||||||
result = await db.execute(select(DesktopConfig))
|
result = await db.execute(select(DesktopConfig).where(DesktopConfig.oem_id == oem_id))
|
||||||
rows = result.scalars().all()
|
rows = result.scalars().all()
|
||||||
return {row.name: row.value for row in rows}
|
return {row.name: row.value for row in rows}
|
||||||
|
|||||||
12
app/services/oem_desktop_config.py
Normal file
12
app/services/oem_desktop_config.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
"""desktop_configs 表:键值配置。"""
|
||||||
|
|
||||||
|
from sqlalchemy import select
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
from app.models.desktopConfig import DesktopConfig
|
||||||
|
|
||||||
|
|
||||||
|
async def list_all_as_map(db: AsyncSession,oem_id: int) -> dict[str, str]:
|
||||||
|
result = await db.execute(select(DesktopConfig).where(DesktopConfig.oem_id == oem_id))
|
||||||
|
rows = result.scalars().all()
|
||||||
|
return {row.name: row.value for row in rows}
|
||||||
Reference in New Issue
Block a user