147 lines
5.6 KiB
Python
147 lines
5.6 KiB
Python
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
|
from sqlalchemy import select
|
|
from sqlalchemy.orm import Session, selectinload
|
|
|
|
from app.database import get_db
|
|
from app.deps import current_user, require_roles
|
|
from app.models import Notice, NoticePoint, Personnel, Role
|
|
from app.schemas import NoticeCreate, NoticeOut, NoticeUpdate, PageResponse
|
|
from app.services.common import paginate
|
|
from app.services.attendance_points import accessible_point_names
|
|
|
|
router = APIRouter(prefix="/api/notices", tags=["notices"])
|
|
|
|
|
|
def _notice_out(notice: Notice) -> NoticeOut:
|
|
point_names = sorted([item.attendance_point_name for item in notice.attendance_points])
|
|
return NoticeOut(
|
|
id=notice.id,
|
|
title=notice.title,
|
|
content=notice.content,
|
|
attendance_point_names=point_names,
|
|
sort_order=notice.sort_order,
|
|
is_active=notice.is_active,
|
|
created_by=notice.created_by,
|
|
creator_name=notice.creator.name if notice.creator else "",
|
|
created_at=notice.created_at,
|
|
updated_at=notice.updated_at,
|
|
)
|
|
|
|
|
|
def _clean_text(value: str, field_name: str) -> str:
|
|
text = value.strip()
|
|
if not text:
|
|
raise HTTPException(status_code=400, detail=f"{field_name}不能为空")
|
|
return text
|
|
|
|
|
|
@router.get("", response_model=PageResponse)
|
|
def list_notices(
|
|
include_inactive: bool = False,
|
|
page: int = Query(1, ge=1),
|
|
page_size: int = Query(10, ge=1, le=100),
|
|
user: Personnel = Depends(current_user),
|
|
db: Session = Depends(get_db),
|
|
) -> PageResponse:
|
|
if include_inactive and user.role not in (Role.admin, Role.manager):
|
|
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="无权限")
|
|
|
|
query = (
|
|
select(Notice)
|
|
.options(selectinload(Notice.creator), selectinload(Notice.attendance_points))
|
|
.order_by(Notice.sort_order.asc(), Notice.updated_at.desc())
|
|
)
|
|
if not include_inactive:
|
|
query = query.where(Notice.is_active.is_(True))
|
|
point_names = accessible_point_names(db, user)
|
|
query = query.where(Notice.attendance_points.any(NoticePoint.attendance_point_name.in_(point_names)))
|
|
|
|
result = paginate(db, query, page, page_size)
|
|
return PageResponse(**{**result, "rows": [_notice_out(row) for row in result["rows"]]})
|
|
|
|
|
|
@router.post("", response_model=NoticeOut)
|
|
def create_notice(
|
|
payload: NoticeCreate,
|
|
user: Personnel = Depends(require_roles(Role.admin, Role.manager)),
|
|
db: Session = Depends(get_db),
|
|
) -> NoticeOut:
|
|
allowed = set(accessible_point_names(db, user))
|
|
point_names = payload.attendance_point_names or list(allowed)
|
|
denied = [name for name in point_names if name not in allowed]
|
|
if denied:
|
|
raise HTTPException(status_code=403, detail=f"无该考勤点通知配置权限:{'、'.join(denied)}")
|
|
notice = Notice(
|
|
title=_clean_text(payload.title, "标题"),
|
|
content=_clean_text(payload.content, "内容"),
|
|
sort_order=payload.sort_order,
|
|
is_active=payload.is_active,
|
|
created_by=user.phone,
|
|
)
|
|
for name in dict.fromkeys(point_names):
|
|
notice.attendance_points.append(NoticePoint(attendance_point_name=name))
|
|
db.add(notice)
|
|
db.commit()
|
|
db.refresh(notice)
|
|
return _notice_out(notice)
|
|
|
|
|
|
@router.put("/{notice_id}", response_model=NoticeOut)
|
|
def update_notice(
|
|
notice_id: int,
|
|
payload: NoticeUpdate,
|
|
user: Personnel = Depends(require_roles(Role.admin, Role.manager)),
|
|
db: Session = Depends(get_db),
|
|
) -> NoticeOut:
|
|
notice = db.scalar(
|
|
select(Notice)
|
|
.options(selectinload(Notice.attendance_points))
|
|
.where(Notice.id == notice_id)
|
|
)
|
|
if notice is None:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="通知不存在")
|
|
allowed = set(accessible_point_names(db, user))
|
|
if user.role == Role.admin and not any(item.attendance_point_name in allowed for item in notice.attendance_points):
|
|
raise HTTPException(status_code=403, detail="无该考勤点通知配置权限")
|
|
|
|
if payload.title is not None:
|
|
notice.title = _clean_text(payload.title, "标题")
|
|
if payload.content is not None:
|
|
notice.content = _clean_text(payload.content, "内容")
|
|
if payload.sort_order is not None:
|
|
notice.sort_order = payload.sort_order
|
|
if payload.is_active is not None:
|
|
notice.is_active = payload.is_active
|
|
if payload.attendance_point_names is not None:
|
|
denied = [name for name in payload.attendance_point_names if name not in allowed]
|
|
if denied:
|
|
raise HTTPException(status_code=403, detail=f"无该考勤点通知配置权限:{'、'.join(denied)}")
|
|
notice.attendance_points.clear()
|
|
db.flush()
|
|
for name in dict.fromkeys(payload.attendance_point_names or list(allowed)):
|
|
notice.attendance_points.append(NoticePoint(attendance_point_name=name))
|
|
|
|
db.commit()
|
|
db.refresh(notice)
|
|
return _notice_out(notice)
|
|
|
|
|
|
@router.delete("/{notice_id}", status_code=204)
|
|
def delete_notice(
|
|
notice_id: int,
|
|
user: Personnel = Depends(require_roles(Role.admin, Role.manager)),
|
|
db: Session = Depends(get_db),
|
|
) -> None:
|
|
notice = db.scalar(
|
|
select(Notice)
|
|
.options(selectinload(Notice.attendance_points))
|
|
.where(Notice.id == notice_id)
|
|
)
|
|
if notice is None:
|
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="通知不存在")
|
|
allowed = set(accessible_point_names(db, user))
|
|
if user.role == Role.admin and not any(item.attendance_point_name in allowed for item in notice.attendance_points):
|
|
raise HTTPException(status_code=403, detail="无该考勤点通知配置权限")
|
|
db.delete(notice)
|
|
db.commit()
|