# 客服中心工单系统设计方案

## 一、背景与目标

本文基于 `doc/jykj/ERP功能与权限需求规格草案_V1.0.md` 中 `2.4 客服中心` 的要求，设计一套独立的客服工单系统，用于承接发货前、运输中、售后三类订单问题处理。

客服工单系统的定位不是 CRM，也不是生产工单。它只负责记录和推动“订单相关问题”的处理闭环：

- 客服沟通、处理摘要、确认结果、下一步动作和附件统一沉淀在工单上。
- 普通客服可以查看客服部门全部工单，但只能修改本人负责工单。
- 客服组长可以分配、转派、催办、关闭和重新打开工单。
- 客服不得直接修改订单、处方、退款、生产任务或解除 Hold，只能通过工单发起、跟进和协调。

## 二、系统边界

### 2.1 不做的事情

- 不新增独立客户中心，客户信息从订单或客户资料中关联查看。
- 不新增独立物流查询中心，物流信息从 `express_task`、物流轨迹或承运商接口中关联查看。
- 不用 `custom_item_process` 承接客服工单；该表是生产工单/工序。
- 不建议用现有 `task` 直接承接客服工单；`task` 更偏通用任务和绩效任务，缺少客服工单的分类、沟通、重开、物流去重和售后审批语义。

### 2.2 需要承接的事情

| 工单类型 | 范围 | 典型问题 |
| --- | --- | --- |
| 发货前工单 | 订单进入 ERP 至交给承运商前 | 缺处方、处方异常、镜框缺货、换款、改地址、补差价、取消、暂停恢复 |
| 运输中工单 | 承运商收件至签收或退回前 | 派送失败、地址错误、清关、滞留、拦截、重派、退回途中 |
| 售后工单 | 签收后或正式退回后 | 退货、退款、换货、重做、补发、质量问题、处方不适应、少件错发 |

## 三、核心模型

### 3.1 客服工单主表：`customer_service_ticket`

建议表名：`hg_customer_service_ticket`

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `id` | int unsigned | 主键 |
| `company_id` | varchar(32) | 公司 ID |
| `ticket_no` | varchar(64) | 工单编号 |
| `ticket_type` | varchar(32) | 工单类型：`pre_ship` 发货前，`in_transit` 运输中，`after_sale` 售后 |
| `source` | varchar(32) | 来源：`manual` 手动，`system` 系统，`shopify` Shopify，`logistics` 物流 |
| `status` | tinyint | 工单状态 |
| `priority` | tinyint | 优先级：0普通，10紧急，20高危 |
| `title` | varchar(255) | 工单标题 |
| `problem_type` | varchar(64) | 问题类型 |
| `problem_reason` | varchar(255) | 问题原因 |
| `description` | text | 问题描述 |
| `sale_order_id` | int unsigned | 关联销售订单 |
| `customer_id` | int unsigned | 关联客户 |
| `sale_order_detail_id` | int unsigned null | 关联订单明细 |
| `custom_order_item_id` | int unsigned null | 关联定制履约项 |
| `express_task_id` | int unsigned null | 关联快递任务 |
| `tracking_no` | varchar(128) null | 运单号 |
| `after_sale_order_id` | int unsigned null | 关联重做/补发/换货订单 |
| `owner_admin_id` | int unsigned | 当前负责人 |
| `department_id` | int unsigned null | 当前归属部门 |
| `create_admin_id` | int unsigned | 创建人 |
| `edit_admin_id` | int unsigned null | 最近编辑人 |
| `close_admin_id` | int unsigned null | 关闭人 |
| `customer_contact_channel` | varchar(64) null | 沟通渠道 |
| `customer_contact_account` | varchar(128) null | 联系账号或方式 |
| `customer_confirm_result` | varchar(64) null | 客户确认结果 |
| `next_action` | varchar(255) null | 下一步动作 |
| `next_action_admin_id` | int unsigned null | 下一步负责人 |
| `next_action_time` | datetime null | 下一步处理时间 |
| `resolved_result` | varchar(255) null | 处理结果 |
| `closed_reason` | varchar(255) null | 关闭原因 |
| `first_response_time` | datetime null | 首次响应时间 |
| `resolved_time` | datetime null | 解决时间 |
| `closed_time` | datetime null | 关闭时间 |
| `reopen_count` | int unsigned | 重开次数 |
| `last_log_time` | datetime null | 最近沟通/操作时间 |
| `create_time` | datetime | 创建时间 |
| `update_time` | datetime | 更新时间 |
| `deleted_at` | datetime null | 软删除 |

