# 镜框资料与供应状态需求分析

## 一、背景

本需求补充了镜框产品资料、镜片厂家加工参数、镜框采购单推送、镜框销售状态、供应商供应状态回传、SKU 销售等级和定期回访机制。

当前项目按定制化项目处理，镜框核心资料直接通过新增 `product` 表字段承载，不再走 `extra_field/product_extra`。

```text
product          # SKU、产品名、成本、重量、镜框尺寸、厂家信息、销售状态、供应状态、SKU等级
```

本需求不新增独立镜框主表。供应商状态历史、SKU 等级计算历史、供应商回传任务属于过程型数据，仍新增专用业务表。

## 二、需求拆分

本次需求可拆为五块：

1. 镜框产品资料维护。
2. 定制订单推送镜片厂家加工参数。
3. 镜框采购单推送厂家数据。
4. 镜框销售状态和供应状态。
5. SKU 自动分级和供应商定期回传。

## 三、镜框尺寸参数

### 3.1 用户补充字段

必填：

| 字段 | 说明 |
| --- | --- |
| 镜片宽度 | 单片镜片水平宽度 |
| 镜框宽度 | 整副镜框宽度 |
| 中梁宽度 | 鼻梁/中梁宽度 |
| 镜腿长度 | 镜腿长度 |

选填：

| 字段 | 说明 |
| --- | --- |
| 镜片斜对角线 | 镜片对角线长度 |

### 3.2 关键缺口：镜片高度来源

用户要求定制订单推给镜片厂家：

- 镜片高度。
- 中梁宽度。
- 镜片最大宽度。

但当前必填字段中没有“镜片高度”。仅靠镜片宽度、镜框宽度、中梁宽度、镜腿长度无法推导镜片高度。斜对角线虽然可与镜片宽度通过几何公式估算高度，但斜对角线是选填，并且实际镜片形状不是标准矩形，计算值未必能作为加工参数。

因此建议新增：

| 字段 | 是否必填 | 说明 |
| --- | --- | --- |
| 镜片高度 | 必填 | 推送镜片厂家加工使用 |

如果业务坚持不维护镜片高度，则推送镜片厂家时必须允许该字段为空，或在缺失时阻断定制订单进入镜片委外。

推荐口径：`镜片高度` 应作为镜框尺寸必填字段。

### 3.3 推荐产品表新增字段

建议在 `product` 表新增以下字段：

| 字段 | 说明 | 是否必填 |
| --- | --- | --- |
| `frame_lens_width` | 镜片宽度 | 是 |
| `frame_lens_height` | 镜片高度 | 是 |
| `frame_width` | 镜框宽度 | 是 |
| `frame_bridge_width` | 中梁宽度 | 是 |
| `frame_temple_length` | 镜腿长度 | 是 |
| `frame_lens_diagonal` | 镜片斜对角线 | 否 |
| `frame_size_display` | 框架上的尺寸，例如 `53-19-147` | 是 |
| `frame_size_unit` | 尺寸单位，默认 `mm` | 否 |

说明：

- `frame_bridge_width` 沿用既有字段名，但中文展示改为“中梁宽度”。
- `frame_size_display` 表示框架上印刷或标注的尺寸，不一定等同于系统结构化尺寸。
- 所有数值字段建议按 `decimal(8,2)` 处理，单位默认 `mm`。

## 四、推送镜片厂家的加工参数

### 4.1 推送字段

定制订单进入镜片委外时，需要推给镜片厂家：

| 推送字段 | 来源 | 规则 |
| --- | --- | --- |
| 镜片高度 | `frame_lens_height` | 必须有值 |
| 中梁宽度 | `frame_bridge_width` | 必须有值 |
| 镜片最大宽度 | `max(frame_lens_width, frame_lens_diagonal)` | 没有斜对角线时直接用镜片宽度 |

### 4.2 镜片最大宽度计算

```text
if frame_lens_diagonal 有值:
    lens_max_width = max(frame_lens_width, frame_lens_diagonal)
else:
    lens_max_width = frame_lens_width
```

注意：

- 这里按用户口径使用“镜片宽度和斜对角线取大值”。
- 字段名建议使用 `lens_max_width`，避免误解成“镜框宽度”。

### 4.3 订单快照

