feat: add trusted ticket intake backend

This commit is contained in:
hailin
2026-07-16 19:37:32 +08:00
parent b592dc219d
commit bcd6de4609
28 changed files with 1966 additions and 42 deletions

View File

@@ -1,13 +1,98 @@
from django.contrib.auth import get_user_model
from django.utils import timezone
from apps.audit.models import OperationAuditLog
from apps.tickets.models import Ticket, TicketEvent
from apps.tickets.models import Ticket, TicketCategory, TicketEvent, TicketPriority
class TicketStatusError(ValueError):
pass
class TicketDataError(ValueError):
pass
REQUIRED_TICKET_FIELDS = (
"title",
"description",
"category_code",
"priority",
"problem_performance",
"problem_object",
"submitter_name",
"submitter_org",
"environment",
)
def validate_ticket_fields(fields: dict) -> tuple[dict, TicketCategory]:
normalized = {
"title": str(fields.get("title") or "").strip(),
"description": str(fields.get("description") or "").strip(),
"category_code": str(fields.get("category_code") or "").strip(),
"priority": str(fields.get("priority") or "medium").strip(),
"problem_performance": str(fields.get("problem_performance") or "").strip(),
"problem_object": str(fields.get("problem_object") or "").strip(),
"submitter_name": str(fields.get("submitter_name") or "").strip(),
"submitter_org": str(fields.get("submitter_org") or "").strip(),
"impact_scope": str(fields.get("impact_scope") or "").strip(),
"environment": str(fields.get("environment") or "").strip(),
"related_system": str(fields.get("related_system") or "").strip(),
"assignee_id": fields.get("assignee_id"),
}
missing = [code for code in REQUIRED_TICKET_FIELDS if not normalized.get(code)]
if missing:
raise TicketDataError(f"缺少必填字段: {', '.join(missing)}")
if normalized["priority"] not in TicketPriority.values:
raise TicketDataError("工单优先级无效")
category = TicketCategory.objects.filter(code=normalized["category_code"], enabled=True).first()
if category is None:
raise TicketDataError("工单分类无效或已停用")
if normalized["assignee_id"]:
assignee = get_user_model().objects.filter(id=normalized["assignee_id"]).first()
if assignee is None:
raise TicketDataError("处理人不存在")
else:
assignee = None
normalized["assignee"] = assignee
return normalized, category
def next_ticket_no() -> str:
prefix = timezone.localdate().strftime("TK%Y%m%d")
count = Ticket.objects.filter(ticket_no__startswith=prefix).count() + 1
return f"{prefix}{count:04d}"
def create_ticket_from_fields(fields: dict, user) -> Ticket:
normalized, category = validate_ticket_fields(fields)
ticket = Ticket.objects.create(
ticket_no=next_ticket_no(),
title=normalized["title"],
description=normalized["description"],
category=category,
priority=normalized["priority"],
problem_performance=normalized["problem_performance"],
problem_object=normalized["problem_object"],
submitter_name=normalized["submitter_name"],
submitter_org=normalized["submitter_org"],
impact_scope=normalized["impact_scope"],
environment=normalized["environment"],
related_system=normalized["related_system"],
assignee=normalized["assignee"],
requester=user,
created_by=user,
)
TicketEvent.objects.create(
ticket=ticket,
event_type="created",
to_value={"status": ticket.status, "category_code": normalized["category_code"]},
operator=user,
)
return ticket
STANDARD_TRANSITIONS: dict[str, set[str]] = {
"pending_info": {"pending", "closed"},
"pending": {"in_progress", "closed"},