Files
qiji/backend/app/api/daily_notes.py
T
v6ole bf67e0575f feat: 信息架构重组 + 变更追踪 + 图片预览增强
- 信息架构重组: 周报精简为拜访+纪要两个Tab,工作计划/商机/要客独立为侧边栏「工作」分组下的独立页面
- 侧边栏分组: 汇总/工作/管理三层分组,仪表盘四卡可点击跳转
- 变更追踪(edit_log): 5张表新增JSONB edit_log列,POST创建/PUT diff自动记录,编辑弹窗变更时间轴,表格🕐编辑标记
- 图片预览增强: ImagePreview统一组件,支持适应页面/缩放/拖拽平移/滚轮缩放/键盘快捷键
- 修复客户导入500错误(errors变量未初始化)
- 移除工作计划/商机/要客页面冗余编辑按钮

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-24 08:30:12 +08:00

139 lines
5.3 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
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),
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))
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=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)
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 "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)
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")
await db.delete(note)
await db.commit()
return {"detail": "deleted"}