t_ticket主表必须包含id、customer_id、operator_id、title、content、status、priority、created_at、updated_at、status_updated_at字段,且需合理设置类型、索引与语义分离。
t_ticket 必须包含哪些字段客服系统里工单是核心实体,t_ticket 不能只存标题和内容。漏掉关键字段会导致后续无法过滤、统计或对接客服SaaS接口。
id:用 BIGINT UNSIGNED AUTO_INCREMENT,别用 INT —— 高并发客服场景下月增10万单,INT 2年就溢出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 字段,短期省事,长期必踩坑。
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 = ? 时显式赋值,更可控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)/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;
实际跑起来才发现,最麻烦的不是字段怎么设,而是「状态机」怎么落地——比如“已解决”后是否允许回退到“处理中”,这种业务规则得在应用层硬校验,数据库只管存,不替你做决策。