### 3.2 工单日志表：`customer_service_ticket_log`

建议表名：`hg_customer_service_ticket_log`

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `id` | int unsigned | 主键 |
| `company_id` | varchar(32) | 公司 ID |
| `ticket_id` | int unsigned | 工单 ID |
| `event` | varchar(64) | 事件类型 |
| `content` | text | 事件内容、沟通摘要或处理说明 |
| `from_status` | tinyint null | 变更前状态 |
| `to_status` | tinyint null | 变更后状态 |
| `operator_admin_id` | int unsigned null | 操作人 |
| `contact_channel` | varchar(64) null | 沟通渠道 |
| `customer_confirm_result` | varchar(64) null | 客户确认结果 |
| `next_action` | varchar(255) null | 下一步动作 |
| `payload` | json null | 扩展快照，如物流异常原始数据 |
| `create_time` | datetime | 创建时间 |

事件类型建议：

| 事件 | 说明 |
| --- | --- |
| `created` | 创建工单 |
| `assigned` | 分配负责人 |
| `transferred` | 转派 |
| `commented` | 添加沟通或处理记录 |
| `urged` | 催办 |
| `status_changed` | 状态变更 |
| `closed` | 关闭 |
| `reopened` | 重新打开 |
| `linked_order` | 关联订单 |
| `linked_express` | 关联快递任务 |
| `after_sale_requested` | 发起售后动作 |

### 3.3 工单附件

无需新增附件表。工单附件复用现有文件系统和文件关联能力。

建议约定：

| 场景 | 关联方式 |
| --- | --- |
| 工单主附件 | `module=customer-service-ticket`，`ref_id=customer_service_ticket.id` |
| 工单日志附件 | `module=customer-service-ticket-log`，`ref_id=customer_service_ticket_log.id` |

接口中可继续接收 `file_ids`，由服务层把已上传文件关联到工单或工单日志。

## 四、状态与枚举

### 4.1 工单状态

| 值 | 状态 | 说明 |
| --- | --- | --- |
| `0` | 待确认 | 新建后待客服确认或领取 |
| `10` | 处理中 | 已有人负责并处理中 |
| `20` | 待外部处理 | 等待运营、采购、下单员、仓库、承运商或客户动作 |
| `30` | 待客户确认 | 已给出方案，等待客户确认 |
| `80` | 已解决 | 问题已解决，等待关闭或自动关闭 |
| `100` | 已关闭 | 工单关闭 |
| `-1` | 已取消 | 创建错误或不需要处理 |

### 4.2 工单类型

| 值 | 类型 |
| --- | --- |
| `pre_ship` | 发货前工单 |
| `in_transit` | 运输中工单 |
| `after_sale` | 售后工单 |

### 4.3 问题类型

发货前：

- `missing_prescription`：缺处方
- `prescription_abnormal`：处方异常
- `frame_out_of_stock`：镜框缺货
- `change_frame_or_lens`：换款或换镜片
- `address_change`：改地址
- `payment_or_price_gap`：补差价或付款问题
- `cancel_request`：取消请求
- `pause_resume`：暂停或恢复

运输中：

- `delivery_failed`：派送失败
- `address_error`：地址错误
- `customs_clearance`：清关问题
- `carrier_delay`：运输滞留
- `intercept_or_reroute`：拦截或改派
- `returning`：退回途中

售后：

- `return_refund`：退货退款
- `exchange`：换货
- `remake`：重做
- `reship`：补发
- `quality_issue`：质量问题
- `prescription_discomfort`：处方不适应
- `missing_or_wrong_item`：少件错发

## 五、业务流程

### 5.1 发货前工单

1. 客服可根据客户消息手动创建发货前工单。
2. 系统或生产环节发现问题时，先由对应岗位确认。
3. 镜框缺货、处方异常等问题不能直接推给客服；采购或下单员确认后才生成工单。
4. 工单可关联订单、订单明细、定制履约项和订单暂停状态。
5. 客服记录客户沟通和确认结果，但不能直接修改 Shopify 订单、处方、退款或生产状态。
6. 需要暂停订单时，由有权限岗位调用订单暂停接口，客服工单只记录原因和跟进过程。

### 5.2 运输中工单

