# 委外工单时效性管理设计方案

## 一、背景

来源：`doc/jykj/2027-07-30会议纪要-整理版.md`

会议中明确提出：

1. 委外加工需要增加超时提醒机制，不同镜片配置对应不同加工期限。
2. 供应商接单等待需要有时长控制，例如 24 小时未接单要同时提示内部人员和供应商。
3. 流程需要维护预计完成时间，便于客服、生产、供应商协同跟进。
4. 订单暂停、回退、供应商拒单、生产问题反馈都会影响委外时效，需要有记录和重新计算机制。

本方案聚焦 `entrust` 委外工单的时效性管理，优先复用现有模型，补充必要的 SLA 规则、预警、通知和统计能力。

## 二、目标

### 2.1 业务目标

1. 委外单生成时自动计算预计接单截止时间和预计交付时间。
2. 不同镜片属性组合、供应商、部件类型可配置不同加工周期，例如防蓝光、染色、膜层、折射率、处方范围等。
3. 供应商未按时接单、未按时发货、未按时入库时自动预警。
4. 预警同时反馈到：
   - 内部订单面板
   - 委外单列表
   - 供应商门户
   - 生产看板
   - 系统消息
5. 供应商反馈异常、拒单、订单暂停、流程回退时，时效状态可被重新计算或冻结。
6. 沉淀供应商真实交付时效，用于供应商分析和后续分配策略。

### 2.2 技术目标

1. 复用现有 `entrust` 作为委外工单主表。
2. 复用 `custom_item_component` 作为镜片部件级进度载体。
3. 不另起一套平行工单系统。
4. 时效规则结构化，避免只写在备注或 JSON 中。
5. 定时任务幂等执行，重复扫描不会重复生成相同提醒。

## 三、现有基础

### 3.1 现有表与字段

| 模型 | 可复用字段 | 用途 |
| --- | --- | --- |
| `entrust` | `supplier_id` | 委外供应商 |
| `entrust` | `product_id` | 委外产品/镜片产品，仅作执行结果和统计维度，不作为 SLA 规则的主匹配条件 |
| `entrust` | `plan_start_time` | 计划开始时间 |
| `entrust` | `plan_end_time` | 计划完成时间 |
| `entrust` | `status` | 委外单状态 |
| `entrust` | `audit_status` | 审核状态 |
| `entrust` | `tracking_num` | 供应商寄回物流单号 |
| `entrust` | `shipped_time` | 供应商发货时间 |
| `entrust` | `stock_in_time` | 接收入库时间 |
| `custom_item_component` | `entrust_id` | 关联委外单 |
| `custom_item_component` | `supplier_status` | 供应商侧镜片状态 |
| `custom_item_component` | `supplier_eta_time` | 供应商预计完成时间 |
| `custom_item_component` | `expected_finish_time` | 系统预计完成时间 |
| `custom_item_component` | `is_abnormal` / `abnormal_reason` | 部件异常标记 |

### 3.2 当前状态口径

`ENTRUST_STATUS` 当前枚举：

| 状态 | 含义 |
| --- | --- |
| `0` | 待审核 |
| `10` | 待接单 |
| `20` | 加工中 |
| `30` | 已发货 |
| `50` | 待入库 |
| `70` | 部分入库 |
| `90` | 近全合格 |
| `100` | 已完成 |

`custom_item_component.supplier_status` 当前供应商侧状态：

| 状态 | 建议含义 |
| --- | --- |
| `0` | 待派单/未审核 |
| `5` | 待接单 |
| `10` | 已接单 |
| `20` | 加工中 |
| `30` | 已生产 |
| `40` | 待发货 |
| `50` | 已发货/已返回 |
| `80` | 异常/拒单 |

## 四、核心设计

### 4.1 时效节点

委外工单至少要管理 4 个时效节点：

| 节点 | 触发时间 | 截止时间 | 超时判断 |
| --- | --- | --- | --- |
| 接单 | 委外单审核通过，进入待接单 | `accept_deadline_time` | 当前时间超过截止时间且仍未接单 |
| 加工 | 供应商接单 | `produce_deadline_time` | 当前时间超过截止时间且未生产完成 |
| 发货 | 供应商生产完成 | `ship_deadline_time` | 当前时间超过截止时间且未填写物流单号 |
| 入库 | 供应商发货 | `stock_in_deadline_time` | 当前时间超过截止时间且未接收入库 |

