# 定制履约关键节点完成时间设计方案

## 1. 背景与目标

定制眼镜订单履约链路中，需要把关键环节的预计完成时间在销售订单审核时一次性填入，作为后续生产、供应商协同、质检、发货和逾期预警的依据。

本方案定义 4 个重要节点：

| 节点编码 | 节点名称 | 业务含义 |
| --- | --- | --- |
| `frame_ready` | 镜框准备 | 镜框已锁库、已到货，或已确认可进入后续组装。 |
| `lens_finished` | 镜片完成加工 | 左右镜片已完成加工，达到可入库或可交付组装状态。 |
| `glasses_qc_done` | 眼镜完成组装质检 | 镜框和镜片已组装成成品眼镜，并且质检通过。 |
| `logistics_shipped` | 物流发货 | 成品眼镜已完成仓库发货，生成物流交运结果。 |

核心目标：

1. 订单审核时生成每个定制履约单元的 4 个节点计划完成时间。
2. 节点真实完成时回写实际完成时间。
3. 计划时间作为订单履约承诺快照，不随之后 SLA 规则调整自动漂移。
4. 支持订单列表、生产看板、异常预警按节点查看逾期风险。

## 2. 时间口径

本需求中的“完成时间”建议拆成两个口径：

| 时间类型 | 字段含义 | 写入时机 | 是否可覆盖 |
| --- | --- | --- | --- |
| 计划完成时间 | 订单审核时根据当前库存、供应、委外、组装、质检、发货 SLA 推算出的预计完成时间。 | 销售订单审核通过后。 | 默认不覆盖，异常重排时生成新版本或记录调整日志。 |
| 实际完成时间 | 对应节点真实完成时的时间。 | 业务事件发生时回写。 | 原则上只写一次，人工修正需要记录日志。 |

订单审核时必须填入的是计划完成时间。实际完成时间由后续履约事件自动回写。

## 3. 数据模型建议

### 3.1 推荐落表位置

推荐优先落在 `custom_order_item`，因为定制履约是以“每副眼镜”为履约单元，一个销售订单可能包含多副定制眼镜，每副眼镜的镜框、镜片、组装和发货时间可能不同。

建议新增字段：

| 字段名 | 类型 | 说明 |
| --- | --- | --- |
| `frame_ready_plan_time` | datetime nullable | 镜框准备计划完成时间。 |
| `frame_ready_finish_time` | datetime nullable | 镜框准备实际完成时间。 |
| `lens_finish_plan_time` | datetime nullable | 镜片完成加工计划完成时间。 |
| `lens_finish_time` | datetime nullable | 镜片完成加工实际完成时间。 |
| `glasses_qc_plan_time` | datetime nullable | 眼镜完成组装质检计划完成时间。 |
| `glasses_qc_finish_time` | datetime nullable | 眼镜完成组装质检实际完成时间。 |
| `logistics_ship_plan_time` | datetime nullable | 物流发货计划完成时间。 |
| `logistics_ship_time` | datetime nullable | 物流发货实际完成时间。 |
| `node_time_calculated_at` | datetime nullable | 节点计划时间计算时间。 |
| `node_time_version` | int default 1 | 节点时间版本，用于异常重排。 |
| `node_time_rule_snapshot` | json nullable | 审核时使用的 SLA 和供应规则快照。 |

### 3.2 订单级汇总

订单维度不建议直接取任意一条明细的时间。多副眼镜订单应按如下规则汇总：

| 订单级节点 | 汇总规则 |
| --- | --- |
| 镜框准备 | 取所有定制履约单元 `frame_ready_plan_time` 的最大值。 |
| 镜片完成加工 | 取所有定制履约单元 `lens_finish_plan_time` 的最大值。 |
| 眼镜完成组装质检 | 取所有定制履约单元 `glasses_qc_plan_time` 的最大值。 |
| 物流发货 | 取所有定制履约单元 `logistics_ship_plan_time` 的最大值。 |

订单列表可以动态聚合展示；如果后续存在高频筛选和排序性能问题，再冗余到 `sale_order`。

## 4. 审核时计算规则

### 4.1 触发点

销售订单审核通过后，现有流程会进入定制履约准备逻辑：

```text
SaleOrder::afterAuditData()
  -> CustomFulfillmentPrepareService::prepareBySaleOrder()
```

节点时间计算应放在定制履约准备完成之后执行，因为此时已经生成或识别了：

