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.
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
|
||||
|
||||
|
||||
# ── 战略回顾会 聚合接口 ──────────────────────
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user