Files
qiji/backend/app/api/work_plans.py
T
v6ole 6504a393e3 feat: 个人用户聚合视图 — 客户管理双Tab+四模块数据聚合
- customers 表新增 customer_type 列 (unit/individual)
- 客户管理页:单位客户 | 个人用户 Tab 切换
- 个人用户 Tab: 直接内嵌4 Tab 显示所有个人用户的聚合数据
- 个人用户通过业务模块快速新建产生 (customer_type=individual)
- 4个业务API新增 customer_type 筛选参数(JOIN customers)
- 客户列表排序: 个人用户置顶

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-12 22:14:44 +08:00

134 lines
5.2 KiB
Python

import uuid
from datetime import date
from typing import Optional
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from app.database import get_db
from app.middleware.auth import get_current_user, require_any_role
from app.utils.timezone import parse_date
from app.utils.edit_log import compute_diff, append_entry, init_entry
from app.models.work_plan import WorkPlan
from app.models.customer import Customer
from app.models.user import User
from app.schemas.work_plan import WorkPlanCreate, WorkPlanUpdate, WorkPlanOut
router = APIRouter(prefix="/work-plans", tags=["WorkPlans"])
async def _enrich(wp: WorkPlan, db: AsyncSession) -> dict:
cust = await db.execute(select(Customer.name).where(Customer.id == wp.customer_id))
mgr = await db.execute(select(User.name).where(User.id == wp.manager_id))
return {
"id": str(wp.id),
"customer_id": str(wp.customer_id),
"customer_name": cust.scalar_one_or_none(),
"plan_content": wp.plan_content,
"plan_date": wp.plan_date,
"manager_id": str(wp.manager_id),
"manager_name": mgr.scalar_one_or_none(),
"status": wp.status,
"edit_log": wp.edit_log or [],
}
@router.get("/")
async def list_work_plans(
customer_id: Optional[str] = Query(None),
customer_type: Optional[str] = Query(None),
current_user: dict = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
query = select(WorkPlan)
if current_user["role"] == "manager":
query = query.where(WorkPlan.manager_id == uuid.UUID(current_user["user_id"]))
if customer_id:
query = query.where(WorkPlan.customer_id == uuid.UUID(customer_id))
if customer_type:
query = query.join(Customer, WorkPlan.customer_id == Customer.id).where(Customer.customer_type == customer_type)
query = query.order_by(WorkPlan.plan_date.desc()).limit(200)
result = await db.execute(query)
return [await _enrich(w, db) for w in result.scalars().all()]
@router.get("/{item_id}")
async def get_work_plan(
item_id: str,
current_user: dict = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(select(WorkPlan).where(WorkPlan.id == item_id))
wp = result.scalar_one_or_none()
if not wp:
raise HTTPException(status_code=404, detail="Not found")
if current_user["role"] == "manager" and str(wp.manager_id) != current_user["user_id"]:
raise HTTPException(status_code=403, detail="Access denied")
return await _enrich(wp, db)
@router.post("/")
async def create_work_plan(
data: WorkPlanCreate,
current_user: dict = Depends(require_any_role),
db: AsyncSession = Depends(get_db),
):
wp = WorkPlan(
customer_id=data.customer_id,
plan_content=data.plan_content,
plan_date=parse_date(data.plan_date),
manager_id=data.manager_id if (current_user["role"] in ("director", "leader") and data.manager_id) else uuid.UUID(current_user["user_id"]),
status=data.status,
)
init_entry(wp, current_user["name"])
db.add(wp)
await db.commit()
await db.refresh(wp)
return await _enrich(wp, db)
@router.put("/{plan_id}")
async def update_work_plan(
plan_id: str, data: WorkPlanUpdate,
current_user: dict = Depends(require_any_role),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(select(WorkPlan).where(WorkPlan.id == plan_id))
wp = result.scalar_one_or_none()
if not wp:
raise HTTPException(status_code=404, detail="Not found")
if current_user["role"] == "manager" and str(wp.manager_id) != current_user["user_id"]:
raise HTTPException(status_code=403, detail="Access denied")
old_snapshot = {"customer_id": str(wp.customer_id), "plan_content": wp.plan_content, "plan_date": str(wp.plan_date), "status": wp.status}
update_data = data.model_dump(exclude_unset=True)
if current_user["role"] not in ("director", "leader"):
update_data.pop("manager_id", None)
if "plan_date" in update_data and update_data["plan_date"]:
update_data["plan_date"] = parse_date(update_data["plan_date"])
for k, v in update_data.items():
setattr(wp, k, v)
new_snapshot = {"customer_id": str(wp.customer_id), "plan_content": wp.plan_content, "plan_date": str(wp.plan_date), "status": wp.status}
changes = compute_diff(old_snapshot, new_snapshot)
if changes:
append_entry(wp, current_user["name"], changes, getattr(data, "edit_reason", None))
await db.commit()
await db.refresh(wp)
return await _enrich(wp, db)
@router.delete("/{plan_id}")
async def delete_work_plan(
plan_id: str,
current_user: dict = Depends(require_any_role),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(select(WorkPlan).where(WorkPlan.id == plan_id))
wp = result.scalar_one_or_none()
if not wp:
raise HTTPException(status_code=404, detail="Not found")
if current_user["role"] == "manager" and str(wp.manager_id) != current_user["user_id"]:
raise HTTPException(status_code=403, detail="Access denied")
await db.delete(wp)
await db.commit()
return {"detail": "deleted"}