创建定制履约部件时，需要把镜框尺寸写入 `custom_item_component.params_snapshot`。

建议结构：

```json
{
  "frame": {
    "product_id": 123,
    "sku": "FRAME-001",
    "color": "Black",
    "size": {
      "lens_width": 53,
      "lens_height": 40,
      "frame_width": 138,
      "bridge_width": 19,
      "temple_length": 147,
      "lens_diagonal": 55,
      "lens_max_width": 55,
      "display": "53-19-147",
      "unit": "mm"
    }
  }
}
```

镜片委外单推送供应商时，应从订单快照读取，不应实时读取产品资料，避免产品后续改动影响历史订单。

## 五、镜框其余资料

### 5.1 必填字段

用户要求镜框资料必填：

| 字段 | 说明 | 建议存储 |
| --- | --- | --- |
| 厂家 | 镜框供应厂家 | `supplier_id` 或 `frame_manufacturer` |
| 厂家货号 | 厂家产品编号 | `frame_factory_item_no` |
| 厂家颜色编号 | 厂家颜色编码 | `frame_factory_color_no` |
| 镜框成本 | 镜框采购/成本价 | `product.cost` 或 `frame_cost` |
| 产品名 | ERP 产品名称 | `product.title` |
| SKU | ERP SKU | `product.code` |
| 颜色 | 镜框颜色 | `frame_color` |
| 原始定价 | 上架参考价，不参与利润核算 | `frame_original_price` |
| 包材费用 | ERP 内部成本核算和利润统计 | `product.packaging_cost` |
| 弹簧铰链 | 是否弹簧铰链 | `frame_spring_hinge` |
| 可调节鼻托 | 是否可调节鼻托 | `frame_adjustable_nose_pad` |
| 重量 | 镜框重量 | `product.weight` 或 `frame_weight` |
| 框架上的尺寸 | 如 `53-19-147` | `frame_size_display` |

说明：

- 用户文本中“颜色”重复出现一次，按一个字段处理。
- `product.cost` 当前是产品成本价。如果镜框产品就是单独 SKU，镜框成本可优先使用 `product.cost`。
- `原始定价` 只做上架参考，不能参与利润核算，不能覆盖销售价或成本价。

### 5.2 推荐产品表新增字段

除现有产品基础字段外，建议在 `product` 表新增：

| 字段 | 说明 | 是否必填 |
| --- | --- | --- |
| `frame_manufacturer` | 厂家名称，若没有 supplier_id 时使用 | 是 |
| `frame_factory_item_no` | 厂家货号 | 是 |
| `frame_factory_color_no` | 厂家颜色编号 | 是 |
| `frame_color` | 颜色 | 是 |
| `frame_original_price` | 原始定价，仅上架参考 | 是 |
| `packaging_cost` | 包材费用，仅内部核算 | 否 |
| `frame_spring_hinge` | 弹簧铰链，0否1是 | 是 |
| `frame_adjustable_nose_pad` | 可调节鼻托，0否1是 | 是 |
| `frame_sales_status` | 镜框销售状态 | 是 |
| `frame_supply_status` | 供应状态 | 是 |
| `frame_sku_grade` | SKU 等级 A/B/C/D | 是，系统自动 |
| `frame_supply_status_updated_at` | 最近供应状态更新时间 | 否 |
| `frame_supply_status_updated_by` | 最近供应状态回传方 | 否 |

如果供应状态需要保留历史记录，不能只存 `product.frame_supply_status`，应新增供应状态回传记录表。

## 六、镜框采购单推送厂家

### 6.1 推送字段

下镜框采购单时，只推给对应厂家：

| 推送字段 | 来源 |
| --- | --- |
| 厂家货号 | `frame_factory_item_no` |
| 厂家颜色编号 | `frame_factory_color_no` |
| 镜框成本 | `product.cost` 或 `frame_cost` |

明确不推：

- 产品名。
- ERP SKU。
- 原始定价。
- 弹簧铰链。
- 可调节鼻托。
- 重量。
- 销售状态。
- 其他内部运营信息。

### 6.2 采购单校验

创建镜框采购单时应校验：

1. 采购明细产品必须是镜框 SKU。
2. 必须存在对应厂家或供应商。
3. 必须有厂家货号。
4. 必须有厂家颜色编号。
5. 必须有镜框成本。

