Files
bxh/app/api/areas.py

114 lines
3.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""City areas & responsibility (P3) — list, tree, sync-from-graph, assign."""
from fastapi import APIRouter, HTTPException
from app.auth import CurrentUser
from app.contracts import AreaUpdate
from app.db import list_areas, update_area, upsert_area
from app.api.graph import _get_graph
router = APIRouter()
def _area_level(area_id: str) -> str:
"""Derive admin level from the GB/T 2260 6-digit code."""
if area_id.isdigit() and len(area_id) == 6:
if area_id.endswith("0000"):
return "province"
if area_id.endswith("00"):
return "city"
return "county"
return "poi" # semantic graph areas (e.g. jiaxiu_lou)
def _area_parent(area_id: str, level: str) -> str | None:
if level == "city":
return area_id[:2] + "0000"
if level == "county":
return area_id[:4] + "00"
return None
@router.get("/areas")
async def _list(_user: CurrentUser = None):
return await list_areas()
@router.get("/areas/tree")
async def _tree(_user: CurrentUser = None):
"""省 > 市 > 区县 hierarchy, plus a group for 专题/自定义 areas."""
rows = await list_areas()
by_id = {r["area_id"]: r for r in rows}
def node(r):
return {
"value": r["area_id"],
"title": f'{r["name"]}{r["area_id"]}',
"name": r["name"],
"level": r["level"],
"children": [],
}
nodes = {aid: node(r) for aid, r in by_id.items()}
roots: list = []
specials: list = []
for aid, r in by_id.items():
lvl = r["level"]
if lvl == "province":
roots.append(nodes[aid])
elif lvl in ("city", "county"):
parent = r.get("parent_id")
if parent and parent in nodes:
nodes[parent]["children"].append(nodes[aid])
else:
roots.append(nodes[aid])
else:
specials.append(nodes[aid])
# collapse empty children
def clean(n):
n["children"] = [clean(c) for c in n["children"]]
if not n["children"]:
n.pop("children")
return n
tree = [clean(r) for r in roots]
if specials:
tree.append({
"value": "__special__",
"title": "专题 / 自定义区域",
"selectable": False,
"children": specials,
})
return tree
@router.post("/areas/sync-from-graph")
async def _sync(_user: CurrentUser):
"""Pull Area nodes from the knowledge graph, computing admin levels."""
try:
g = _get_graph()
res = g.query("MATCH (a:Area) RETURN a")
except Exception as e:
raise HTTPException(400, f"读取图谱失败:{str(e)[:200]}")
synced = 0
for row in res.result_set:
node = row[0]
props = getattr(node, "properties", {}) or {}
area_id = str(props.get("area_id") or "").strip()
if not area_id:
continue
name = str(props.get("name") or area_id)
lvl = _area_level(area_id)
await upsert_area(area_id, name, lvl, _area_parent(area_id, lvl))
synced += 1
return {"synced": synced}
@router.patch("/areas/{area_id}")
async def _update(area_id: str, body: AreaUpdate, _user: CurrentUser):
row = await update_area(area_id, body.model_dump(exclude_none=True))
if not row:
raise HTTPException(404, "区域不存在")
return row