feat: 战略地图收尾 — 自动保存+导出+表迁移+因果链推荐

This commit is contained in:
Hermes CI Fix
2026-07-14 17:32:22 +08:00
parent 5594a572e9
commit 9afcea8f8d
18 changed files with 207 additions and 59 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+55 -7
View File
@@ -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
# ── 战略回顾会 聚合接口 ──────────────────────