Files
qiji/backend/app/api/daily_notes.py
T
v6ole ab20bc5a1f Merge branch 'main' into develop
# Conflicts:
#	backend/app/api/key_visits.py
#	backend/app/api/mini_business.py
#	backend/app/api/visits.py
#	backend/app/api/work_plans.py
#	backend/app/main.py
#	backend/app/models/__init__.py
#	backend/app/schemas/key_visit.py
#	backend/app/schemas/mini_business.py
#	backend/app/schemas/work_plan.py
#	backend/app/services/light_board.py
#	frontend/src/components/DesktopLayout.vue
#	frontend/src/stores/theme.ts
#	frontend/src/views/desktop/ManagerWorkspace.vue
#	frontend/src/views/desktop/WorkPlans.vue
#	frontend/src/views/mobile/KeyVisitForm.vue
#	frontend/src/views/mobile/LeaveForm.vue
#	frontend/src/views/mobile/PlansList.vue
#	frontend/src/views/mobile/VisitForm.vue
#	frontend/src/views/mobile/WorkPlanForm.vue
2026-08-17 09:31:12 +08:00

155 lines
6.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.models.daily_note import DailyNote
from app.models.user import User
from app.schemas.daily_note import DailyNoteCreate, DailyNoteUpdate, DailyNoteOut
from app.utils.timezone import today_cst, parse_date
from app.utils.edit_log import compute_diff, append_entry, init_entry
from app.utils.audit import log_audit
router = APIRouter(prefix="/daily-notes", tags=["DailyNotes"])
async def _enrich(note: DailyNote, db: AsyncSession) -> dict:
mgr = await db.execute(select(User.name).where(User.id == note.manager_id))
return {
"id": note.id, "manager_id": note.manager_id,
"note_date": note.note_date, "category": note.category,
"content": note.content, "time_range": note.time_range,
"edit_log": note.edit_log or [], "created_at": note.created_at,
"updated_at": note.updated_at, "manager_name": mgr.scalar_one_or_none(),
}
@router.get("/")
async def list_notes(
date_from: Optional[str] = Query(None),
date_to: Optional[str] = Query(None),
search: Optional[str] = Query(None),
current_user: dict = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
query = select(DailyNote)
if current_user["role"] == "manager":
query = query.where(DailyNote.manager_id == uuid.UUID(current_user["user_id"]))
if date_from:
query = query.where(DailyNote.note_date >= parse_date(date_from))
if date_to:
query = query.where(DailyNote.note_date <= parse_date(date_to))
if search:
query = query.where(DailyNote.content.ilike(f"%{search}%"))
query = query.order_by(DailyNote.note_date.desc(), DailyNote.created_at.desc()).limit(100)
result = await db.execute(query)
return [await _enrich(n, db) for n in result.scalars().all()]
@router.get("/today")
async def list_today_notes(
current_user: dict = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
query = select(DailyNote).where(DailyNote.note_date == today_cst())
if current_user["role"] == "manager":
query = query.where(DailyNote.manager_id == uuid.UUID(current_user["user_id"]))
result = await db.execute(query)
notes = [await _enrich(n, db) for n in result.scalars().all()]
return {"count": len(notes), "notes": notes}
@router.get("/{note_id}")
async def get_note(
note_id: str,
current_user: dict = Depends(get_current_user),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(select(DailyNote).where(DailyNote.id == note_id))
note = result.scalar_one_or_none()
if not note:
raise HTTPException(status_code=404, detail="Not found")
if current_user["role"] == "manager" and str(note.manager_id) != current_user["user_id"]:
raise HTTPException(status_code=403, detail="Access denied")
return await _enrich(note, db)
@router.post("/")
async def create_note(
data: DailyNoteCreate,
current_user: dict = Depends(require_any_role),
db: AsyncSession = Depends(get_db),
):
note = DailyNote(
manager_id=data.manager_id if (current_user["role"] in ("director", "leader") and data.manager_id) else uuid.UUID(current_user["user_id"]),
note_date=parse_date(data.note_date),
category=data.category,
content=data.content,
time_range=data.time_range,
)
init_entry(note, current_user["name"])
db.add(note)
await db.commit()
await db.refresh(note)
# Audit log
await log_audit(db, "daily_note", note.id, f"{note.note_date} 纪要", "create", current_user["user_id"], current_user["name"])
await db.commit()
return await _enrich(note, db)
@router.put("/{note_id}")
async def update_note(
note_id: str, data: DailyNoteUpdate,
current_user: dict = Depends(require_any_role),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(select(DailyNote).where(DailyNote.id == note_id))
note = result.scalar_one_or_none()
if not note:
raise HTTPException(status_code=404, detail="Not found")
if current_user["role"] == "manager" and str(note.manager_id) != current_user["user_id"]:
raise HTTPException(status_code=403, detail="Access denied")
old_snapshot = {"note_date": str(note.note_date), "category": note.category, "content": note.content, "time_range": note.time_range}
update_data = data.model_dump(exclude_unset=True)
if current_user["role"] not in ("director", "leader"):
update_data.pop("manager_id", None)
if "note_date" in update_data and update_data["note_date"]:
update_data["note_date"] = parse_date(update_data["note_date"])
for k, v in update_data.items():
setattr(note, k, v)
new_snapshot = {"note_date": str(note.note_date), "category": note.category, "content": note.content, "time_range": note.time_range}
changes = compute_diff(old_snapshot, new_snapshot)
if changes:
append_entry(note, current_user["name"], changes, getattr(data, "edit_reason", None))
await db.commit()
await db.refresh(note)
# Audit log
await log_audit(db, "daily_note", note.id, f"{note.note_date} 纪要", "update", current_user["user_id"], current_user["name"])
await db.commit()
return await _enrich(note, db)
@router.delete("/{note_id}")
async def delete_note(
note_id: str,
current_user: dict = Depends(require_any_role),
db: AsyncSession = Depends(get_db),
):
result = await db.execute(select(DailyNote).where(DailyNote.id == note_id))
note = result.scalar_one_or_none()
if not note:
raise HTTPException(status_code=404, detail="Not found")
if current_user["role"] == "manager" and str(note.manager_id) != current_user["user_id"]:
raise HTTPException(status_code=403, detail="Access denied")
note_date = str(note.note_date)
await db.delete(note)
await db.commit()
# Audit log
await log_audit(db, "daily_note", uuid.UUID(note_id), f"{note_date} 纪要", "delete", current_user["user_id"], current_user["name"])
await db.commit()
return {"detail": "deleted"}