1. 客服可手动创建运输中工单。
2. ERP 接收物流异常后可自动创建运输中工单。
3. 同一订单、同一运单、同一问题类型已有未关闭工单时，不重复创建，只追加日志并更新时间。
4. 工单默认状态为 `0=待确认`，由客服确认后进入处理。
5. 运输中工单可转售后工单，转售后时保留原沟通和物流记录。

### 5.3 售后工单

1. 客服创建售后工单并记录客户诉求、附件和初步判断。
2. 客服不得直接执行退款，也不得直接创建生产任务。
3. 涉及退款时，工单流转给运营在 Shopify 执行。
4. 涉及重做、补发、换货时，必须走运营组长和下单员审批。
5. 审批通过后创建独立关联订单，原订单历史保持不变。
6. 售后工单关闭前必须记录处理结果。

## 六、权限规则

| 操作 | 普通客服 | 客服组长 | 运营 | 采购 | 下单员 | 仓库 | 管理层/系统管理员 |
| --- | --- | --- | --- | --- | --- | --- | --- |
| 查看客服部门全部工单 | 是 | 是 | 按权限 | 按关联 | 按关联 | 按关联 | 是 |
| 创建客服工单 | 是 | 是 | 是 | 是 | 是 | 是 | 是 |
| 编辑本人负责工单 | 是 | 是 | 按权限 | 按关联 | 按关联 | 按关联 | 是 |
| 编辑他人负责工单 | 否 | 是 | 否 | 否 | 否 | 否 | 是 |
| 分配负责人 | 否 | 是 | 否 | 否 | 否 | 否 | 是 |
| 转派工单 | 否 | 是 | 否 | 否 | 否 | 否 | 是 |
| 催办 | 是 | 是 | 是 | 是 | 是 | 是 | 是 |
| 关闭工单 | 仅本人且满足关闭条件 | 是 | 按权限 | 按关联 | 按关联 | 按关联 | 是 |
| 重新打开 | 否 | 是 | 按权限 | 否 | 否 | 否 | 是 |
| 修改订单核心资料 | 否 | 否 | Shopify 中处理 | 否 | 否 | 否 | 否 |
| 执行退款 | 否 | 否 | Shopify 中处理 | 否 | 否 | 否 | 否 |
| 解除订单 Hold | 否 | 否 | 按场景 | 按场景 | 按场景 | 否 | 按权限 |

## 七、接口设计

### 7.1 工单列表

```text
GET /admin/customer-service-ticket/index
```

常用查询参数：

| 参数 | 说明 |
| --- | --- |
| `ticket_type` | `pre_ship`、`in_transit`、`after_sale` |
| `status` | 工单状态 |
| `owner_admin_id` | 负责人 |
| `only_mine` | 只看本人负责 |
| `sale_order_id` | 订单 ID |
| `customer_id` | 客户 ID |
| `tracking_no` | 运单号 |
| `problem_type` | 问题类型 |
| `priority` | 优先级 |
| `create_time|>=` / `create_time|<=` | 创建时间范围 |
| `keyword` | 工单号、标题、订单号、运单号模糊搜索 |

### 7.2 工单详情

```text
GET /admin/customer-service-ticket/detail
```

参数：

| 参数 | 说明 |
| --- | --- |
| `id` | 工单 ID |

返回应包含：

- 工单主信息
- 关联订单
- 关联客户
- 关联快递任务
- 关联定制履约项
- 沟通/操作日志
- 附件

### 7.3 创建工单

```text
POST /admin/customer-service-ticket/add
```

核心参数：

| 参数 | 必填 | 说明 |
| --- | --- | --- |
| `ticket_type` | 是 | 工单类型 |
| `title` | 是 | 标题 |
| `problem_type` | 是 | 问题类型 |
| `description` | 是 | 问题描述 |
| `sale_order_id` | 否 | 关联订单 |
| `customer_id` | 否 | 关联客户 |
| `express_task_id` | 否 | 快递任务 |
| `tracking_no` | 否 | 运单号 |
| `owner_admin_id` | 否 | 负责人，不传则默认创建人或待分配 |
| `priority` | 否 | 优先级 |
| `customer_contact_channel` | 否 | 沟通渠道 |

### 7.4 编辑工单

```text
POST /admin/customer-service-ticket/edit
```

只允许修改非审计型字段，例如标题、问题描述、负责人、优先级、下一步动作等。状态流转建议使用独立接口。

### 7.5 添加沟通/处理记录

