← Назад к вопросам
Что такое тикет-система в БД?
2.3 Middle🔥 41 комментариев
#Python Core#Базы данных (SQL)
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Тикет-система в БД (Ticket System in Database)
Тикет-система в БД — это совокупность механизмов и структур данных, используемых для управления и отслеживанием заявок, задач или проблем (тикетов) в системе. Тикет обычно содержит информацию о проблеме, её статусе, приоритете, отправителе и других метаданных. Это фундаментальная часть систем управления проектами, систем поддержки клиентов и workflow-приложений.
Основная структура данных
1. Базовая таблица тикетов
from datetime import datetime
from enum import Enum
from typing import Optional
class TicketStatus(Enum):
OPEN = "open"
IN_PROGRESS = "in_progress"
RESOLVED = "resolved"
CLOSED = "closed"
REOPENED = "reopened"
class TicketPriority(Enum):
LOW = "low"
MEDIUM = "medium"
HIGH = "high"
CRITICAL = "critical"
class Ticket:
def __init__(
self,
id: int,
title: str,
description: str,
status: TicketStatus,
priority: TicketPriority,
created_at: datetime,
created_by: int, # user_id
assigned_to: Optional[int] = None,
updated_at: datetime = None,
):
self.id = id
self.title = title
self.description = description
self.status = status
self.priority = priority
self.created_at = created_at
self.created_by = created_by
self.assigned_to = assigned_to
self.updated_at = updated_at or created_at
2. SQL-схема тикет-системы
# SQL DDL (Data Definition Language)
sql_schema = """
CREATE TABLE tickets (
id SERIAL PRIMARY KEY,
title VARCHAR(255) NOT NULL,
description TEXT NOT NULL,
status VARCHAR(50) NOT NULL DEFAULT 'open',
priority VARCHAR(50) NOT NULL DEFAULT 'medium',
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_by INTEGER NOT NULL REFERENCES users(id),
assigned_to INTEGER REFERENCES users(id),
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
resolved_at TIMESTAMP,
resolution_notes TEXT,
CONSTRAINT valid_status CHECK (status IN ('open', 'in_progress', 'resolved', 'closed', 'reopened')),
CONSTRAINT valid_priority CHECK (priority IN ('low', 'medium', 'high', 'critical'))
);
CREATE TABLE ticket_comments (
id SERIAL PRIMARY KEY,
ticket_id INTEGER NOT NULL REFERENCES tickets(id) ON DELETE CASCADE,
author_id INTEGER NOT NULL REFERENCES users(id),
content TEXT NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE ticket_history (
id SERIAL PRIMARY KEY,
ticket_id INTEGER NOT NULL REFERENCES tickets(id) ON DELETE CASCADE,
changed_by INTEGER NOT NULL REFERENCES users(id),
field_name VARCHAR(100) NOT NULL,
old_value TEXT,
new_value TEXT,
changed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE ticket_tags (
id SERIAL PRIMARY KEY,
ticket_id INTEGER NOT NULL REFERENCES tickets(id) ON DELETE CASCADE,
tag VARCHAR(100) NOT NULL,
UNIQUE(ticket_id, tag)
);
CREATE INDEX idx_ticket_status ON tickets(status);
CREATE INDEX idx_ticket_assigned_to ON tickets(assigned_to);
CREATE INDEX idx_ticket_created_by ON tickets(created_by);
CREATE INDEX idx_ticket_priority ON tickets(priority);
"""
Операции с тикетами
1. Создание тикета
import psycopg2
from datetime import datetime
def create_ticket(
connection,
title: str,
description: str,
created_by: int,
priority: str = "medium"
) -> int:
"""Создаёт новый тикет и возвращает его ID"""
cursor = connection.cursor()
query = """
INSERT INTO tickets (title, description, status, priority, created_by, created_at)
VALUES (%s, %s, %s, %s, %s, %s)
RETURNING id;
"""
cursor.execute(query, (
title,
description,
"open",
priority,
created_by,
datetime.now()
))
ticket_id = cursor.fetchone()[0]
connection.commit()
cursor.close()
return ticket_id
2. Обновление статуса
def update_ticket_status(
connection,
ticket_id: int,
new_status: str,
updated_by: int,
resolution_notes: str = None
):
"""Обновляет статус тикета и создаёт запись в истории"""
cursor = connection.cursor()
# Получаем старый статус
cursor.execute("SELECT status FROM tickets WHERE id = %s", (ticket_id,))
old_status = cursor.fetchone()[0]
# Обновляем статус
resolved_at = datetime.now() if new_status == "resolved" else None
update_query = """
UPDATE tickets
SET status = %s, updated_at = %s, resolved_at = %s, resolution_notes = %s
WHERE id = %s;
"""
cursor.execute(update_query, (
new_status,
datetime.now(),
resolved_at,
resolution_notes,
ticket_id
))
# Создаём запись в истории
history_query = """
INSERT INTO ticket_history (ticket_id, changed_by, field_name, old_value, new_value)
VALUES (%s, %s, %s, %s, %s);
"""
cursor.execute(history_query, (
ticket_id,
updated_by,
"status",
old_status,
new_status
))
connection.commit()
cursor.close()
3. Назначение тикета
def assign_ticket(connection, ticket_id: int, assigned_to: int, assigned_by: int):
"""Назначает тикет сотруднику"""
cursor = connection.cursor()
# Получаем текущего исполнителя
cursor.execute("SELECT assigned_to FROM tickets WHERE id = %s", (ticket_id,))
old_assigned = cursor.fetchone()[0]
# Обновляем
cursor.execute(
"UPDATE tickets SET assigned_to = %s, updated_at = %s WHERE id = %s",
(assigned_to, datetime.now(), ticket_id)
)
# История
cursor.execute(
"""INSERT INTO ticket_history (ticket_id, changed_by, field_name, old_value, new_value)
VALUES (%s, %s, %s, %s, %s)""",
(ticket_id, assigned_by, "assigned_to", str(old_assigned), str(assigned_to))
)
connection.commit()
cursor.close()
4. Добавление комментария
def add_comment(connection, ticket_id: int, author_id: int, content: str):
"""Добавляет комментарий к тикету"""
cursor = connection.cursor()
cursor.execute(
"""INSERT INTO ticket_comments (ticket_id, author_id, content, created_at)
VALUES (%s, %s, %s, %s)""",
(ticket_id, author_id, content, datetime.now())
)
# Обновляем время обновления тикета
cursor.execute(
"UPDATE tickets SET updated_at = %s WHERE id = %s",
(datetime.now(), ticket_id)
)
connection.commit()
cursor.close()
5. Получение тикета со всеми данными
def get_ticket_full(connection, ticket_id: int) -> dict:
"""Получает тикет со всеми комментариями и историей"""
cursor = connection.cursor()
# Основная информация
cursor.execute("""
SELECT id, title, description, status, priority,
created_at, created_by, assigned_to, updated_at
FROM tickets
WHERE id = %s
""", (ticket_id,))
ticket = cursor.fetchone()
# Комментарии
cursor.execute("""
SELECT id, author_id, content, created_at
FROM ticket_comments
WHERE ticket_id = %s
ORDER BY created_at DESC
""", (ticket_id,))
comments = cursor.fetchall()
# История
cursor.execute("""
SELECT id, changed_by, field_name, old_value, new_value, changed_at
FROM ticket_history
WHERE ticket_id = %s
ORDER BY changed_at DESC
""", (ticket_id,))
history = cursor.fetchall()
cursor.close()
return {
"ticket": ticket,
"comments": comments,
"history": history
}
Аналитика и отчёты
1. Статистика по статусам
def get_status_statistics(connection) -> dict:
"""Возвращает количество тикетов по статусам"""
cursor = connection.cursor()
cursor.execute("""
SELECT status, COUNT(*) as count
FROM tickets
GROUP BY status
ORDER BY count DESC
""")
stats = {row[0]: row[1] for row in cursor.fetchall()}
cursor.close()
return stats
2. Среднее время разрешения
def get_average_resolution_time(connection) -> float:
"""Возвращает среднее время разрешения тикета в часах"""
cursor = connection.cursor()
cursor.execute("""
SELECT AVG(EXTRACT(EPOCH FROM (resolved_at - created_at)) / 3600) as avg_hours
FROM tickets
WHERE resolved_at IS NOT NULL
""")
avg_hours = cursor.fetchone()[0]
cursor.close()
return avg_hours
3. Производительность сотрудника
def get_employee_stats(connection, employee_id: int) -> dict:
"""Возвращает статистику по тикетам сотрудника"""
cursor = connection.cursor()
cursor.execute("""
SELECT
COUNT(*) as total_tickets,
SUM(CASE WHEN status = 'resolved' THEN 1 ELSE 0 END) as resolved,
SUM(CASE WHEN status = 'open' THEN 1 ELSE 0 END) as open,
AVG(EXTRACT(EPOCH FROM (resolved_at - created_at)) / 3600) as avg_resolution_hours
FROM tickets
WHERE assigned_to = %s
""", (employee_id,))
stats = cursor.fetchone()
cursor.close()
return {
"total_tickets": stats[0],
"resolved": stats[1],
"open": stats[2],
"avg_resolution_hours": stats[3]
}
Лучшие практики
- Индексирование: добавьте индексы на часто фильтруемые поля (status, assigned_to)
- Партиционирование: для больших таблиц используйте партиционирование по дате
- Архивирование: архивируйте закрытые тикеты в отдельную таблицу
- SLA (Service Level Agreement): отслеживайте время ответа и разрешения
- История изменений: сохраняйте полную историю для аудита
- Уведомления: отправляйте уведомления при изменении тикета
- Поиск: используйте полнотекстовый поиск (FTS) для заголовков и описаний
Тикет-система в БД — это критическая часть любого системы поддержки или управления проектами, обеспечивающая отслеживание, организацию и анализ работы команды.