Files
2026-09-13 22:21:49 +08:00

162 lines
5.6 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""业务工单链:认领 → 提交审核 → 复核(软校验职责分离,流转写审计)。
状态机:待处理 ─认领─► 处理中 ─提交审核─► 待审核 ─复核─► 已完成 / 已驳回(终态)。
流转均用条件更新防并发跳步,每次流转写 audit_log。
"""
from __future__ import annotations
from datetime import datetime
from sqlalchemy.ext.asyncio import AsyncSession
from model.audit_log import AuditLog
from model.biz_work_order import BizWorkOrder
from model.sys_user import SysUser
from repositories.biz_work_order import BizWorkOrderRepo
from schemas.work_order import WorkOrderResp
from utils.exceptions import NotFoundError, ParamError
from utils.pagination import normalize_pagination, pagination_result
def _audit(db: AsyncSession, user: SysUser, module: str, action: str, target, detail: str) -> None:
db.add(
AuditLog(
user_id=user.id,
username=user.real_name or user.username,
module=module,
action=action,
target=str(target),
detail=detail,
status="成功",
)
)
def _resp(wo: BizWorkOrder) -> WorkOrderResp:
return WorkOrderResp(
id=wo.id,
work_order_no=wo.work_order_no,
order_type=wo.order_type,
sub_type=wo.sub_type,
customer_id=wo.customer_id,
submitter_id=wo.submitter_id,
handler_id=wo.handler_id,
current_node=wo.current_node,
priority=wo.priority,
status=wo.status,
biz_content=wo.biz_content,
create_time=wo.create_time,
update_time=wo.update_time,
)
async def list_work_orders(
db: AsyncSession,
status: str | None = None,
*,
page: int = 1,
page_size: int = 10,
) -> dict:
"""分页查询公共工单,默认每页 10 条。"""
page, page_size, offset = normalize_pagination(page, page_size)
repo = BizWorkOrderRepo(db)
orders = await repo.list_with_filter(
status=status,
limit=page_size,
offset=offset,
)
return pagination_result(
[_resp(o).model_dump(mode="json") for o in orders],
await repo.count_with_filter(status=status),
page=page,
page_size=page_size,
)
async def get_work_order(db: AsyncSession, work_order_id: int) -> WorkOrderResp:
wo = await BizWorkOrderRepo(db).get(work_order_id)
if wo is None:
raise NotFoundError("工单不存在")
return _resp(wo)
async def claim(db: AsyncSession, user: SysUser, work_order_id: int) -> dict:
"""认领:待处理 → 处理中,handler_id 记为当前专员。"""
wo_repo = BizWorkOrderRepo(db)
wo = await wo_repo.get(work_order_id)
if wo is None:
raise NotFoundError("工单不存在")
try:
if not await wo_repo.conditional_transition(
work_order_id,
from_status="待处理",
to_status="处理中",
handler_id=user.id,
update_time=datetime.now(),
):
raise ParamError("工单已被认领或状态已变更")
_audit(db, user, "work_order", "claim", work_order_id, f"认领工单 {wo.work_order_no}:待处理 → 处理中")
await db.commit()
return {"work_order_id": work_order_id, "status": "处理中", "work_order_no": wo.work_order_no}
except Exception:
await db.rollback()
raise
async def submit_review(db: AsyncSession, user: SysUser, work_order_id: int) -> dict:
"""提交审核:处理中 → 待审核。"""
wo_repo = BizWorkOrderRepo(db)
wo = await wo_repo.get(work_order_id)
if wo is None:
raise NotFoundError("工单不存在")
try:
if not await wo_repo.conditional_transition(
work_order_id,
from_status="处理中",
to_status="待审核",
current_node="复核",
update_time=datetime.now(),
):
raise ParamError("工单状态已变更,无法提交审核")
_audit(db, user, "work_order", "submit_review", work_order_id, f"提交审核工单 {wo.work_order_no}:处理中 → 待审核")
await db.commit()
return {"work_order_id": work_order_id, "status": "待审核", "work_order_no": wo.work_order_no}
except Exception:
await db.rollback()
raise
async def review(
db: AsyncSession, user: SysUser, work_order_id: int, approve: bool, comment: str | None = None
) -> dict:
"""复核:待审核 → 已完成 / 已驳回(终态)。复核人与处理人为同一人时软校验仅提示。"""
wo_repo = BizWorkOrderRepo(db)
wo = await wo_repo.get(work_order_id)
if wo is None:
raise NotFoundError("工单不存在")
to_status = "已完成" if approve else "已驳回"
warning = "复核人与处理人为同一人" if wo.handler_id == user.id else None
try:
if not await wo_repo.conditional_transition(
work_order_id,
from_status="待审核",
to_status=to_status,
current_node="完成",
update_time=datetime.now(),
):
raise ParamError("工单状态已变更,无法复核")
detail = f"复核工单 {wo.work_order_no}:待审核 → {to_status}"
if comment:
detail += f"({comment})"
_audit(db, user, "work_order", "review", work_order_id, detail)
await db.commit()
return {
"work_order_id": work_order_id,
"status": to_status,
"work_order_no": wo.work_order_no,
"warning": warning,
}
except Exception:
await db.rollback()
raise