Skip to content

工单与客服 ​

本文面向带客服团队的站长与运营主管,讲清工单从提交到关闭的完整流转、权限与可见范围、自动预回复与超时关闭的行为边界,以及工单传递到上游供应商后的收发现状。文中字段名、状态值与菜单路径均取自当前代码;管理端页面为 frontend-admin-v3,「后台入口」指该端的实际路由。

1. 部门、分类与状态 ​

工单部门固定四类,取值 sales、support、billing、abuse,页面标签依次为「销售」「技术支持」「财务」「投诉」。部门只是分类标签,不决定由谁处理——处理人靠指派。

优先级取值为 1 至 4,标签为「低」「中」「高」「紧急」;客户建单时未提交优先级则落库为 2(中)。工单传递到上游时优先级会被映射成 low / medium / high 三档:4 映射 high,3 与 2 都映射 medium,1 映射 low。

状态是会话推进的唯一依据,取值如下。

值标签由什么动作变成这个状态
0开启客户建单成功
1客户回复客户追加回复
2员工回复管理员回复;预回复命中时建单即为该状态
3已关闭管理员关闭、客户关闭或超时自动关闭

关闭原因存 close_reason,标签由系统给出:admin 管理员关闭、client 用户关闭、auto 自动关闭。重开成功会把它清空。

2. 权限与可见范围 ​

工单相关的权限码共 5 个,全部必须显式授予;除下面标明隐含的部分外,ticket.manage 不隐含传递与预回复权限。

权限码能做什么后台入口
ticket.list看工单列表、工单详情、回复列表、上游投递状态与日志工单处理 → 工单管理 → 工单列表
ticket.reply以员工身份回复工单、上传图片、撤回自己 2 分钟内的消息工单交流页内操作
ticket.manage关闭、重开、指派处理人、重新注册上游回调工单交流页内操作
ticket.delivery_manage工单传递规则、上游部门列表、上传防护配置工单处理 → 工单管理 → 工单传递设置
ticket.pre_reply_manage工单预回复设置工单处理 → 工单管理 → 工单预回复设置

ticket.manage 隐含 ticket.list 与 ticket.reply;系统内置默认角色拿不到 ticket.list 之外的任何工单权限,「访客」类角色只有 ticket.list。

可见范围的边界要记牢三点:

  • 列表接口没有按部门或处理人过滤的能力,拿到 ticket.list 的人看到的是全部工单。要限制某人只看自己负责的工单,只能靠不给他 ticket.list。
  • 客户邮箱在后续 API 响应里按权限码 privacy.view_raw 脱敏成 首字母***@域名;缺少该权限的账号看到的是掩码,无法据此联系客户。工单通知邮件本身不受影响,仍发往真实地址。
  • 「指派处理人」下拉只列启用(status=1)且持 ticket.reply 的管理员,一次最多返回 100 人;停用或没有回复权限的账号既不能出现在候选里,也不能被指派(接口会直接报「该管理员无工单处理权限」)。

3. 建单、回复与撤回 ​

客户建单要求标题非空(最长 200 字),内容与图片至少有一项(内容最长 10000 字);图片单条消息最多 9 张,上传接口只接受 image/jpeg、image/png、image/webp,单张不超过 5 MB,落盘在 storage/app/private/tickets/,只能通过签名短链读取。工单可以关联一个属于该客户自己的服务实例,选了别的用户的服务会被拒绝。

管理端不提供「代客户建单」入口:工单只能由客户在会员中心提交。管理员在客户视角看到的建单能力只在服务控制台的报障入口(服务实例 → 提交工单),最终仍以该客户身份建单。

员工回复、客户回复共用同一条消息通道:正文可为空但必须有图片,正文按纯文本清洗入库。两条约定容易被忽略:

  • 没有内部备注。 系统里不存在「仅内部可见」的回复类型,员工发出的每一条回复客户都能在会员中心看到,并会触发客户邮件与站内信。要写内部沟通记录,不要用工单回复。
  • 撤回有时限。 只有发送者本人能撤回自己的消息,且距发送不超过 120 秒;撤回后正文清空、附件清空,只保留一条「消息已撤回」占位。客户或员工都无法撤回对方的消息。

客户回复会把状态置为「客户回复」;员工回复与上游回调会把状态置为「员工回复」。已关闭工单拒绝任何一侧继续回复,客户必须先重开。关闭与回复并发时以行锁内的先到者为准,后到的一方会收到「工单已关闭」。

4. 分配与转派 ​

指派入口在工单交流页右栏的「处理人」下拉,保存后写 assignee_id,接口是 PUT /api/v2/admin/tickets/{ticket}/assignment,需要 ticket.manage。转派就是再指派一次:重新提交另一个管理员 ID 即可,不需要先解除。

指派会影响通知收件人范围,这是最容易出错的联动:

  • 工单没有处理人时,客户建单与客户回复的提醒邮件会发给全部启用且持 ticket.reply、且填了邮箱的管理员(按邮箱去重,只发一次)。
  • 工单有处理人、且该处理人本身在可接收名单里时,提醒只发给这一个人,其余客服收不到。所以指派既是分工,也是「静音开关」。

