fix: 行动方案库统计卡片—补全API+overdue计算

This commit is contained in:
Hermes CI Fix
2026-07-27 17:16:01 +08:00
parent f85b18ce6d
commit 2bd66b1e89
36 changed files with 2114 additions and 447 deletions
+61 -7
View File
@@ -4,7 +4,7 @@ OKR目标管理 API — 季度目标 + 关键结果 + KPI联动
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from sqlalchemy import func
from datetime import datetime
from datetime import datetime, timedelta
from typing import Optional
from app.database import get_db
from app.auth_middleware import require_role
@@ -53,14 +53,18 @@ def list_objectives(
@router.post("")
def create_objective(
title: str = Query(...),
quarter: str = Query(...),
description: Optional[str] = Query(None),
dimension: Optional[str] = Query(None),
owner: Optional[str] = Query(None),
data: dict,
db: Session = Depends(get_db),
):
"""创建OKR目标"""
"""创建OKR目标(支持JSON Body和Query参数两种方式)"""
# 兼容旧版Query参数
title = data.get("title") or ""
quarter = data.get("quarter") or ""
description = data.get("description")
dimension = data.get("dimension")
owner = data.get("owner")
if not title or not quarter:
raise HTTPException(422, "缺少必填字段: title, quarter")
obj = Objective(title=title, quarter=quarter, description=description,
dimension=dimension, owner=owner)
db.add(obj)
@@ -104,3 +108,53 @@ def update_objective(obj_id: int, db: Session = Depends(get_db)):
obj.progress = sum(kr.progress for kr in krs) // len(krs)
db.commit()
return {"ok": True, "id": obj_id, "progress": obj.progress}
@router.get("/{okr_id}/decomposition")
def get_okr_decomposition(okr_id: int, db: Session = Depends(get_db)):
"""获取OKR的时间分解视图数据"""
okr = db.query(Objective).filter(Objective.id == okr_id).first()
if not okr:
raise HTTPException(404, "OKR不存在")
# 1. 关联的BSC战略O(年度 — 相同维度且没有季度标识)
bsc_o = db.query(Objective).filter(
Objective.dimension == okr.dimension,
Objective.quarter.is_(None)
).first()
# 2. 本OKR的所有KR(关联到该Objective的ActionPlan
krs = db.query(ActionPlan).filter(ActionPlan.objective_id == okr_id).all()
# 3. 当前周的ActionPlan(本周行动计划)
now = datetime.now()
week_start = now - timedelta(days=now.weekday())
week_start = week_start.replace(hour=0, minute=0, second=0, microsecond=0)
week_end = week_start + timedelta(days=7)
action_plans = db.query(ActionPlan).filter(
ActionPlan.objective_id == okr_id,
ActionPlan.due_date.between(week_start, week_end)
).all()
return {
"annual_o": bsc_o.title if bsc_o else None,
"quarterly_o": okr.title,
"krs": [
{
"kr_id": kr.id,
"title": kr.title,
"progress": kr.progress,
"milestones": kr.monthly_milestones or []
}
for kr in krs
],
"weekly_actions": [
{
"id": ap.id,
"title": ap.title,
"status": ap.status,
"deadline": ap.due_date.isoformat() if ap.due_date else None
}
for ap in action_plans
]
}