缺任意关键字段时，应阻断采购单推送厂家。

## 七、镜框销售状态

镜框销售状态由内部运营填写，是 ERP 自己的销售策略状态。

| 状态 | 说明 |
| --- | --- |
| 未上架 | 产品尚未上架销售 |
| 正常销售 | 我们有库存，或工厂有货 |
| 停止销售 | 我们没库存，工厂也不再生产 |
| 暂时隐藏 | 我们没库存，但工厂可以再生产 |

建议枚举：

```text
0 未上架
10 正常销售
20 暂时隐藏
30 停止销售
```

说明：

- 销售状态由内部运营控制。
- 供应商回传的供应状态可以辅助建议销售状态，但不应直接覆盖销售状态。
- Shopify 上架/下架/隐藏可根据销售状态做同步策略，但需要单独配置。

## 八、供应状态

供应状态由供应商填写并回传给 ERP。

| 状态 | 说明 |
| --- | --- |
| 供应正常 | 供应商有货或稳定可供 |
| 需要生产 | 当前无现货，但可生产 |
| 停产 | 供应商不再生产 |
| 未知 | 供应商未确认或状态过期 |

建议枚举：

```text
0 未知
10 供应正常
20 需要生产
30 停产
```

### 8.1 状态影响建议

供应状态不直接等同销售状态，但可给运营提示：

| 供应状态 | 系统建议 |
| --- | --- |
| 供应正常 | 可建议销售状态为正常销售 |
| 需要生产 | 若本地无库存，可建议销售状态为暂时隐藏 |
| 停产 | 若本地无库存，可建议销售状态为停止销售 |
| 未知 | 提醒运营或供应商确认 |

最终销售状态仍由运营确认。

## 九、供应状态回传场景

### 9.1 下采购单时回传

供应商接到采购单后，需要回传供应状态。

采购单维度可记录：

- 是否有货。
- 是否需要生产。
- 是否停产。
- 预计交期。
- 供应商备注。

同时更新产品 SKU 当前供应状态。

### 9.2 定期 SKU 状态回传

系统根据 SKU 销售等级，定期把 SKU 清单发给对应供应商填写供应状态。

频率：

| SKU 等级 | 回传频率 |
| --- | --- |
| A | 每周 |
| B | 每两周 |
| C | 每月 |
| D | 不发送 |

供应商填写后，系统更新：

- `frame_supply_status`
- `frame_supply_status_updated_at`
- 状态回传记录

## 十、SKU 自动分级

### 10.1 分级目标

根据销售数据自动标记 SKU 等级 A/B/C/D，用于决定供应商状态回传频率。

等级需要展示在 SKU 信息处。

### 10.2 推荐分级口径

第一阶段建议采用近 90 天销量分级：

| 等级 | 默认口径 | 回传频率 |
| --- | --- | --- |
| A | 近 90 天销量排名前 20% 或销量 >= 配置阈值 | 每周 |
| B | 近 90 天销量排名 20%-50% | 每两周 |
| C | 近 90 天有销量但不属于 A/B | 每月 |
| D | 近 90 天无销量 | 不发送 |

更稳妥的实现是将阈值做成公司配置：

```text
frame_sku_grade_window_days = 90
frame_sku_grade_a_min_qty
frame_sku_grade_b_min_qty
frame_sku_grade_c_min_qty
```

如果不配置阈值，则使用销量排名百分位。

### 10.3 分级计算范围

建议只统计：

- 正常销售订单。
- 已付款、已发货或已完成订单。
- 排除取消订单。
- 排除红人寄样订单。
- 排除退货已全额冲销订单。

统计指标优先级：

1. 销量数量。
2. 销售金额。
3. 最近成交时间。

第一阶段按销量数量即可。

## 十一、推荐新增表

### 11.1 供应状态回传记录表

如果需要追溯供应商每次填写结果，建议新增：