MVP 阶段可先实现两个关键节点：

1. 接单超时：`entrust.status = 10` 且超过接单截止时间。
2. 交付超时：`entrust.status < 30/50/100` 且超过预计交付时间。

### 4.2 SLA 规则优先级

委外预计时效按以下优先级计算，越靠前优先级越高：

```text
手动指定时间
  > 供应商 + 镜片属性组合规则
  > 镜片属性组合规则
  > 供应商默认时效
  > 公司默认时效
```

建议规则来源：

| 来源 | 说明 |
| --- | --- |
| 手动指定 | 内部人员或供应商填写 `supplier_eta_time` |
| `custom_item_component.params_snapshot` / `entrust.custom_params` | 委外工单实际镜片属性来源，例如防蓝光、染色、膜层、折射率、球镜、柱镜、ADD |
| `hg_optical_component_product_rule` | 可作为镜片属性解析和默认供应商匹配来源，但 SLA 不应只绑定最终 `product_id` |
| `supplier` | 供应商默认接单时长、默认加工时长 |
| 公司配置 | 无规则时兜底，例如接单 24 小时，加工 3 天 |

### 4.3 推荐新增 SLA 规则表

为避免把复杂时效规则塞进 `entrust`，建议新增规则表。

SLA 规则不要以 `product_id` 作为核心匹配条件。镜片产品 ID 往往是系统根据规则匹配出来的执行结果，而时效真正受镜片属性影响，例如防蓝光、染色、渐进、多焦点、折射率、膜层、处方范围等。因此 SLA 规则应采用“属性条件组合”的方式匹配。