1. 定制履约单元 `custom_order_item`。
2. 镜框、左镜片、右镜片等部件 `custom_item_component`。
3. 组装、质检、成品入库、发货等履约节点 `custom_item_process`。
4. 库存、采购、委外、供应商等执行路径。

建议新增服务：

```text
CustomFulfillmentNodeTimeService::initializeBySaleOrder($saleOrder, $adminId)
```

调用顺序：

```text
订单审核
  -> 创建定制履约数据
  -> 计算并写入 4 个关键节点计划完成时间
  -> 同步订单阶段和看板数据
```

### 4.2 基准时间

计划时间计算基准为销售订单审核时间：

```text
base_time = sale_order.audit_time
```

如果历史订单补算时没有审核时间，则使用当前系统时间，并在 `node_time_rule_snapshot` 中记录为补算。

### 4.3 镜框准备完成时间

计算逻辑：

| 镜框来源/状态 | 计划完成时间规则 |
| --- | --- |
| 已有可用库存并审核时完成锁库 | `base_time` 或审核当天工作截止时间。 |
| 需要采购，供应商供应状态为供应正常 | `base_time + 镜框采购 SLA`，优先使用采购单或供应商回传交期。 |
| 需要生产，供应状态为需要生产 | `base_time + 镜框生产 SLA + 采购到货 SLA`。 |
| 供应状态未知 | 使用保守 SLA，并标记 `supply_status_unknown` 风险。 |
| 停产或停止销售且无库存 | 不生成可靠承诺时间，订单进入异常或待人工确认。 |

实际完成回写事件：

1. 镜框库存锁定成功。
2. 镜框采购入库完成。
3. 镜框供应商确认可交付并进入可组装状态。

### 4.4 镜片完成加工时间

计算逻辑：

```text
lens_finished_plan_time = base_time + 镜片加工 SLA
```

如果左右镜片分别下单或分供应商加工，则取两侧镜片计划完成时间的最大值：

```text
lens_finished_plan_time = max(left_lens_plan_time, right_lens_plan_time)
```

优先级：

1. 镜片委外单明确交期。
2. 镜片供应商 SLA 规则。
3. 系统默认镜片加工 SLA。

实际完成回写事件：

1. 左右镜片委外单均已生产完成。
2. 左右镜片均已入库或被确认可进入组装。

### 4.5 眼镜完成组装质检时间

组装质检必须等待镜框和镜片都完成准备。

计算逻辑：

```text
assemble_start_plan_time = max(frame_ready_plan_time, lens_finished_plan_time)

glasses_qc_plan_time =
  assemble_start_plan_time
  + 组装 SLA
  + 质检 SLA
```

如果采用委外组装，则优先使用委外组装单 SLA：

```text
glasses_qc_plan_time =
  assemble_start_plan_time
  + 委外组装接单 SLA
  + 委外组装生产 SLA
  + 委外组装入库/质检 SLA
```

实际完成回写事件：

1. 组装工序完成。
2. 质检通过。
3. 成品眼镜进入可发货状态。

质检不通过时，不写入 `glasses_qc_finish_time`，应进入返工或异常流程；如果返工后再次质检通过，再写入实际完成时间。

### 4.6 物流发货时间

物流发货依赖眼镜完成组装质检。

计算逻辑：

```text
logistics_ship_plan_time =
  glasses_qc_plan_time
  + 成品入库/拣货 SLA
  + 打包面单 SLA
  + 物流交运 SLA
```

如果订单需要人工确认地址、补款、合单、拆单或特殊物流渠道，则应增加人工处理 SLA 或标记风险。

实际完成回写事件：

1. 系统发货接口成功。
2. 出库单审核完成。
3. 物流面单创建并完成交运。

## 5. SLA 配置建议

节点时间不建议写死在代码中，应通过配置或数据表维护。

建议维护以下 SLA 项：

| SLA 编码 | 说明 |
| --- | --- |
| `frame_stock_ready_hours` | 镜框库存准备耗时。 |
| `frame_purchase_hours` | 镜框采购默认耗时。 |
| `frame_production_hours` | 镜框供应商生产默认耗时。 |
| `lens_production_hours` | 镜片加工默认耗时。 |
| `assemble_hours` | 内部组装默认耗时。 |
| `qc_hours` | 质检默认耗时。 |
| `finished_stock_in_hours` | 成品入库或可发货准备耗时。 |
| `ship_hours` | 仓库发货默认耗时。 |
| `outsource_assemble_accept_hours` | 委外组装接单耗时。 |
| `outsource_assemble_produce_hours` | 委外组装生产耗时。 |
| `outsource_assemble_stock_in_hours` | 委外组装回来到入库/质检耗时。 |

