leave.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. """请假申请 API — 工作流测试"""
  2. from __future__ import annotations
  3. import logging
  4. from typing import Optional
  5. from fastapi import APIRouter, Depends, Query
  6. from sqlalchemy.orm import Session
  7. from app.core.menu_guard import require_admin_or_super_admin
  8. from app.core.database import get_db
  9. from app.core.response import success_response, error_response
  10. from app.crud import leave as leave_crud
  11. from app.crud.leave import total_pages
  12. from app.schemas.leave import LeaveCreate, LeaveUpdate
  13. logger = logging.getLogger(__name__)
  14. router = APIRouter(prefix="/web/admin/leave", tags=["请假管理"])
  15. _LEAVE_TYPE_MAP = {"personal": "事假", "sick": "病假", "annual": "年假", "other": "其他"}
  16. def _leave_local_callback(db: Session, business_id: str, event: str) -> None:
  17. if not business_id:
  18. return
  19. if event == "completed":
  20. leave_crud.update_leave_status(db, business_id, "approved")
  21. elif event == "rejected":
  22. leave_crud.update_leave_status(db, business_id, "rejected")
  23. db.commit()
  24. from app.services.workflow_callback import register_local_callback
  25. register_local_callback("/api/admin/leave/callback", _leave_local_callback)
  26. @router.get("")
  27. def list_leaves(
  28. page: int = Query(1, ge=1),
  29. pageSize: int = Query(10, ge=1, le=100),
  30. keyword: Optional[str] = Query(None),
  31. status: Optional[str] = Query(None),
  32. _user: dict = Depends(require_admin_or_super_admin),
  33. db: Session = Depends(get_db),
  34. ):
  35. tenant_no = _user.get("tenant_no", "")
  36. user_id = _user.get("user_id") or _user.get("id", "")
  37. items, total = leave_crud.list_leaves(
  38. db, page=page, page_size=pageSize, keyword=keyword, status=status,
  39. tenant_no=tenant_no, create_by=user_id, user_info=_user,
  40. )
  41. for item in items:
  42. item["leaveTypeLabel"] = _LEAVE_TYPE_MAP.get(item.get("leaveType", ""), item.get("leaveType", ""))
  43. return success_response({
  44. "list": items, "total": total, "page": page, "pageSize": pageSize,
  45. "totalPages": total_pages(total, pageSize),
  46. })
  47. @router.post("")
  48. def create_leave(
  49. body: LeaveCreate,
  50. _user: dict = Depends(require_admin_or_super_admin),
  51. db: Session = Depends(get_db),
  52. ):
  53. tenant_no = _user.get("tenant_no", "")
  54. user_id = _user.get("user_id") or _user.get("id", "")
  55. dept_id = _user.get("region_id", "")
  56. row = leave_crud.create_leave(
  57. db,
  58. leave_type=body.leaveType,
  59. start_date=body.startDate,
  60. end_date=body.endDate,
  61. days=body.days,
  62. reason=body.reason or "",
  63. tenant_no=tenant_no,
  64. user_id=user_id,
  65. dept_id=dept_id,
  66. remark=body.remark or "",
  67. )
  68. db.commit()
  69. return success_response(leave_crud._row_to_dict(row))
  70. @router.post("/{leave_id}/submit")
  71. def submit_leave(
  72. leave_id: str,
  73. _user: dict = Depends(require_admin_or_super_admin),
  74. db: Session = Depends(get_db),
  75. ):
  76. from app.models.models import WfProcessDef
  77. from app.services.workflow_engine import WorkflowEngine
  78. tenant_no = _user.get("tenant_no", "")
  79. user_id = _user.get("user_id") or _user.get("id", "")
  80. leave = leave_crud.get_leave(db, leave_id)
  81. if not leave:
  82. return error_response(404, "请假记录不存在")
  83. if leave["status"] not in ("draft", "rejected"):
  84. return error_response(400, "只有草稿或已驳回的请假单可以提交")
  85. defn = db.query(WfProcessDef).filter(
  86. WfProcessDef.process_code == "leave_request",
  87. WfProcessDef.del_flag == 0,
  88. WfProcessDef.status == "1",
  89. ).first()
  90. if not defn:
  91. return error_response(400, "未找到请假审批流程(leave_request),请先在流程配置中创建并发布")
  92. variables = {
  93. "days": leave["days"],
  94. "leave_days": leave["days"],
  95. "leaveType": leave["leaveType"],
  96. "applicant": user_id,
  97. }
  98. try:
  99. engine = WorkflowEngine(db)
  100. instance = engine.start_process(
  101. process_def_id=defn.id,
  102. business_type="leave",
  103. business_id=leave_id,
  104. starter_user_id=user_id,
  105. variables=variables,
  106. callback_url="/api/admin/leave/callback",
  107. tenant_no=tenant_no,
  108. user_id=user_id,
  109. )
  110. leave_crud.update_leave_status(db, leave_id, "pending", wf_instance_id=instance.id, user_id=user_id)
  111. db.commit()
  112. return success_response({"instanceId": instance.id, "status": "pending"})
  113. except Exception as e:
  114. db.rollback()
  115. logger.exception("发起请假审批失败: %s", e)
  116. return error_response(500, f"发起审批失败: {e}")
  117. @router.post("/callback")
  118. def leave_workflow_callback(
  119. body: dict,
  120. db: Session = Depends(get_db),
  121. ):
  122. logger.info("请假工作流回调: %s", body)
  123. business_id = body.get("businessId", "")
  124. event = body.get("event", "")
  125. if not business_id:
  126. return success_response({"received": True})
  127. if event == "completed":
  128. leave_crud.update_leave_status(db, business_id, "approved")
  129. elif event == "rejected":
  130. leave_crud.update_leave_status(db, business_id, "rejected")
  131. db.commit()
  132. return success_response({"received": True})
  133. @router.get("/{leave_id}")
  134. def get_leave(
  135. leave_id: str,
  136. _user: dict = Depends(require_admin_or_super_admin),
  137. db: Session = Depends(get_db),
  138. ):
  139. item = leave_crud.get_leave(db, leave_id)
  140. if not item:
  141. return error_response(404, "请假记录不存在")
  142. item["leaveTypeLabel"] = _LEAVE_TYPE_MAP.get(item.get("leaveType", ""), item.get("leaveType", ""))
  143. return success_response(item)
  144. @router.delete("/{leave_id}")
  145. def delete_leave(
  146. leave_id: str,
  147. _user: dict = Depends(require_admin_or_super_admin),
  148. db: Session = Depends(get_db),
  149. ):
  150. user_id = _user.get("user_id") or _user.get("id", "")
  151. leave = leave_crud.get_leave(db, leave_id)
  152. if not leave:
  153. return error_response(404, "请假记录不存在")
  154. if leave["status"] == "pending":
  155. return error_response(400, "审批中的请假单不能删除")
  156. leave_crud.soft_delete_leave(db, leave_id, user_id=user_id, user_info=_user)
  157. db.commit()
  158. return success_response(None)
  159. @router.put("/{leave_id}")
  160. def update_leave(
  161. leave_id: str,
  162. body: LeaveUpdate,
  163. _user: dict = Depends(require_admin_or_super_admin),
  164. db: Session = Depends(get_db),
  165. ):
  166. user_id = _user.get("user_id") or _user.get("id", "")
  167. leave = leave_crud.get_leave(db, leave_id)
  168. if not leave:
  169. return error_response(404, "请假记录不存在")
  170. if leave["status"] not in ("draft", "rejected"):
  171. return error_response(400, "只有草稿或已驳回状态可以编辑")
  172. leave_crud.update_leave(db, leave_id, user_id=user_id, **body.model_dump(exclude_none=True))
  173. db.commit()
  174. return success_response(leave_crud.get_leave(db, leave_id))