93 lines
3.1 KiB
Python
93 lines
3.1 KiB
Python
# =============================================================================
|
|
# 企微IT智能服务台 — 阶段5 自动化 审批单服务
|
|
# =============================================================================
|
|
# 说明:管理高危动作 / 员工二次确认对应的审批单(ApprovalTicket)。
|
|
# 1. ensure_ticket:动作首次需要审批时创建(幂等,避免重复提单)
|
|
# 2. decide:坐席审批或员工 H5 确认后更新状态
|
|
# 3. get_pending_for_action:查询动作当前待决审批单
|
|
# =============================================================================
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from datetime import datetime, timezone
|
|
from typing import Any, Optional
|
|
|
|
from sqlalchemy import select
|
|
|
|
from app.models.automation import ApprovalTicket
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class ApprovalService:
|
|
"""审批单服务。"""
|
|
|
|
def __init__(self, db: Any):
|
|
self.db = db
|
|
|
|
async def ensure_ticket(
|
|
self, action: Any, channel: str, reason: Optional[str] = None
|
|
) -> ApprovalTicket:
|
|
"""确保动作存在一张待决审批单(幂等)。"""
|
|
stmt = select(ApprovalTicket).where(
|
|
ApprovalTicket.action_id == action.id,
|
|
ApprovalTicket.status == "pending",
|
|
)
|
|
existing = (await self.db.execute(stmt)).scalar_one_or_none()
|
|
if existing is not None:
|
|
return existing
|
|
|
|
ticket = ApprovalTicket(
|
|
action_id=action.id,
|
|
session_id=action.session_id,
|
|
channel=channel,
|
|
status="pending",
|
|
reason=reason,
|
|
)
|
|
self.db.add(ticket)
|
|
await self.db.flush()
|
|
return ticket
|
|
|
|
async def get_pending_for_action(self, action_id: str) -> Optional[ApprovalTicket]:
|
|
"""查询动作当前待决审批单。"""
|
|
stmt = select(ApprovalTicket).where(
|
|
ApprovalTicket.action_id == action_id,
|
|
ApprovalTicket.status == "pending",
|
|
)
|
|
return (await self.db.execute(stmt)).scalar_one_or_none()
|
|
|
|
async def decide(
|
|
self,
|
|
ticket_id: str,
|
|
decision: str,
|
|
note: Optional[str],
|
|
approver_id: Optional[str],
|
|
) -> ApprovalTicket:
|
|
"""更新审批单决策。
|
|
|
|
Args:
|
|
decision: approve / reject
|
|
note: 审批意见
|
|
approver_id: 审批人(坐席或员工)
|
|
|
|
Returns:
|
|
ApprovalTicket: 更新后的审批单
|
|
|
|
Raises:
|
|
ValueError: 审批单不存在或已决
|
|
"""
|
|
stmt = select(ApprovalTicket).where(ApprovalTicket.id == ticket_id)
|
|
ticket = (await self.db.execute(stmt)).scalar_one_or_none()
|
|
if ticket is None:
|
|
raise ValueError(f"审批单不存在: {ticket_id}")
|
|
if ticket.status != "pending":
|
|
return ticket
|
|
|
|
ticket.status = "approved" if decision == "approve" else "rejected"
|
|
ticket.decision_note = note
|
|
ticket.approver_id = approver_id
|
|
ticket.decided_at = datetime.now(timezone.utc)
|
|
await self.db.flush()
|
|
return ticket
|