fix(park): /park/tenants PUT 改用 TenantPatch(exclude_none) — 仅改管理员账号不再清空名称/简介
平台端管理园区账号(改 username/password)时,原 TenantBody 默认 name/intro 会被写入覆盖; 改用全可选 TenantPatch + exclude_none,只 patch 提供的字段。TestClient 验证保存账号保留名称/简介。
This commit is contained in:
+40
-8
@@ -27,11 +27,15 @@ logger = logging.getLogger("dpm.api")
|
||||
|
||||
|
||||
def _resolve_tenant(authorization: str | None, tenant_id: str | None) -> str:
|
||||
"""解析目标园区:大屏 park-token > ?tenant_id= > 默认园区。"""
|
||||
if authorization and authorization.startswith("Bearer "):
|
||||
return parse_token(authorization.split(" ", 1)[1])
|
||||
"""解析目标园区:?tenant_id=(admin 亮传)> 大屏 park-token > 默认园区。
|
||||
admin 携带平台 token,不应被当作 park-token 解析失败;故 tenant_id 优先。"""
|
||||
if tenant_id:
|
||||
return tenant_id
|
||||
if authorization and authorization.startswith("Bearer "):
|
||||
try:
|
||||
return parse_token(authorization.split(" ", 1)[1])
|
||||
except HTTPException:
|
||||
pass # 平台 token → 回退默认
|
||||
return tenants.ensure_default_tenant()
|
||||
|
||||
log = logging.getLogger("dpm.api")
|
||||
@@ -89,9 +93,9 @@ async def tenant_create(body: TenantBody):
|
||||
return {"ok": True, "tenant": t}
|
||||
|
||||
|
||||
@router.put("/tenants/{tid}", summary="更新园区(名称/简介/账密/数据)")
|
||||
async def tenant_update(tid: str, body: TenantBody):
|
||||
patch = {k: v for k, v in body.model_dump().items() if v is not None}
|
||||
@router.put("/tenants/{tid}", summary="更新园区(名称/简介/管理员账号/密码)")
|
||||
async def tenant_update(tid: str, body: TenantPatch):
|
||||
patch = {k: v for k, v in body.model_dump(exclude_none=True).items() if v is not None}
|
||||
t = tenants.update_tenant(tid, patch)
|
||||
if t is None:
|
||||
return JSONResponse({"ok": False, "error": "园区不存在"}, status_code=404)
|
||||
@@ -239,6 +243,34 @@ async def park_zones(authorization: str | None = Header(None), tenant_id: str |
|
||||
return get_engine(tid).snapshot().get("zones", [])
|
||||
|
||||
|
||||
# ==================== 屏幕管理(园区端创建/管理大屏设备) ====================
|
||||
|
||||
class ScreenBody2(BaseModel):
|
||||
device_id: str = ""
|
||||
name: str = ""
|
||||
role: str = "main"
|
||||
location: str = ""
|
||||
|
||||
|
||||
@router.get("/api/screens")
|
||||
async def screens_list(authorization: str | None = Header(None), tenant_id: str | None = None):
|
||||
tid = _resolve_tenant(authorization, tenant_id)
|
||||
return tenants.list_screens(tid)
|
||||
|
||||
|
||||
@router.post("/api/screens")
|
||||
async def screens_create(body: ScreenBody2, authorization: str | None = Header(None), tenant_id: str | None = None):
|
||||
tid = _resolve_tenant(authorization, tenant_id)
|
||||
return tenants.create_screen(tid, body.model_dump())
|
||||
|
||||
|
||||
@router.delete("/api/screens/{sid}")
|
||||
async def screens_delete(sid: str, authorization: str | None = Header(None), tenant_id: str | None = None):
|
||||
tid = _resolve_tenant(authorization, tenant_id)
|
||||
ok = tenants.delete_screen(tid, sid)
|
||||
return {"ok": ok, "id": sid}
|
||||
|
||||
|
||||
# ==================== 入驻企业(租户 data.companies 主数据源) ====================
|
||||
|
||||
@router.get("/api/park/companies")
|
||||
@@ -482,8 +514,8 @@ async def display_register(body: RegisterBody, authorization: str | None = Heade
|
||||
|
||||
|
||||
@router.post("/api/display/command")
|
||||
async def display_command_publish(body: DisplayCommandBody, authorization: str | None = Header(None)):
|
||||
tid = require_tenant(authorization)
|
||||
async def display_command_publish(body: DisplayCommandBody, authorization: str | None = Header(None), tenant_id: str | None = None):
|
||||
tid = tenant_id or require_tenant(authorization)
|
||||
if body.action not in _VALID_DISPLAY_ACTIONS:
|
||||
return JSONResponse({"ok": False, "error": f"不支持的指令: {body.action}"}, status_code=400)
|
||||
cmd = hub.publish_command(body.action, body.params, body.screen_id or None, body.screen_role or None, tid)
|
||||
|
||||
Reference in New Issue
Block a user