如果已有委外加工 SLA 规则表，可复用其规则口径；新增规则只补齐镜框、镜片、组装质检、发货这 4 个节点需要的计算项。

## 6. 逾期与异常规则

节点逾期判断：

```text
当前时间 > 节点计划完成时间
and 节点实际完成时间为空
```

逾期后建议只生成预警，不直接改变业务状态；是否自动进入异常状态应由配置控制。

异常处理规则：

| 场景 | 处理方式 |
| --- | --- |
| 镜框停产或无货 | 节点时间标记不可承诺，订单进入待人工确认。 |
| 镜片加工失败 | 保留原计划时间，记录异常；返工时生成新版本计划。 |
| 质检不通过 | 不写质检完成时间，进入返工或异常。 |
| 物流下单失败 | 不写发货实际时间，保留待发货状态并预警。 |
| 人工改期 | 记录改期前后时间、操作人、原因。 |

## 7. 页面与接口影响

### 7.1 订单审核

订单审核接口需要在审核成功后自动计算节点计划时间。原则上不要求前端手工填写每个节点时间，避免运营人员重复录入。

如果业务要求人工干预，可在审核弹窗中提供可选覆盖字段：

| 字段 | 说明 |
| --- | --- |
| `frame_ready_plan_time` | 覆盖镜框准备计划时间。 |
| `lens_finish_plan_time` | 覆盖镜片完成加工计划时间。 |
| `glasses_qc_plan_time` | 覆盖组装质检完成计划时间。 |
| `logistics_ship_plan_time` | 覆盖物流发货计划时间。 |
| `node_time_remark` | 人工调整原因。 |

自动计算仍应作为默认值，人工覆盖必须记录日志。

### 7.2 订单列表和详情

订单列表建议展示：

1. 当前履约阶段。
2. 4 个关键节点计划完成时间。
3. 已完成节点的实际完成时间。
4. 逾期节点红色标识。
5. 多副眼镜订单的汇总时间。

订单详情建议展示每副眼镜的节点明细，便于定位是哪一副、哪个部件导致延期。

### 7.3 生产看板

生产看板可以按节点筛选：

| 筛选项 | 说明 |
| --- | --- |
| 镜框准备逾期 | 镜框计划时间已过但未完成。 |
| 镜片加工逾期 | 镜片计划时间已过但未完成。 |
| 组装质检逾期 | 组装质检计划时间已过但未完成。 |
| 发货逾期 | 发货计划时间已过但未发货。 |

### 7.4 对外状态同步

如果需要同步 Shopify 或其他第三方平台，不建议直接暴露所有内部节点时间。可同步：

1. 当前履约阶段。
2. 下一关键节点名称。
3. 下一关键节点预计完成时间。
4. 最终预计发货时间。

## 8. 实施步骤

### 阶段一：字段与审核计算

1. `custom_order_item` 增加 4 个节点计划时间和实际完成时间字段。
2. 新增 `CustomFulfillmentNodeTimeService`。
3. 在销售订单审核完成并生成定制履约数据后调用节点时间初始化。
4. 订单列表和详情展示节点计划时间。

### 阶段二：实际完成时间回写

1. 镜框锁库或采购入库时回写 `frame_ready_finish_time`。
2. 镜片生产完成或入库时回写 `lens_finish_time`。
3. 组装质检通过时回写 `glasses_qc_finish_time`。
4. 发货成功时回写 `logistics_ship_time`。

### 阶段三：预警与改期

1. 增加节点逾期扫描任务。
2. 增加人工改期接口和日志。
3. 增加返工、重做、异常恢复后的节点时间版本管理。
4. 对接生产看板、供应商提醒和第三方状态同步。

## 9. 推荐结论

1. 审核时填入的是 4 个关键节点的计划完成时间，不是实际完成时间。
2. 字段建议落在 `custom_order_item`，订单维度通过聚合展示。
3. 计算应在 `CustomFulfillmentPrepareService::prepareBySaleOrder()` 之后执行。
4. 计划时间要保存审核时规则快照，避免后续 SLA 配置变化影响已审核订单。
5. 实际完成时间由镜框准备、镜片入库/完成、质检通过、发货成功等业务事件自动回写。
