feat(园区端): 园区状态(禁用/启用)+联级删除 — 禁用禁止登录,删除一并删资源
park_tenants 增 status 列(migration)、set_status、/park/tenants/{id}/status 端点;
/ park/auth/login 对被禁用园区返回 403;delete_tenant 联级删 企业/知识库/屏幕+引擎缓存;
_summary 含 status。TestClient:禁用→登录403、启用→200、删除→资源清零。
This commit is contained in:
@@ -80,6 +80,8 @@ async def park_login(body: LoginBody):
|
||||
t = tenants.find_by_admin(body.username.strip())
|
||||
if not t:
|
||||
return JSONResponse({"ok": False, "error": "该账号未绑定任何园区管理员"}, status_code=401)
|
||||
if t.get("status") == "disabled":
|
||||
return JSONResponse({"ok": False, "error": "该园区已禁用,禁止登录使用"}, status_code=403)
|
||||
finally:
|
||||
await db.close()
|
||||
return {"ok": True, "tenant_id": t["id"], "name": t["name"], "intro": t["intro"], "token": create_token(t["id"])}
|
||||
@@ -142,6 +144,18 @@ async def tenant_unbind(tid: str):
|
||||
return {"ok": ok, "tenant_id": tid}
|
||||
|
||||
|
||||
class StatusBody(BaseModel):
|
||||
status: str # active | disabled
|
||||
|
||||
|
||||
@router.post("/tenants/{tid}/status", summary="禁用/启用园区(不删,禁用禁止登录)")
|
||||
async def tenant_status(tid: str, body: StatusBody):
|
||||
ok = tenants.set_status(tid, body.status)
|
||||
if not ok:
|
||||
return JSONResponse({"ok": False, "error": "无效状态或园区不存在"}, status_code=400)
|
||||
return {"ok": True, "tenant_id": tid, "status": body.status}
|
||||
|
||||
|
||||
# ==================== 数据模型 ====================
|
||||
|
||||
class SettingsBody(BaseModel):
|
||||
|
||||
+22
-4
@@ -34,6 +34,7 @@ CREATE TABLE IF NOT EXISTS park_tenants (
|
||||
salt TEXT,
|
||||
password_hash TEXT,
|
||||
admin_username TEXT DEFAULT '', -- 绑定的平台账号(园区管理员,园区端登录用)
|
||||
status TEXT DEFAULT 'active', -- active | disabled(禁用则不删、禁止登录/使用)
|
||||
data_json TEXT,
|
||||
agent_json TEXT,
|
||||
created_at TEXT
|
||||
@@ -86,6 +87,8 @@ def init_db():
|
||||
cols = [r["name"] for r in conn.execute("PRAGMA table_info(park_tenants)").fetchall()]
|
||||
if "admin_username" not in cols:
|
||||
conn.execute("ALTER TABLE park_tenants ADD COLUMN admin_username TEXT DEFAULT ''")
|
||||
if "status" not in cols:
|
||||
conn.execute("ALTER TABLE park_tenants ADD COLUMN status TEXT DEFAULT 'active'")
|
||||
conn.commit()
|
||||
conn.close()
|
||||
ensure_default_tenant()
|
||||
@@ -118,6 +121,7 @@ def _row_to_tenant(row) -> dict:
|
||||
"intro": json.loads(row["intro_json"] or "[]"),
|
||||
"auth": {"username": row["username"], "salt": row["salt"], "password_hash": row["password_hash"]},
|
||||
"admin_username": row["admin_username"] or "",
|
||||
"status": row["status"] if "status" in row.keys() else "active",
|
||||
"data": json.loads(row["data_json"] or "{}"),
|
||||
"agent": json.loads(row["agent_json"] or "{}"),
|
||||
}
|
||||
@@ -222,15 +226,29 @@ def update_tenant(tenant_id: str, patch: dict) -> dict | None:
|
||||
|
||||
|
||||
def delete_tenant(tenant_id: str) -> bool:
|
||||
"""联级删除园区:租户行 + 全部资源(企业/知识库/屏幕/引擎)。"""
|
||||
conn = _conn()
|
||||
cur = conn.execute("DELETE FROM park_tenants WHERE id=?", (tenant_id,))
|
||||
conn.execute("DELETE FROM park_companies WHERE tenant_id=?", (tenant_id,))
|
||||
conn.execute("DELETE FROM park_kb_docs WHERE tenant_id=?", (tenant_id,))
|
||||
conn.execute("DELETE FROM park_screens WHERE tenant_id=?", (tenant_id,))
|
||||
for cid in [c["id"] for c in conn.execute("SELECT id FROM park_tenants").fetchall()]:
|
||||
pass # 引擎缓存后续统一清理
|
||||
conn.commit()
|
||||
conn.close()
|
||||
refresh_engine(tenant_id) # 淘汰缓存引擎
|
||||
from .sim_engine import _engines
|
||||
_engines.pop(tenant_id, None)
|
||||
_engines.pop(tenant_id, None) # 淘汰缓存引擎
|
||||
return cur.rowcount > 0
|
||||
|
||||
|
||||
def set_status(tenant_id: str, status: str) -> bool:
|
||||
"""禁用/启用园区(disabled 不删除,但禁止登录/使用)。"""
|
||||
if status not in ("active", "disabled"):
|
||||
return False
|
||||
conn = _conn()
|
||||
cur = conn.execute("UPDATE park_tenants SET status=? WHERE id=?", (status, tenant_id))
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return cur.rowcount > 0
|
||||
|
||||
|
||||
@@ -266,8 +284,8 @@ def ensure_default_tenant() -> str:
|
||||
return _DEFAULT_TENANT_ID
|
||||
|
||||
|
||||
def _summary(tid: str, name: str, intro: list, username: str, admin_username: str = "") -> dict:
|
||||
return {"id": tid, "name": name, "intro": intro, "username": username, "admin_username": admin_username}
|
||||
def _summary(tid: str, name: str, intro: list, username: str, admin_username: str = "", status: str = "active") -> dict:
|
||||
return {"id": tid, "name": name, "intro": intro, "username": username, "admin_username": admin_username, "status": status}
|
||||
|
||||
|
||||
def _seed_companies(tenant_id: str, companies: list) -> None:
|
||||
|
||||
Reference in New Issue
Block a user