"""战略地图目标 API""" from fastapi import APIRouter, Depends, HTTPException from sqlalchemy.orm import Session from app.database import get_db from app.deps import get_entity_id from app.auth_middleware import require_role from app.models import MapObjective, StrategicMap, KPIDefinition router = APIRouter(prefix="/api/cma/maps", tags=["战略地图目标"], dependencies=[Depends(require_role("ceo", "finance"))], ) @router.get("/{map_id}/objectives") def list_objectives(map_id: int, db: Session = Depends(get_db), entity_id: int = Depends(get_entity_id)): """获取某地图下的所有目标(账套隔离: 跨企业返回空, OpenCode执行层发现)""" m = db.query(StrategicMap).filter(StrategicMap.id == map_id, StrategicMap.entity_id == entity_id).first() if not m: return {"data": []} objs = db.query(MapObjective).filter( MapObjective.map_id == map_id ).order_by(MapObjective.sort_order).all() return {"data": [_obj_to_dict(o) for o in objs]} @router.post("/{map_id}/objectives") def create_objective(map_id: int, data: dict, db: Session = Depends(get_db), entity_id: int = Depends(get_entity_id)): """新增目标""" m = db.query(StrategicMap).filter(StrategicMap.id == map_id, StrategicMap.entity_id == entity_id).first() if not m: raise HTTPException(404, "战略地图不存在") obj = MapObjective( map_id=map_id, dimension_key=data["dimension_key"], name=data["name"], description=data.get("description"), icon=data.get("icon", "target"), sort_order=data.get("sort_order", 0), ) db.add(obj) db.commit() db.refresh(obj) return _obj_to_dict(obj) @router.put("/{map_id}/objectives/{obj_id}") def update_objective(map_id: int, obj_id: int, data: dict, db: Session = Depends(get_db), entity_id: int = Depends(get_entity_id)): """修改目标(账套隔离 + 字段白名单, OpenCode审查#8)""" m = db.query(StrategicMap).filter(StrategicMap.id == map_id, StrategicMap.entity_id == entity_id).first() if not m: raise HTTPException(404, "战略地图不存在") obj = db.query(MapObjective).filter( MapObjective.id == obj_id, MapObjective.map_id == map_id ).first() if not obj: raise HTTPException(404, "目标不存在") ALLOWED_OBJ_FIELDS = {"name", "description", "icon", "sort_order", "kpis", "dimension_key"} for k, v in data.items(): if k in ALLOWED_OBJ_FIELDS and hasattr(obj, k) and v is not None: setattr(obj, k, v) db.commit() return _obj_to_dict(obj) @router.delete("/{map_id}/objectives/{obj_id}") def delete_objective(map_id: int, obj_id: int, db: Session = Depends(get_db), entity_id: int = Depends(get_entity_id)): """删除目标(账套隔离)""" m = db.query(StrategicMap).filter(StrategicMap.id == map_id, StrategicMap.entity_id == entity_id).first() if not m: raise HTTPException(404, "战略地图不存在") obj = db.query(MapObjective).filter( MapObjective.id == obj_id, MapObjective.map_id == map_id ).first() if not obj: raise HTTPException(404, "目标不存在") db.delete(obj) db.commit() return {"message": "已删除"} @router.put("/{map_id}/objectives/sort") def sort_objectives(map_id: int, data: dict, db: Session = Depends(get_db), entity_id: int = Depends(get_entity_id)): """批量排序: {"ids": [3, 1, 2]}(账套隔离)""" m = db.query(StrategicMap).filter(StrategicMap.id == map_id, StrategicMap.entity_id == entity_id).first() if not m: raise HTTPException(404, "战略地图不存在") ids = data.get("ids", []) for idx, obj_id in enumerate(ids): db.query(MapObjective).filter( MapObjective.id == obj_id, MapObjective.map_id == map_id ).update({"sort_order": idx}) db.commit() return {"message": "排序已更新"} def _obj_to_dict(o): return {c.name: getattr(o, c.name) for c in o.__table__.columns}