```sql
CREATE TABLE `hg_entrust_sla_rule` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `company_id` varchar(32) NOT NULL,
  `title` varchar(128) NOT NULL DEFAULT '' COMMENT '规则名称',
  `supplier_id` int(11) unsigned DEFAULT NULL COMMENT '供应商ID，空表示不限供应商',
  `component_type` varchar(64) DEFAULT NULL COMMENT '部件类型：lens_left/lens_right/lens_pair/assemble，空表示不限',
  `scope_type` varchar(32) NOT NULL DEFAULT 'lens' COMMENT '规则范围：lens/assemble/all',
  `condition_mode` varchar(16) NOT NULL DEFAULT 'all' COMMENT '条件关系：all全部满足/any任一满足',
  `condition_summary` varchar(255) DEFAULT NULL COMMENT '条件摘要，便于列表展示',
  `match_conditions` json DEFAULT NULL COMMENT '属性匹配条件JSON，来源于params_snapshot/custom_params',
  `priority` int(11) NOT NULL DEFAULT '100' COMMENT '优先级，越小越优先',
  `accept_timeout_hours` int(11) NOT NULL DEFAULT '24' COMMENT '接单超时时长',
  `produce_timeout_hours` int(11) NOT NULL DEFAULT '72' COMMENT '加工超时时长',
  `ship_timeout_hours` int(11) NOT NULL DEFAULT '24' COMMENT '生产完成后发货超时时长',
  `stock_in_timeout_hours` int(11) NOT NULL DEFAULT '168' COMMENT '发货后入库超时时长',
  `warning_before_hours` int(11) NOT NULL DEFAULT '12' COMMENT '提前预警小时数',
  `is_enabled` tinyint(1) NOT NULL DEFAULT '1',
  `remark` varchar(255) DEFAULT NULL,
  `create_admin_id` int(11) unsigned DEFAULT NULL,
  `edit_admin_id` int(11) unsigned DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_company_supplier` (`company_id`, `supplier_id`, `is_enabled`),
  KEY `idx_company_component` (`company_id`, `component_type`, `scope_type`, `is_enabled`),
  KEY `idx_company_priority` (`company_id`, `priority`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='委外工单SLA规则';
```

`match_conditions` 示例：

```json
[
  {"field": "blue_light", "op": "=", "value": true},
  {"field": "refractive_index", "op": "in", "value": ["1.67", "1.74"]},
  {"field": "coating", "op": "in", "value": ["防蓝光膜", "高清防蓝光膜"]},
  {"field": "sphere_abs", "op": "between", "value": [0, 600]},
  {"field": "cylinder_abs", "op": "<=", "value": 200}
]
```

支持的操作符建议：

| 操作符 | 说明 | 示例 |
| --- | --- | --- |
| `=` | 等于 | `blue_light = true` |
| `!=` | 不等于 | `lens_type != stock` |
| `in` | 在枚举中 | `coating in [...]` |
| `not_in` | 不在枚举中 | `color not_in [...]` |
| `between` | 数值区间 | `sphere_abs between [0, 600]` |
| `>` / `>=` / `<` / `<=` | 数值比较 | `cylinder_abs <= 200` |
| `exists` | 属性存在 | `prism exists` |
| `contains` | 数组或文本包含 | `options contains blue_light` |

规则示例：

| 规则 | 条件 | 加工时效 |
| --- | --- | --- |
| 普通单光镜片 | 无特殊属性 | 48 小时 |
| 防蓝光镜片 | `blue_light = true` | 72 小时 |
| 高折射率防蓝光 | `blue_light = true` 且 `refractive_index in 1.67/1.74` | 96 小时 |
| 染色镜片 | `tint = true` 或 `color != empty` | 120 小时 |
| 高度数镜片 | `sphere_abs > 600` 或 `cylinder_abs > 200` | 120 小时 |

MVP 如果暂不建表，也可以先在 `config/autoload/variable.php` 或公司配置中固化默认值，但正式方案建议建表。

### 4.4 推荐新增时效事件表

为保证提醒可追踪、可去重、可统计，建议新增时效事件表：

```sql
CREATE TABLE `hg_entrust_sla_event` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `company_id` varchar(32) NOT NULL,
  `entrust_id` int(11) unsigned NOT NULL,
  `custom_item_component_id` int(11) unsigned DEFAULT NULL,
  `event_type` varchar(64) NOT NULL COMMENT 'accept_warning/accept_timeout/produce_warning/produce_timeout/ship_timeout/stock_in_timeout',
  `deadline_time` datetime DEFAULT NULL COMMENT '截止时间',
  `event_time` datetime NOT NULL COMMENT '事件触发时间',
  `level` tinyint(2) NOT NULL DEFAULT '1' COMMENT '1提醒 2预警 3严重',
  `status` tinyint(2) NOT NULL DEFAULT '0' COMMENT '0未处理 10已处理 20已忽略',
  `message` varchar(255) NOT NULL DEFAULT '',
  `notify_internal` tinyint(1) NOT NULL DEFAULT '1',
  `notify_supplier` tinyint(1) NOT NULL DEFAULT '0',
  `handled_admin_id` int(11) unsigned DEFAULT NULL,
  `handled_time` datetime DEFAULT NULL,
  `handle_remark` varchar(255) DEFAULT NULL,
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_entrust_event` (`company_id`, `entrust_id`, `custom_item_component_id`, `event_type`, `deadline_time`),
  KEY `idx_company_status` (`company_id`, `status`, `event_time`),
  KEY `idx_entrust` (`entrust_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='委外工单时效事件';
```

说明：

1. 唯一索引用于保证同一节点、同一截止时间只生成一次提醒。
2. 如果截止时间被重新计算，新的 `deadline_time` 会生成新的事件。
3. 事件处理不直接改变委外单业务状态，只用于提醒、看板和追责。

### 4.5 `entrust` 字段扩展建议

为提升查询效率，建议在 `entrust` 上补充冗余字段：

| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `accept_deadline_time` | datetime | 接单截止时间 |
| `produce_deadline_time` | datetime | 加工完成截止时间 |
| `ship_deadline_time` | datetime | 发货截止时间 |
| `stock_in_deadline_time` | datetime | 入库截止时间 |
| `sla_status` | tinyint | `0` 正常，`10` 临近超时，`20` 已超时，`30` 已处理 |
| `sla_level` | tinyint | `0` 无，`1` 提醒，`2` 预警，`3` 严重 |
| `sla_reason` | varchar | 当前时效异常原因 |
| `sla_pause_seconds` | int | 暂停累计秒数 |
| `sla_paused_at` | datetime | 当前暂停开始时间 |

如果短期不扩展 `entrust`，可先完全通过 `entrust_sla_event` + `plan_end_time` 实现，但列表筛选性能会差一些。

## 五、时效计算规则

### 5.1 委外单生成时

触发点：定制履约生成镜片委外单。

处理逻辑：

1. 根据 `supplier_id`、`component_type` 和镜片属性条件匹配 SLA 规则。
   - 镜片属性优先读取 `custom_item_component.params_snapshot`。
   - 委外单生成后也可读取 `entrust.custom_params`。
   - `product_id` 只作为兜底统计维度，不作为主要匹配条件。
2. 写入：
   - `entrust.plan_start_time = 当前时间`
   - `entrust.accept_deadline_time = 当前时间 + accept_timeout_hours`
   - `entrust.produce_deadline_time = 当前时间 + accept_timeout_hours + produce_timeout_hours`
   - `entrust.plan_end_time = produce_deadline_time`
3. 回写关联部件：
   - `custom_item_component.expected_finish_time = entrust.produce_deadline_time`
   - `custom_item_component.supplier_eta_time = entrust.produce_deadline_time`

### 5.2 审核通过进入待接单时

当前定制履约自动生成委外单已默认审核通过，因此状态会进入：

```text
entrust.status = 10
custom_item_component.supplier_status = 5
```

此时开始计算接单倒计时。

### 5.3 供应商接单时

供应商点击接单后：

1. `custom_item_component.supplier_status = 10`
2. `entrust.status` 推进到 `20` 或保持后续由组件状态同步为 `20`
3. 重新计算加工截止时间：

```text
produce_deadline_time = 接单时间 + produce_timeout_hours
```

如果供应商填写预计完成时间：

```text
supplier_eta_time = 供应商填写时间
produce_deadline_time = min(系统规则时间, 供应商承诺时间)
```

业务解释：

1. 系统规则是合同/内部预期。
2. 供应商承诺时间如果更早，以供应商承诺时间作为预警依据。
3. 供应商承诺时间如果更晚，需要内部人员确认后才覆盖系统规则。

### 5.4 生产完成时

供应商将镜片状态改为已生产：

1. `custom_item_component.supplier_status = 30`
2. 写入 `supplier_finish_time`
3. 计算发货截止时间：

```text
ship_deadline_time = supplier_finish_time + ship_timeout_hours
```

### 5.5 供应商发货时

供应商录入物流单号：

1. `entrust.status = 30`
2. `entrust.tracking_num = 物流单号`
3. `entrust.shipped_time = 当前时间`
4. `custom_item_component.supplier_status = 50`
5. 计算入库截止时间：

```text
stock_in_deadline_time = shipped_time + stock_in_timeout_hours
```

### 5.6 接收入库时

接收入库完成后：

1. `entrust.stock_in_time = 当前时间`
2. `entrust.status` 根据入库数量变为 `70/90/100`
3. 关联部件 `status = 50`
4. 清除或关闭未处理的 SLA 事件。

## 六、预警策略

### 6.1 预警类型

| 类型 | 条件 | 通知对象 |
| --- | --- | --- |
| 接单临近超时 | 距接单截止时间小于 `warning_before_hours` 且未接单 | 内部、供应商 |
| 接单超时 | 超过接单截止时间且未接单 | 内部、供应商 |
| 加工临近超时 | 距加工截止时间小于 `warning_before_hours` 且未生产完成 | 内部、供应商 |
| 加工超时 | 超过加工截止时间且未生产完成 | 内部、供应商 |
| 发货超时 | 已生产但超过发货截止时间未发货 | 内部、供应商 |
| 入库超时 | 已发货但超过入库截止时间未入库 | 内部 |

### 6.2 升级规则

建议按超时长度升级：

| 超时时长 | 等级 | 处理 |
| --- | --- | --- |
| 未超时，仅临近 | `1` 提醒 | 列表黄色提醒 |
| 超时 0-12 小时 | `2` 预警 | 系统消息提醒负责人 |
| 超时 12-24 小时 | `3` 严重 | 提醒负责人和主管 |
| 超时超过 24 小时 | `3` 严重 | 每日重复提醒，进入异常看板 |

### 6.3 异常状态联动

临近超时不建议直接标记异常，只做提醒。

真正超时后：

1. `entrust.sla_status = 20`
2. `entrust.sla_reason = 具体超时原因`
3. 关联 `custom_item_component.is_abnormal = 1`
4. `custom_item_component.abnormal_reason = 委外加工超时：xxx`
5. 触发 `CustomOrderStageSyncService`，让订单进入异常展示。

如果超时后供应商完成动作：

1. 保留历史 `entrust_sla_event`
2. 当前 `sla_status` 可更新为 `30 已处理`
3. 部件异常是否自动清除建议由内部人员确认，不建议自动清除。

## 七、暂停、拒单、回退处理

### 7.1 订单暂停

订单暂停时，如果委外单尚未接单：

1. 可同步暂停供应商端操作。
2. 冻结 SLA 倒计时。
3. 写入：

```text
entrust.sla_paused_at = 当前时间
```

恢复订单时：

```text
暂停时长 = 当前时间 - sla_paused_at
所有未完成 deadline_time 顺延暂停时长
sla_pause_seconds += 暂停时长
sla_paused_at = null
```

如果委外单已接单或加工中：

1. 不建议默认暂停供应商生产。
2. 需要内部选择：
   - 继续生产
   - 暂停生产
   - 取消委外
3. 不同选择决定 SLA 是否冻结。

### 7.2 供应商拒单

供应商拒单时：

1. `custom_item_component.supplier_status = 80`
2. `custom_item_component.is_abnormal = 1`
3. `custom_item_component.abnormal_reason = 供应商拒单：原因`
4. `entrust.status` 可保持异常状态或作废，建议新增 `ENTRUST_STATUS = 80 拒单/异常`。
5. 关闭当前委外单未处理 SLA 事件。
6. 订单面板提示需要重新分配供应商。

重新分配供应商时：

1. 创建新的委外单。
2. 按新供应商重新计算 SLA。
3. 原委外单保留历史，不直接覆盖。

### 7.3 流程回退或重做

如果质检不通过需要重新委外：

1. 原委外单保留完成或异常记录。
2. 新增重做委外单。
3. 新委外单重新计算 SLA。
4. `custom_process_log` 记录回退原因和新旧委外单关联。

## 八、定时任务设计

### 8.1 任务名称

```text
CustomEntrustSlaInspection
```

### 8.2 执行频率

建议每 10 分钟执行一次：

```text
*/10 * * * *
```

### 8.3 扫描范围

只扫描未完成委外单：

```text
entrust.status in (10, 20, 30, 50, 70, 90)
entrust.audit_status = 10
entrust.status <> -1
```

排除：

1. 已完成 `status = 100`
2. 已作废 `status = -1`
3. 当前 SLA 已暂停且 `sla_paused_at is not null`

### 8.4 处理流程

```text
查询待检查委外单
  -> 判断当前状态对应节点
  -> 读取 deadline_time
  -> 判断临近超时或已超时
  -> upsert entrust_sla_event
  -> 更新 entrust.sla_status / sla_level / sla_reason
  -> 必要时标记 custom_item_component 异常
  -> 发送系统消息
```

## 九、通知设计

### 9.1 内部通知

通知对象建议按优先级：

1. 销售订单负责人 `sale_order.owner_admin_id`
2. 委外单负责人 `entrust.main_admin_id`
3. 订单审核人或创建人
4. 生产/采购主管角色

通知内容：

```text
委外单 ETxxx 接单超时
供应商：xxx
产品：xxx
销售订单：内部可见订单号
截止时间：yyyy-mm-dd hh:mm:ss
当前状态：待接单
```

### 9.2 供应商通知

供应商端只显示脱敏内容：

```text
委外单：短单号
镜片参数：可加工参数
产品基础信息：可展示字段
截止时间：yyyy-mm-dd hh:mm:ss
提醒原因：待接单超时/加工超时
```

不展示：

1. Shopify 原始订单号
2. 销售 SKU 价格
3. 客户敏感信息
4. 内部成本和利润

## 十、接口设计

### 10.1 Admin 端

#### 委外时效事件列表

```text
GET /admin/entrust-sla-event/index
```

参数：

| 参数 | 说明 |
| --- | --- |
| `event_type` | 事件类型 |
| `status` | 处理状态 |
| `supplier_id` | 供应商 |
| `entrust_id` | 委外单 |
| `level` | 预警等级 |
| `deadline_time|>=` | 截止时间开始 |
| `deadline_time|<=` | 截止时间结束 |

#### 处理时效事件

```text
POST /admin/entrust-sla-event/handle
```

参数：

| 参数 | 说明 |
| --- | --- |
| `id` | 事件 ID |
| `status` | `10` 已处理 / `20` 已忽略 |
| `handle_remark` | 处理说明 |

#### 手动调整委外预计时间

```text
POST /admin/entrust/update-sla
```

参数：

| 参数 | 说明 |
| --- | --- |
| `id` | 委外单 ID |
| `accept_deadline_time` | 接单截止时间 |
| `produce_deadline_time` | 加工截止时间 |
| `ship_deadline_time` | 发货截止时间 |
| `stock_in_deadline_time` | 入库截止时间 |
| `reason` | 调整原因 |

### 10.2 供应商端

#### 接单

```text
POST /admin/supplier-entrust/accept
```

参数：

| 参数 | 说明 |
| --- | --- |
| `entrust_id` | 委外单 ID |
| `price` | 供应商填写价格 |
| `supplier_eta_time` | 供应商承诺完成时间 |
| `remark` | 备注 |

#### 拒单

```text
POST /admin/supplier-entrust/reject
```

参数：

| 参数 | 说明 |
| --- | --- |
| `entrust_id` | 委外单 ID |
| `reason` | 拒单原因 |

#### 反馈生产问题

```text
POST /admin/supplier-entrust/feedback
```

参数：

| 参数 | 说明 |
| --- | --- |
| `entrust_id` | 委外单 ID |
| `component_id` | 可选，具体镜片部件 |
| `problem_type` | 问题类型 |
| `content` | 问题说明 |
| `images` | 图片 |

## 十一、列表和看板展示

### 11.1 委外单列表

增加字段：

| 字段 | 说明 |
| --- | --- |
| `accept_deadline_time` | 接单截止时间 |
| `produce_deadline_time` | 加工截止时间 |
| `current_sla_status` | 当前时效状态 |
| `current_sla_reason` | 当前时效原因 |
| `timeout_hours` | 已超时小时数 |
| `warning_level` | 预警等级 |

### 11.2 订单面板

订单面板中镜片状态展示：

```text
镜片：委外中
供应商：xxx
预计完成：2027-08-02 18:00
状态：接单超时 / 加工超时 / 正常
```

异常时红色展示，并进入异常筛选。

### 11.3 供应商门户

供应商端展示倒计时：

```text
待接单：剩余 03:20
加工中：预计 2027-08-02 18:00 完成
已超时：超时 5 小时
```

## 十二、统计分析

### 12.1 供应商时效指标

| 指标 | 说明 |
| --- | --- |
| 平均接单时长 | 审核通过到供应商接单 |
| 接单超时率 | 接单超时单数 / 总委外单数 |
| 平均加工时长 | 接单到生产完成或发货 |
| 加工超时率 | 加工超时单数 / 总委外单数 |
| 平均入库时长 | 发货到接收入库 |
| 准时交付率 | 未超时完成单数 / 总完成单数 |
| 异常反馈次数 | 供应商反馈问题次数 |
| 拒单率 | 拒单单数 / 分配单数 |

### 12.2 后续用于供应商分配

后续镜片供应商自动匹配可引入评分：

```text
供应商评分 = 价格评分 * 30% + 准时率 * 40% + 异常率评分 * 20% + 接单响应评分 * 10%
```

用于在多个供应商都可加工时自动选择更优供应商。

## 十三、实施阶段

### 阶段一：MVP

1. 委外单生成时写入 `plan_end_time`。
2. 回写 `custom_item_component.expected_finish_time` 和 `supplier_eta_time`。
3. 实现接单超时、加工超时扫描。
4. 超时后标记 `custom_item_component.is_abnormal = 1`。
5. 订单面板和生产看板展示超时。

### 阶段二：规则化

1. 新增 `entrust_sla_rule`。
2. 支持按供应商、部件类型、镜片属性条件配置 SLA。
3. 支持供应商接单时填写预计完成时间。
4. 支持人工调整预计时间并留痕。

### 阶段三：事件化和通知闭环

1. 新增 `entrust_sla_event`。
2. 实现临近超时提醒、超时提醒、处理闭环。
3. 接入系统消息。
4. 供应商门户展示提醒。

### 阶段四：分析和分配优化

1. 供应商时效分析。
2. 供应商准时率、拒单率、异常率统计。
3. 自动供应商选择时引入时效评分。

## 十四、关键注意事项

1. 时效预警不等同于业务失败，不能直接自动取消委外单。
2. 超时事件必须保留历史，即使供应商后续完成也不能删除。
3. 订单暂停时是否冻结 SLA 必须有明确操作选择，不能默认冻结所有委外单。
4. 供应商承诺时间晚于系统规则时，应需要内部确认。
5. 客服侧只展示预计完成和异常原因，不展示供应商价格、成本、利润等敏感信息。
6. 组装委外可以复用同一套 SLA 机制，但 `component_type` 应区分为 `assemble`。
