工单主表 t_ticket
必须包含哪些字段
客服系统里工单是核心实体,
t_ticket不能只存标题和内容。漏掉关键字段会导致后续无法过滤、统计或对接客服SaaS接口。
id:用
BIGINT UNSIGNED AUTO_INCREMENT,别用
INT—— 高并发客服场景下月增10万单,
INT2年就溢出
status:用
TINYINT存状态码(如 1=新建, 2=处理中, 3=已解决, 4=已关闭),别用
VARCHAR存“已解决”——排序、索引、JOIN 都会变慢
priority:同理用
TINYINT(1=低, 2=中, 3=高, 4=紧急),避免模糊查询 like '%紧急%' 必须有
created_at和
updated_at,且都设为
DATETIME(3)(毫秒级),客服SLA统计依赖精确到秒的更新时间 别忘了
customer_id(关联客户表)和
operator_id(当前处理人),这两个是高频 JOIN 字段,记得加索引
工单流转记录为什么不能合并在主表里
把每次分配、转交、升级的操作都写进
t_ticket的
last_operator_id或
history字段,短期省事,长期必踩坑。 历史不可追溯:谁在什么时间把工单转给了谁?主表只留最新值,审计、复盘时抓瞎 触发器或应用层拼接 JSON 到
history字段,会导致该字段越来越大,
SELECT *拖慢所有查询 正确做法是建独立表
t_ticket_flow,字段至少含:
ticket_id、
from_operator_id、
to_operator_id、
action(如 'assign'/'transfer'/'escalate')、
created_at
t_ticket_flow上建联合索引
(ticket_id, created_at),查某工单全流程只要
WHERE ticket_id = ? ORDER BY created_at
status
字段变更时要不要自动更新 updated_at
要,但不能靠 MySQL 的
ON UPDATE CURRENT_TIMESTAMP自动机制。
ON UPDATE CURRENT_TIMESTAMP在任何字段更新时都会触发,而客服系统常批量更新
description或
attachment_url,不该因此污染
updated_at—— 它应仅代表「业务状态变化」的时间点 应用层在调用
UPDATE t_ticket SET status = ?, updated_at = NOW(3) WHERE id = ?时显式赋值,更可控 如果用 ORM(如 Laravel Eloquent 或 Django ORM),确保状态变更逻辑走专用方法,而不是通用
save()额外建议:加一个
status_updated_at字段,专用于记录状态变更时间,和通用
updated_at分离,避免语义混淆
附件、消息、评论该不该和工单主表放一起
绝对不要。常见错误是把图片 base64、聊天记录 JSON 全塞进
t_ticket的
attachments或
messages字段。
TEXT或
MEDIUMTEXT字段膨胀后,主表
SELECT变慢,即使你只查
id和
title,MySQL 仍需读取整行(除非用
ROW_FORMAT=COMPRESSED+
BLOB外存,但太重) 正确拆分:建
t_ticket_attachment(字段:ticket_id, file_name, file_size, url, uploaded_at)、
t_ticket_message(ticket_id, sender_type, sender_id, content, sent_at) 附件 URL 建议存相对路径(如
/uploads/ticket_12345/abc.jpg),由 Nginx 或 CDN 直接服务,别让 MySQL 承担文件传输 消息表必须加索引
(ticket_id, sent_at),客服坐席翻页加载历史消息时,
LIMIT 20 OFFSET 100才不会全表扫
-- 示例:工单主表精简结构(不含冗余字段) CREATE TABLE `t_ticket` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `customer_id` BIGINT UNSIGNED NOT NULL, `operator_id` BIGINT UNSIGNED DEFAULT NULL, `title` VARCHAR(200) NOT NULL, `content` TEXT NOT NULL, `status` TINYINT NOT NULL DEFAULT 1, `priority` TINYINT NOT NULL DEFAULT 2, `created_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), `updated_at` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), `status_updated_at` DATETIME(3) NULL, PRIMARY KEY (`id`), KEY `idx_customer_status` (`customer_id`, `status`), KEY `idx_status_priority` (`status`, `priority`), KEY `idx_operator_status` (`operator_id`, `status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
实际跑起来才发现,最麻烦的不是字段怎么设,而是「状态机」怎么落地——比如“已解决”后是否允许回退到“处理中”,这种业务规则得在应用层硬校验,数据库只管存,不替你做决策。