```text
POST /admin/customer-service-ticket/comment
```

参数：

| 参数 | 必填 | 说明 |
| --- | --- | --- |
| `id` | 是 | 工单 ID |
| `content` | 是 | 沟通摘要或处理说明 |
| `contact_channel` | 否 | 沟通渠道 |
| `customer_confirm_result` | 否 | 客户确认结果 |
| `next_action` | 否 | 下一步动作 |
| `next_action_time` | 否 | 下一步处理时间 |
| `file_ids` | 否 | 附件 ID 列表 |

### 7.6 分配、转派、催办

```text
POST /admin/customer-service-ticket/assign
POST /admin/customer-service-ticket/transfer
POST /admin/customer-service-ticket/urge
```

分配和转派由客服组长或管理层执行。催办可由工单关联岗位执行。

### 7.7 状态流转

```text
POST /admin/customer-service-ticket/status
POST /admin/customer-service-ticket/close
POST /admin/customer-service-ticket/reopen
POST /admin/customer-service-ticket/cancel
```

关闭条件：

- 必须有处理结果。
- 售后工单如果涉及退款、重做、补发或换货，必须有关联处理结果或关联订单。
- 运输中工单如果转售后，应记录转售后工单 ID。

### 7.8 自动创建运输中工单

内部服务方法建议：

```text
CustomerServiceTicketService::createOrAppendTransitTicketByExpressEvent(array $event)
```

去重条件：

- `ticket_type = in_transit`
- `sale_order_id`
- `tracking_no`
- `problem_type`
- `status < 100`
- `status <> -1`

命中未关闭工单时追加日志，不重复建单。

## 八、与现有业务的关系

### 8.1 与订单暂停的关系

订单暂停由 `sale_order.custom_pause_status` 表达，接口为：

- `POST /admin/sale-order/pause`
- `POST /admin/sale-order/resume`

客服工单只记录暂停原因、客户沟通和后续动作。是否能暂停或解除暂停，由订单暂停接口和岗位权限决定。

### 8.2 与生产工单的关系

生产工单仍使用 `custom_item_process`。客服工单可以关联 `custom_order_item_id` 或 `custom_item_process_id`，但不直接改变生产工序状态。

### 8.3 与物流的关系

运输中工单关联 `express_task_id` 和 `tracking_no`。物流轨迹状态变化可以触发自动建单或追加日志，但客服中心不作为物流详情查询系统。

### 8.4 与售后订单的关系

重做、补发、换货必须创建独立关联订单。客服工单记录售后诉求和审批结果，生产和发货仍走订单系统。

## 九、推荐落地步骤

### 第一阶段：客服工单基础闭环

1. 新增客服工单主表和日志表，附件复用现有文件关联能力。
2. 实现列表、详情、创建、编辑、添加记录、关闭、重开。
3. 支持发货前、运输中、售后三类工单。
4. 接入订单详情页，展示关联工单。

### 第二阶段：权限和协同

1. 增加普通客服、客服组长的数据和操作权限。
2. 实现分配、转派、催办。
3. 支持跨岗位协同处理，但客服不能越权修改订单、退款、生产和 Hold。

### 第三阶段：自动触发和售后联动

1. 物流异常自动创建或追加运输中工单。
2. 镜框缺货、处方异常等确认后生成发货前工单。
3. 售后工单联动重做、补发、换货审批和关联订单。

## 十、验收标准

| 编号 | 验收项 | 标准 |
| --- | --- | --- |
| CS-01 | 三类工单 | 支持发货前、运输中、售后三类工单独立筛选和查看。 |
| CS-02 | 沟通记录 | 沟通渠道、摘要、确认结果、下一步动作和附件可记录在工单上。 |
| CS-03 | 客服权限 | 普通客服可看部门工单，但只能编辑本人负责工单。 |
| CS-04 | 组长权限 | 客服组长可分配、转派、催办、关闭和重开工单。 |
| CS-05 | 订单权限隔离 | 客服不能通过工单接口修改订单、处方、退款、生产任务或解除 Hold。 |
| CS-06 | 物流去重 | 同一订单、运单号、问题类型已有未关闭运输中工单时不重复创建。 |
| CS-07 | 售后边界 | 退款、重做、补发、换货必须走对应审批和订单流程，客服工单只记录和推动。 |
| CS-08 | 审计日志 | 创建、分配、转派、状态变更、关闭、重开均有日志。 |
