Files
shanxia2.0/backend/app/api/leave.py
T
2026-05-20 14:32:07 +08:00

206 lines
6.8 KiB
Python

"""请假申请 API — 工作流测试"""
from __future__ import annotations
import logging
from typing import Optional
from fastapi import APIRouter, Depends, Query
from sqlalchemy.orm import Session
from app.core.menu_guard import require_admin_or_super_admin
from app.core.database import get_db
from app.core.response import success_response, error_response
from app.crud import leave as leave_crud
from app.crud.leave import total_pages
from app.schemas.leave import LeaveCreate, LeaveUpdate
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/web/admin/leave", tags=["请假管理"])
_LEAVE_TYPE_MAP = {"personal": "事假", "sick": "病假", "annual": "年假", "other": "其他"}
def _leave_local_callback(db: Session, business_id: str, event: str) -> None:
if not business_id:
return
if event == "completed":
leave_crud.update_leave_status(db, business_id, "approved")
elif event == "rejected":
leave_crud.update_leave_status(db, business_id, "rejected")
db.commit()
from app.services.workflow_callback import register_local_callback
register_local_callback("/api/admin/leave/callback", _leave_local_callback)
@router.get("")
async def list_leaves(
page: int = Query(1, ge=1),
pageSize: int = Query(10, ge=1, le=100),
keyword: Optional[str] = Query(None),
status: Optional[str] = Query(None),
_user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
tenant_no = _user.get("tenant_no", "")
user_id = _user.get("user_id") or _user.get("id", "")
items, total = leave_crud.list_leaves(
db, page=page, page_size=pageSize, keyword=keyword, status=status,
tenant_no=tenant_no, create_by=user_id,
)
for item in items:
item["leaveTypeLabel"] = _LEAVE_TYPE_MAP.get(item.get("leaveType", ""), item.get("leaveType", ""))
return success_response({
"list": items, "total": total, "page": page, "pageSize": pageSize,
"totalPages": total_pages(total, pageSize),
})
@router.post("")
async def create_leave(
body: LeaveCreate,
_user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
tenant_no = _user.get("tenant_no", "")
user_id = _user.get("user_id") or _user.get("id", "")
dept_id = _user.get("region_id", "")
row = leave_crud.create_leave(
db,
leave_type=body.leaveType,
start_date=body.startDate,
end_date=body.endDate,
days=body.days,
reason=body.reason or "",
tenant_no=tenant_no,
user_id=user_id,
dept_id=dept_id,
remark=body.remark or "",
)
db.commit()
return success_response(leave_crud._row_to_dict(row))
@router.post("/{leave_id}/submit")
async def submit_leave(
leave_id: str,
_user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
from app.models.models import WfProcessDef
from app.services.workflow_engine import WorkflowEngine
tenant_no = _user.get("tenant_no", "")
user_id = _user.get("user_id") or _user.get("id", "")
leave = leave_crud.get_leave(db, leave_id)
if not leave:
return error_response(404, "请假记录不存在")
if leave["status"] not in ("draft", "rejected"):
return error_response(400, "只有草稿或已驳回的请假单可以提交")
defn = db.query(WfProcessDef).filter(
WfProcessDef.process_code == "leave_request",
WfProcessDef.del_flag == 0,
WfProcessDef.status == "1",
).first()
if not defn:
return error_response(400, "未找到请假审批流程(leave_request),请先在流程配置中创建并发布")
variables = {
"days": leave["days"],
"leave_days": leave["days"],
"leaveType": leave["leaveType"],
"applicant": user_id,
}
try:
engine = WorkflowEngine(db)
instance = engine.start_process(
process_def_id=defn.id,
business_type="leave",
business_id=leave_id,
starter_user_id=user_id,
variables=variables,
callback_url="/api/admin/leave/callback",
tenant_no=tenant_no,
user_id=user_id,
)
leave_crud.update_leave_status(db, leave_id, "pending", wf_instance_id=instance.id, user_id=user_id)
db.commit()
return success_response({"instanceId": instance.id, "status": "pending"})
except Exception as e:
db.rollback()
logger.exception("发起请假审批失败: %s", e)
return error_response(500, f"发起审批失败: {e}")
@router.post("/callback")
async def leave_workflow_callback(
body: dict,
db: Session = Depends(get_db),
):
logger.info("请假工作流回调: %s", body)
business_id = body.get("businessId", "")
event = body.get("event", "")
if not business_id:
return success_response({"received": True})
if event == "completed":
leave_crud.update_leave_status(db, business_id, "approved")
elif event == "rejected":
leave_crud.update_leave_status(db, business_id, "rejected")
db.commit()
return success_response({"received": True})
@router.get("/{leave_id}")
async def get_leave(
leave_id: str,
_user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
item = leave_crud.get_leave(db, leave_id)
if not item:
return error_response(404, "请假记录不存在")
item["leaveTypeLabel"] = _LEAVE_TYPE_MAP.get(item.get("leaveType", ""), item.get("leaveType", ""))
return success_response(item)
@router.delete("/{leave_id}")
async def delete_leave(
leave_id: str,
_user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
user_id = _user.get("user_id") or _user.get("id", "")
leave = leave_crud.get_leave(db, leave_id)
if not leave:
return error_response(404, "请假记录不存在")
if leave["status"] == "pending":
return error_response(400, "审批中的请假单不能删除")
leave_crud.soft_delete_leave(db, leave_id, user_id=user_id)
db.commit()
return success_response(None)
@router.put("/{leave_id}")
async def update_leave(
leave_id: str,
body: LeaveUpdate,
_user: dict = Depends(require_admin_or_super_admin),
db: Session = Depends(get_db),
):
user_id = _user.get("user_id") or _user.get("id", "")
leave = leave_crud.get_leave(db, leave_id)
if not leave:
return error_response(404, "请假记录不存在")
if leave["status"] not in ("draft", "rejected"):
return error_response(400, "只有草稿或已驳回状态可以编辑")
leave_crud.update_leave(db, leave_id, user_id=user_id, **body.model_dump(exclude_none=True))
db.commit()
return success_response(leave_crud.get_leave(db, leave_id))