工单列表默认筛选是「进行中」(状态 0、1、2),支持按状态、优先级、部门、关键词(标题模糊或工单 ID 精确)过滤,每页默认 20 条、上限 100 条。

4.1 工单交流页与接口限流 ​

工单交流页(/admin/ticket-conversations/{id})左右分栏:左栏是「沟通记录」与回复输入框,右栏是「提交用户、工单分类、优先级、处理人、关联服务、创建时间、关闭原因」的描述列表与处理人选择器;未关闭的工单才显示指派区。列表页点击标题行即跳进本页,工单 ID 与客户 ID 也支持从其他模块(如日志中心)跳转过来。

管理端接口的限流写在路由上,做批量操作前先知道这些数字,免得撞上「请求过于频繁」:

动作接口限流
管理端回复POST /api/v2/admin/tickets/{ticket}/replies未单独限流(需 ticket.reply)
撤回消息POST /api/v2/admin/tickets/{ticket}/replies/{replyId}/recalls未单独限流
重开工单POST /api/v2/admin/tickets/{ticket}/reopen每 10 分钟 10 次
客户建单 / 回复 / 上传 / 关闭/api/v2/client/tickets、/tickets/{id}/replies、/tickets/upload-images、/tickets/{id}/closures每分钟 10 / 10 / 12 / 10 次

5. 预回复(自动首响) ​

预回复让客户建单成功的瞬间就收到一条员工回复,避免「提交后无人应答」。配置在「工单处理 → 工单管理 → 工单预回复设置」,权限码 ticket.pre_reply_manage,对应接口 GET/POST /api/v2/admin/ticket-pre-reply-settings,保存动作会写管理员操作日志(动作 ticket.pre_reply.update)。

字段含义约束与默认值
enabled是否启用预回复必填;默认关闭,config/ticket_pre_reply.php 回退值为 false
admin_user_id以谁的名义发这条回复启用时必填,必须是启用且持 ticket.reply 的管理员
content普通工单的预回复正文启用时清洗后不能为空,最长 5000 字
upstream_content命中传递规则、会推送到上游的工单专用正文可选;留空时回退用 content,最长 5000 字

行为边界必须提前跟团队对齐:

  • 预回复与建单在同一个事务里提交,只对新建工单生效一次,不是每次客户回复都触发。
  • 它会立刻把工单状态改成「员工回复」(状态 2),也就是说自动关闭的倒计时从建单那一刻就开始走了(见下一节)。
  • 它不转发上游:投递日志里会留一条 pre_reply_skipped 记录。若工单命中传递规则,上游收到的是客户的首条内容,而不是预回复。
  • 它不触发通知(客户不会因此收到一封「工单有新回复」的邮件)。
  • 命不命中传递规则决定用哪份正文:upstream_content 留空时无需判断,一律用 content。
  • 配置残缺时保持静默:未启用、没选管理员、内容清洗后为空、管理员被停用或失去 ticket.reply,都不会生成预回复,也不会报错——只会在保存时由校验拦住前三种情况。

6. 超时自动关闭与催办 ​

自动关闭由「自动化策略」页控制,入口是「系统设置 → 自动化策略」(直连 /admin/automation),权限 schedule.view,参数存 automation 配置组。

字段含义默认值取值范围/说明
ticket_auto_close_enabled是否启用自动关闭开启关闭后工单只能人工关闭
ticket_auto_close_after_hours员工回复后多少小时关闭481 至 720;代码内下限强制为 1
ticket_auto_close_schedule_mode任务执行周期每小时与「执行时间」一起决定运行时刻
ticket_auto_close_schedule_time任务执行时间00:00:00非每小时周期时用于对齐

判定口径只有一句话:扫描状态为「员工回复」(2)、且 updated_at 早于 now() - 小时数 的工单。由此推出四条实际后果:

  • 「开启」和「客户回复」的工单永远不会被自动关闭,哪怕放了半年。
  • 客户每次回复都会刷新 updated_at,倒计时重置;员工回复同理。
  • 关闭时附件只做软删标记,文件不删;重开后历史附件重新可见。
  • 关闭成功后才会发通知:邮件模板 100025(工单自动关闭通知)+ 站内信 ticket_auto_closed。若工单已被别人抢先关掉,不会重复发信。

催办在关闭窗口过半时发一次,只发站内信、不发邮件:阈值取 ticket_auto_close_after_hours 的一半(向下取整,至少 1 小时),且尚未到关闭阈值。同一轮等待只提醒一次(幂等键为 idle_since: + 该轮起点时间戳),客户一旦回复、时间戳变化,下一轮重新计算。站内信类型 ticket_pending_reminder,标题「工单待您处理」。