```sql
CREATE TABLE `hg_frame_supply_status_log` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `company_id` char(18) NOT NULL COMMENT '公司ID',
  `product_id` int(11) unsigned NOT NULL COMMENT '镜框产品ID',
  `supplier_id` int(11) unsigned DEFAULT NULL COMMENT '供应商ID',
  `source_type` tinyint(2) unsigned NOT NULL DEFAULT '1' COMMENT '来源: 1采购单 2定期回传 3人工维护',
  `source_id` int(11) unsigned DEFAULT NULL COMMENT '来源单据ID',
  `supply_status` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '供应状态: 0未知 10供应正常 20需要生产 30停产',
  `expected_delivery_days` int(11) unsigned DEFAULT NULL COMMENT '预计交期天数',
  `supplier_remark` varchar(500) DEFAULT NULL COMMENT '供应商备注',
  `create_admin_id` int(11) unsigned DEFAULT NULL COMMENT '创建人',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_product` (`company_id`, `product_id`),
  KEY `idx_supplier` (`supplier_id`),
  KEY `idx_status_time` (`supply_status`, `create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='镜框供应状态回传记录';
```

### 11.2 SKU 等级计算记录表

当前等级直接存 `product.frame_sku_grade`。如果要追溯历史等级，建议新增：

```sql
CREATE TABLE `hg_frame_sku_grade_log` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `company_id` char(18) NOT NULL COMMENT '公司ID',
  `product_id` int(11) unsigned NOT NULL COMMENT '镜框产品ID',
  `grade` char(1) NOT NULL COMMENT '等级 A/B/C/D',
  `stat_start_date` date NOT NULL COMMENT '统计开始日期',
  `stat_end_date` date NOT NULL COMMENT '统计结束日期',
  `sale_qty` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '销量',
  `sale_amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '销售金额',
  `rank_no` int(11) unsigned DEFAULT NULL COMMENT '排名',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_product_date` (`product_id`, `stat_end_date`),
  KEY `idx_grade` (`company_id`, `grade`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='镜框SKU等级计算记录';
```

### 11.3 供应商回传任务表

如果要支持每周/两周/月度自动发给供应商填写，建议新增任务表：

```sql
CREATE TABLE `hg_frame_supply_check_task` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `company_id` char(18) NOT NULL COMMENT '公司ID',
  `supplier_id` int(11) unsigned NOT NULL COMMENT '供应商ID',
  `period` varchar(32) NOT NULL COMMENT '任务周期',
  `grade_scope` varchar(32) DEFAULT NULL COMMENT '等级范围，如 A/B/C',
  `sku_count` int(11) unsigned NOT NULL DEFAULT '0' COMMENT 'SKU数量',
  `status` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '状态: 0待发送 10已发送 20部分回传 30已完成 80已过期',
  `send_time` datetime DEFAULT NULL COMMENT '发送时间',
  `finish_time` datetime DEFAULT NULL COMMENT '完成时间',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_supplier_period` (`supplier_id`, `period`),
  KEY `idx_status` (`company_id`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='镜框供应状态回传任务';
```

任务明细可记录每个 SKU：

```sql
CREATE TABLE `hg_frame_supply_check_task_detail` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `company_id` char(18) NOT NULL COMMENT '公司ID',
  `task_id` int(11) unsigned NOT NULL COMMENT '任务ID',
  `product_id` int(11) unsigned NOT NULL COMMENT '镜框产品ID',
  `sku_grade` char(1) NOT NULL COMMENT 'SKU等级',
  `factory_item_no` varchar(128) DEFAULT NULL COMMENT '厂家货号',
  `factory_color_no` varchar(128) DEFAULT NULL COMMENT '厂家颜色编号',
  `supply_status` tinyint(2) unsigned DEFAULT NULL COMMENT '供应状态',
  `expected_delivery_days` int(11) unsigned DEFAULT NULL COMMENT '预计交期天数',
  `supplier_remark` varchar(500) DEFAULT NULL COMMENT '供应商备注',
  `status` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '状态: 0待填写 10已填写',
  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_task` (`task_id`),
  KEY `idx_product` (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='镜框供应状态回传任务明细';
```

## 十二、接口建议

### 12.1 产品镜框资料维护

复用产品接口和产品表字段：

```text
POST /admin/product/add
POST /admin/product/edit
GET  /admin/product/detail
```

前端在镜框产品编辑页展示固定分组：

- 基础信息。
- 镜框尺寸。
- 厂家信息。
- 销售/供应状态。

### 12.2 镜片厂家加工参数读取

镜片委外单详情或供应商端详情需要返回：

```json
{
  "lens_factory_params": {
    "lens_height": 40,
    "bridge_width": 19,
    "lens_max_width": 55,
    "unit": "mm"
  }
}
```

来源优先级：

```text
custom_item_component.params_snapshot.frame.size
  -> product
  -> 缺失则报错或提示补录
```

### 12.3 采购单推送厂家

镜框采购单发给厂家时，后端只组装允许字段：

```json
{
  "factory_item_no": "F12345",
  "factory_color_no": "C07",
  "frame_cost": 12.5
}
```

### 12.4 供应状态回传

建议接口：

```text
POST /admin/frame-supply-status/update
POST /supplier/frame-supply-status/update
GET  /admin/frame-supply-status/log?product_id={product_id}
```

供应商端只能更新自己关联 SKU 的供应状态。

### 12.5 SKU 等级计算和任务

建议接口：

```text
POST /admin/frame-sku-grade/recalculate
GET  /admin/frame-sku-grade/list
POST /admin/frame-supply-check-task/generate
POST /admin/frame-supply-check-task/send
GET  /admin/frame-supply-check-task/detail?id={id}
POST /supplier/frame-supply-check-task/submit
```

## 十三、页面建议

### 13.1 镜框 SKU 信息页

需要展示：

- SKU。
- 产品名。
- 厂家。
- 厂家货号。
- 厂家颜色编号。
- 颜色。
- 镜框成本。
- 原始定价。
- 镜框尺寸。
- 镜片高度。
- 镜片斜对角线。
- 销售状态。
- 供应状态。
- SKU 等级 A/B/C/D。
- 最近供应状态更新时间。

### 13.2 供应商回传页

供应商只看到：

- 厂家货号。
- 厂家颜色编号。
- 当前要求确认的 SKU。
- 需要填写的供应状态。
- 预计交期。
- 备注。

不展示：

- ERP SKU。
- 产品名。
- 原始定价。
- 销售状态。
- 内部利润和销售数据。

## 十四、实施顺序

### 第一阶段：镜框资料字段补齐

1. 在 `product` 表新增镜框固定字段。
2. 产品编辑页增加镜框资料分组和必填校验。
3. 定制订单快照补充镜片高度、镜框宽度、斜对角线、镜片最大宽度。
4. 镜片委外单和供应商端展示镜片厂家加工参数。

### 第二阶段：采购单推送字段控制

1. 镜框采购单识别镜框 SKU。
2. 推送厂家时只包含厂家货号、厂家颜色编号、镜框成本。
3. 缺关键字段时阻断推送。

### 第三阶段：销售/供应状态

1. 产品页维护镜框销售状态。
2. 供应商端支持供应状态回传。
3. 保存供应状态历史记录。
4. 根据供应状态给运营销售状态建议。

### 第四阶段：SKU 自动分级和定期回访

1. 增加 SKU 等级计算任务。
2. SKU 信息页展示等级。
3. 根据等级生成供应商回传任务。
4. 支持供应商批量填写供应状态。

## 十五、待确认问题

1. 镜片高度是否确认改为镜框尺寸必填字段。
2. SKU 等级按销量、销售金额还是订单数计算。
3. A/B/C/D 分级使用绝对阈值还是销量排名百分位。
4. 供应商回传任务是系统内填写，还是通过 Excel/邮件/外部链接填写。
5. 镜框成本使用 `product.cost`，还是单独维护 `frame_cost`。
6. 原始定价是否需要同步 Shopify compare-at price 或仅 ERP 内部参考。
7. 销售状态是否自动同步 Shopify 上下架/隐藏。
8. 供应状态是否需要记录预计交期、最小起订量、生产周期等扩展信息。

## 十六、结论

本需求不是单纯补几个镜框字段，而是形成镜框 SKU 的供应链闭环：

```text
镜框资料维护
  -> 定制订单推镜片厂家加工参数
  -> 镜框采购单推厂家
  -> 供应商回传供应状态
  -> 系统根据销售数据给 SKU 分级
  -> 按等级定期要求供应商更新供应状态
  -> 运营根据销售状态和供应状态管理上架策略
```

推荐使用 `product` 表直接承载镜框主资料；对供应状态历史、SKU 等级历史和回传任务新增专用表，避免把过程型数据混入产品主记录。
