Files
FastapiAdmin/backend/app/api/v1/module_system/ticket/service.py
T
zhangtao 73f2823692 refactor: 大规模代码整理与功能优化
1. 重构后端API路由、CRUD与模块结构,整合日志管理,移除废弃demo代码
2. 优化前端组件类型定义、样式与路由配置,修复权限判断逻辑
3. 调整默认排序规则、滚动条样式与工具类函数,更新依赖与配置文件
4. 修复多处类型不匹配与默认值问题,完善表单与菜单验证逻辑
2026-06-17 01:56:31 +08:00

153 lines
5.6 KiB
Python

from app.core.base_schema import AuthSchema
from app.core.exceptions import CustomException
from .crud import TicketCRUD
from .schema import (
TicketBatchSchema,
TicketCreateSchema,
TicketOutSchema,
TicketQueryParam,
TicketUpdateSchema,
)
_TICKET_STATUS_TRANSITIONS = {
0: {1, 3},
1: {2, 3},
2: {3},
3: {0},
}
_TICKET_STATUS_LABELS = {
0: "待处理",
1: "处理中",
2: "已完成",
3: "已关闭",
}
class TicketService:
"""工单管理服务层"""
@classmethod
def _validate_status_transition(
cls,
auth: AuthSchema,
ticket,
new_status: int,
) -> None:
"""校验工单状态流转是否合法"""
old_status = ticket.status if ticket.status is not None else 0
old_label = _TICKET_STATUS_LABELS.get(old_status, str(old_status))
new_label = _TICKET_STATUS_LABELS.get(new_status, str(new_status))
if new_status not in _TICKET_STATUS_TRANSITIONS.get(old_status, set()):
raise CustomException(
msg=f"不允许从“{old_label}”转换为“{new_label}”"
)
is_super = auth.user and auth.user.is_superuser
is_creator = auth.user and ticket.created_id == auth.user.id
is_assignee = auth.user and ticket.assigned_id == auth.user.id
if new_status == 0:
if not is_super:
raise CustomException(msg="仅超管可以重新打开已关闭的工单")
elif old_status == 0 and new_status == 1:
if not (is_super or is_creator or is_assignee):
raise CustomException(msg="仅创建人、处理人或超管可以受理工单")
elif old_status == 0 and new_status == 3:
if not (is_super or is_creator):
raise CustomException(msg="仅创建人或超管可以取消工单")
elif old_status == 1 and new_status == 2:
if not (is_super or is_assignee):
raise CustomException(msg="仅处理人或超管可以将工单标记为已完成")
elif old_status == 1 and new_status == 3:
if not (is_super or is_creator or is_assignee):
raise CustomException(msg="仅创建人、处理人或超管可以关闭工单")
elif old_status == 2 and new_status == 3:
if not (is_super or is_creator):
raise CustomException(msg="仅创建人或超管可以确认关闭工单")
@classmethod
async def page_service(
cls,
auth: AuthSchema,
page_no: int,
page_size: int,
search: TicketQueryParam | None = None,
order_by: list | None = None,
) -> dict:
return await TicketCRUD(auth).page(
offset=(page_no - 1) * page_size,
limit=page_size,
order_by=order_by or [{"created_time": "desc"}],
search=search.__dict__ if search else {},
out_schema=TicketOutSchema,
)
@classmethod
async def detail_service(cls, auth: AuthSchema, id: int) -> TicketOutSchema:
obj = await TicketCRUD(auth).get(id=id)
if not obj:
raise CustomException(msg="工单不存在")
return TicketOutSchema.model_validate(obj)
@classmethod
async def create_service(cls, auth: AuthSchema, data: TicketCreateSchema) -> TicketOutSchema:
obj = await TicketCRUD(auth).create(data=data)
if not obj:
raise CustomException(msg="创建工单失败")
return TicketOutSchema.model_validate(obj)
@classmethod
async def update_service(cls, auth: AuthSchema, id: int, data: TicketUpdateSchema) -> TicketOutSchema:
obj = await TicketCRUD(auth).get(id=id)
if not obj:
raise CustomException(msg="工单不存在")
if data.status is not None:
cls._validate_status_transition(auth, obj, data.status)
# 校验 assigned_id:分配处理人时验证用户是否存在且属于同一租户
if data.assigned_id is not None:
from sqlalchemy import select
from app.api.v1.module_system.user.model import UserModel
user_stmt = select(UserModel).where(
UserModel.id == data.assigned_id,
UserModel.is_deleted.is_(False),
)
user_result = await auth.db.execute(user_stmt)
assigned_user = user_result.scalar_one_or_none()
if not assigned_user:
raise CustomException(msg="指定的处理人不存在")
if assigned_user.tenant_id != obj.tenant_id:
raise CustomException(msg="处理人必须与工单属于同一租户")
updated = await TicketCRUD(auth).update(id=id, data=data)
if not updated:
raise CustomException(msg="更新失败")
return TicketOutSchema.model_validate(updated)
@classmethod
async def delete_service(cls, auth: AuthSchema, ids: list[int]) -> None:
if not ids:
raise CustomException(msg="删除对象不能为空")
await TicketCRUD(auth).delete(ids=ids)
@classmethod
async def batch_service(cls, auth: AuthSchema, data: TicketBatchSchema) -> None:
"""批量更新工单状态"""
if not data.ids:
raise CustomException(msg="请选择要操作的工单")
tickets = await TicketCRUD(auth).get_by_ids_crud(ids=data.ids)
ticket_map = {t.id: t for t in tickets}
for tid in data.ids:
obj = ticket_map.get(tid)
if not obj:
raise CustomException(msg=f"工单[{tid}]不存在")
cls._validate_status_transition(auth, obj, data.status)
await TicketCRUD(auth).set_crud(ids=data.ids, status=data.status)