feat: 战略地图收尾 — 自动保存+导出+表迁移+因果链推荐
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+55
-7
@@ -4,7 +4,7 @@ from sqlalchemy.orm import Session
|
||||
from typing import Optional
|
||||
from app.database import get_db
|
||||
from app.auth_middleware import require_auth, require_role
|
||||
from app.models import StrategicMap, OperationLog
|
||||
from app.models import StrategicMap, OperationLog, MapObjective
|
||||
import json
|
||||
|
||||
router = APIRouter(prefix="/api/cma/maps", tags=["战略地图"],
|
||||
@@ -62,7 +62,7 @@ STRATEGIC_MAP_TEMPLATE = [
|
||||
@router.get("")
|
||||
def list_maps(db: Session = Depends(get_db)):
|
||||
maps = db.query(StrategicMap).order_by(StrategicMap.updated_at.desc()).all()
|
||||
return {"data": [m_to_dict(m) for m in maps]}
|
||||
return {"data": [m_to_dict(m, db) for m in maps]}
|
||||
|
||||
@router.post("")
|
||||
def create_map(data: dict, db: Session = Depends(get_db)):
|
||||
@@ -70,7 +70,8 @@ def create_map(data: dict, db: Session = Depends(get_db)):
|
||||
db.add(m)
|
||||
db.commit()
|
||||
db.refresh(m)
|
||||
return m_to_dict(m)
|
||||
_sync_map_objectives(m, db)
|
||||
return m_to_dict(m, db)
|
||||
|
||||
|
||||
@router.post("/create-with-template")
|
||||
@@ -86,7 +87,8 @@ def create_map_with_template(data: dict, db: Session = Depends(get_db)):
|
||||
db.add(m)
|
||||
db.commit()
|
||||
db.refresh(m)
|
||||
return m_to_dict(m)
|
||||
_sync_map_objectives(m, db)
|
||||
return m_to_dict(m, db)
|
||||
|
||||
|
||||
@router.put("/{map_id}")
|
||||
@@ -101,12 +103,14 @@ def update_map(map_id: int, data: dict, db: Session = Depends(get_db)):
|
||||
setattr(m, k, v)
|
||||
|
||||
db.commit()
|
||||
# 同步目标到map_objectives表
|
||||
_sync_map_objectives(m, db)
|
||||
|
||||
# ├─ 版本管理: draft → published 时自动创建快照
|
||||
if old_status == "draft" and m.status == "published":
|
||||
_auto_snapshot(m, db)
|
||||
|
||||
return m_to_dict(m)
|
||||
return m_to_dict(m, db)
|
||||
|
||||
|
||||
# ── 删除地图 ─────────────────────────────────
|
||||
@@ -259,8 +263,52 @@ def _auto_snapshot(m: StrategicMap, db: Session):
|
||||
|
||||
# ── 工具函数 ─────────────────────────────────
|
||||
|
||||
def m_to_dict(m):
|
||||
return {c.name: getattr(m, c.name) for c in m.__table__.columns}
|
||||
def m_to_dict(m, db: Session = None):
|
||||
d = {c.name: getattr(m, c.name) for c in m.__table__.columns}
|
||||
if db:
|
||||
_merge_map_objectives(m, db)
|
||||
d["dimensions"] = m.dimensions
|
||||
return d
|
||||
|
||||
|
||||
def _sync_map_objectives(m, db):
|
||||
"""保存时:将dimensions JSON中的目标同步到map_objectives表"""
|
||||
db.query(MapObjective).filter(MapObjective.map_id == m.id).delete()
|
||||
dims = m.dimensions
|
||||
if isinstance(dims, str):
|
||||
dims = json.loads(dims)
|
||||
for dim in dims:
|
||||
for i, obj in enumerate(dim.get("objectives", [])):
|
||||
mo = MapObjective(
|
||||
map_id=m.id,
|
||||
dimension_key=dim.get("key", ""),
|
||||
name=obj.get("name", ""),
|
||||
description=obj.get("description", ""),
|
||||
icon=obj.get("icon", "target"),
|
||||
sort_order=i,
|
||||
)
|
||||
db.add(mo)
|
||||
db.commit()
|
||||
|
||||
|
||||
def _merge_map_objectives(m, db):
|
||||
"""读取时:将map_objectives表的数据合并进dimensions JSON"""
|
||||
objs = db.query(MapObjective).filter(MapObjective.map_id == m.id).order_by(MapObjective.sort_order).all()
|
||||
if not objs:
|
||||
return
|
||||
dims = m.dimensions
|
||||
if isinstance(dims, str):
|
||||
dims = json.loads(dims)
|
||||
# 按dimension_key分组
|
||||
from collections import defaultdict
|
||||
grouped = defaultdict(list)
|
||||
for o in objs:
|
||||
grouped[o.dimension_key].append(o)
|
||||
for dim in dims:
|
||||
key = dim.get("key", "")
|
||||
if key in grouped:
|
||||
dim["objectives"] = [{"name": o.name, "description": o.description or "", "icon": o.icon or "target"} for o in grouped[key]]
|
||||
m.dimensions = dims
|
||||
|
||||
|
||||
# ── 战略回顾会 聚合接口 ──────────────────────
|
||||
|
||||
Reference in New Issue
Block a user