任务本身注册在 ticket-auto-close(分类「工单管理」),执行记录在「日志中心 → 日志管理 → 自动任务日志」,也可在站内 storage/logs/laravel.log 搜 [定时任务] 工单自动关闭 核对关闭了哪些工单。

7. 上游工单传递与回调 ​

传递的开关分三层,缺一层就整单不转发:一是关联服务所用产品必须绑定启用的 ZJMF 财务接口供应商;二是「工单传递设置」里必须有一条启用的规则命中该工单的部门与产品范围;三是标题与首条回复都不能命中该规则的屏蔽关键词。任一层不满足,工单仍在本站正常流转,只是不会出现在上游。

规则字段(接口 GET/POST/PUT/DELETE /api/v2/admin/ticket-delivery-rules,权限 ticket.delivery_manage):

字段说明
name规则名称,最长 120 字
department命中哪个工单部门,四选一
supplier_id上游供应商,必须启用且配好启用的 ZJMF 财务接口绑定
provider_key前端不填,接口固定写成 ZJMF 财务接口
product_scope_modeall 全部产品 / selected 指定产品
product_idsselected 时至少一个,且每个产品都要有启用的 ZJMF 上游绑定
upstream_department_id上游部门 ID,从上游拉取后选择,最长 64 字符
enabled是否启用该规则
sync_admin_replies是否把管理员回复同步给上游;关闭则只转发客户消息
mask_keywords每行一个关键词,命中标题或首条回复即不转发,最长 10000 字

同一供应商 + 部门 + 产品范围模式只能有一条规则,重复创建会被拒绝。

转发与回调的实际行为:

  • 工单投递成功后,后续历史回复会补投:该工单首条之后的所有回复会按发送者分别排队补发,上游不会丢中间的消息。
  • 客户回复与管理员回复都会带前缀标识([下游用户消息] / [下游管理员消息]),上游可据此区分来源。
  • 上游管理员回复经回调写入本站后会显示为 upstream_admin 类型的员工消息,消息上的显示名取上游传入的 admin_name,缺省显示「上游客服」。这类回调只写本地、不再回推上游,避免形成回环。
  • 回调按事件 ID 幂等:同一个事件重复到达只记一条回复,投递日志里留 duplicate_event。回调附件限制为最多 9 张、单张不超过 5 MB、仅 image/jpeg、image/png、image/webp。
  • 回调签名校验失败、上游供应商被停用、找不到对应本地工单,都会写一条 failed 投递日志并在响应里给出原因;密钥读到空值直接拒绝服务,不会降级放行。

排查入口都在工单交流页右栏的「上游传递」卡片:状态标签有「未配置 / 等待发送 / 发送中 / 已转发 / 转发失败 / 未转发」,另可看上游工单号、尝试次数、最近尝试时间、最近结果与投递日志分页。不转发时会给出原因码,常见的有 rule_not_matched(没有匹配的启用规则)、mask_keyword_matched(命中屏蔽关键词)、upstream_product_missing(服务未配上游产品)、supplier_binding_disabled(供应商接口绑定未启用)。若上游回调地址失效(换域名、改端口),用卡片上的「重新注册回调」按钮重发注册(接口 POST /api/v2/admin/tickets/{ticket}/upstream-delivery/callback-registration,需要 ticket.manage)。

8. 状态机:每一步能做什么 ​

当前状态客户可做员工可做
0 开启回复、关闭、上传图片回复、指派、关闭、重开(重开会失败,仅已关闭可重开)
1 客户回复继续回复、关闭、上传图片回复、指派、关闭
2 员工回复回复(转为 1)、关闭、上传图片回复、指派、关闭
3 已关闭重开重开

已关闭状态下,客户与员工的回复接口都会被拒绝,页面上也不提供回复框;「重开」是唯一出口,重开后状态回到「开启」、关闭原因清空、关闭期间软删的附件恢复可见,同时给客户写一条站内信 ticket_reopened。管理端重开有每 10 分钟 10 次的接口限流,客户侧重开同样受限。

9. 出问题先看这里 ​

  1. 客户说没收到邮件:先确认建单/回复是否真的触发了通知(工单是否已有处理人,决定了收件人是全体客服还是单人),再去「日志中心 → 日志管理 → 邮件日志」按收件人与模板号查发送状态,见通知与站内信。
  2. 客服说看不到某个工单:工单列表不按人过滤,看不到就是缺 ticket.list,去「系统设置 → 权限管理」核对该角色权限。
  3. 指派下拉是空的:说明系统里没有启用且持 ticket.reply 的管理员。下拉本身不依赖邮箱,但收件人缺邮箱时通知会静默失败——被指派人也收不到提醒。
  4. 工单没推送到上游:看「上游传递」卡片的最近结果与原因码,对照第 7 节的三种前置条件;上传类问题另查「工单传递设置」里的上传防护开关。
  5. 工单被自动关掉了:核对「自动化策略」里的自动关闭开关与小时数;判定只看状态 2 与 updated_at,与客户是否已读无关。

关联文档 ​

基于 AGPL-3.0-or-later 发布