fix: 行动方案库统计卡片—补全API+overdue计算
This commit is contained in:
+61
-7
@@ -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
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user