# 泛用定制销售生产数据模型设计方案

## 设计目标

将“定制眼镜”的模型抽象成可适配多行业的定制销售生产履约模型，支持眼镜、家具、服装、设备、礼品等不同行业的定制化订单。

核心目标：

- 复用现有 `sale_order`、`sale_order_detail`、`product`、`supplier`、`purchase_order`、`repository_*`、`production_*`、`entrust` 等通用 ERP 能力。
- 将一条销售明细拆解成一个或多个“定制履约单元”。
- 每个履约单元可配置多个部件、多个工序、多个参数。
- 支持行业模板，不同行业配置不同部件、工序和参数。
- 支持状态汇总、异常预警、重做回退、操作日志。
- 支持必要的行业扩展表，避免所有字段都塞进 JSON。

## 核心抽象

```text
sale_order
  1 - N sale_order_detail                # 销售事实：卖了什么、价格、数量、第三方行项目
        1 - N custom_order_item          # 定制履约单元：一副眼镜/一件家具/一件衣服/一套设备
              1 - N custom_item_component # 部件：镜框/镜片/板材/五金/面料
              1 - N custom_item_process   # 工序：采购/委外/组装/质检/包装/发货
              1 - N custom_process_log    # 状态、异常、重做、回退日志

custom_flow_template
  1 - N custom_component_template
  1 - N custom_process_template
  1 - N custom_param_schema
```

## 设计原则

### 1. 销售和履约分离

`sale_order_detail` 保留为销售行项目，不按每个定制单元强行拆分。

例如 Shopify 一条行项目数量为 3：

```text
sale_order_detail: 1 条，num = 3
custom_order_item: 3 条，item_no = 1 / 2 / 3
```

这样销售金额、退款、改价、第三方同步仍基于销售明细；生产、采购、委外、组装、质检基于履约单元。

### 2. 通用流程配置化

不同行业通过模板定义：

- 订单履约单元是什么。
- 需要哪些部件。
- 需要哪些工序。
- 哪些参数必填。
- 哪些状态算完成、异常、超期。

### 3. JSON 只做快照和低频参数

`params_snapshot` 用于保存下单时的完整参数快照，便于追溯。

高频查询、报表、预警所需字段不要长期只放 JSON，应建立行业扩展表或结构化字段。

例如眼镜的 SPH、CYL、Axis、PD 可放行业扩展表；家具的长宽高、材质、颜色也可放行业扩展表。

### 4. 行业扩展不污染通用主表

通用履约表命名为 `custom_*`。行业专属能力通过扩展表补充：

```text
optical_prescription
optical_product_profile
furniture_item_profile
apparel_measurement
```

## 推荐新增表

### 1. custom_order_item：定制履约单元

一条记录表示一个需要独立履约的定制对象。

眼镜行业是一副眼镜；家具行业是一件家具；服装行业是一件衣服；设备行业是一套设备。

```sql
CREATE TABLE `hg_custom_order_item` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `company_id` char(18) NOT NULL,
  `industry_type` varchar(32) NOT NULL COMMENT '行业类型 optical/furniture/apparel/equipment',
  `sale_order_id` int(11) unsigned NOT NULL COMMENT '销售订单ID',
  `sale_order_detail_id` int(11) unsigned DEFAULT NULL COMMENT '销售订单明细ID',
  `customer_id` int(11) unsigned DEFAULT NULL COMMENT '客户ID',
  `template_id` int(11) unsigned DEFAULT NULL COMMENT '流程模板ID',
  `product_id` int(11) unsigned DEFAULT NULL COMMENT '主产品ID',
  `third_line_item_id` varchar(128) DEFAULT NULL COMMENT '第三方行项目ID',
  `item_no` smallint(6) unsigned NOT NULL DEFAULT '1' COMMENT '同销售明细下第几个履约单元',
  `quantity` int(11) unsigned NOT NULL DEFAULT '1' COMMENT '履约单元数量，通常为1',
  `params_snapshot` json DEFAULT NULL COMMENT '下单时完整参数快照',
  `status` tinyint(2) NOT NULL DEFAULT '0' COMMENT '履约单元状态',
  `abnormal_status` tinyint(2) NOT NULL DEFAULT '0' COMMENT '异常状态',
  `abnormal_reason` varchar(500) DEFAULT NULL COMMENT '异常原因',
  `is_remake` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '是否重做',
  `remake_from_id` int(11) unsigned DEFAULT NULL COMMENT '来源履约单元ID',
  `owner_admin_id` int(11) unsigned DEFAULT NULL COMMENT '负责人',
  `create_admin_id` int(11) unsigned DEFAULT NULL,
  `edit_admin_id` int(11) unsigned DEFAULT NULL,
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_company_order` (`company_id`, `sale_order_id`),
  KEY `idx_detail` (`sale_order_detail_id`),
  KEY `idx_customer` (`customer_id`),
  KEY `idx_template` (`template_id`),
  KEY `idx_status` (`industry_type`, `status`, `abnormal_status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='定制订单履约单元';
```

建议通用状态：

```text
0  待解析/待处理
10 准备中
20 生产/加工中
30 质检中
40 已入库/已完成
50 已发货/已交付
80 异常
90 已取消
```

行业可通过配置映射成更具体文案，例如眼镜行业的“备货中、组装中、质检中、已生产入库、已发货”。

### 2. custom_item_component：履约部件

一条记录表示履约单元中的一个部件。

眼镜：镜框、左镜片、右镜片。  
家具：板材、五金、软包、包装。  
服装：面料、里布、纽扣、拉链。

```sql
CREATE TABLE `hg_custom_item_component` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `company_id` char(18) NOT NULL,
  `custom_order_item_id` int(11) unsigned NOT NULL COMMENT '履约单元ID',
  `component_template_id` int(11) unsigned DEFAULT NULL COMMENT '部件模板ID',
  `component_type` varchar(64) NOT NULL COMMENT '部件类型 frame/lens_left/wood/cloth',
  `component_title` varchar(128) DEFAULT NULL COMMENT '部件名称',
  `product_id` int(11) unsigned DEFAULT NULL COMMENT '产品ID',
  `supplier_id` int(11) unsigned DEFAULT NULL COMMENT '供应商ID',
  `source_type` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '1库存 2采购 3委外 4自产',
  `status` tinyint(2) NOT NULL DEFAULT '0' COMMENT '部件状态',
  `expected_finish_time` datetime DEFAULT NULL COMMENT '预计完成/入库时间',
  `actual_finish_time` datetime DEFAULT NULL COMMENT '实际完成/入库时间',
  `purchase_order_id` int(11) unsigned DEFAULT NULL COMMENT '采购单ID',
  `purchase_order_detail_id` int(11) unsigned DEFAULT NULL COMMENT '采购明细ID',
  `entrust_id` int(11) unsigned DEFAULT NULL COMMENT '委外单ID',
  `production_receipt_id` int(11) unsigned DEFAULT NULL COMMENT '生产工单ID',
  `repository_freeze_log_id` int(11) unsigned DEFAULT NULL COMMENT '库存冻结记录ID',
  `repository_receipt_detail_id` int(11) unsigned DEFAULT NULL COMMENT '出入库明细ID',
  `tracking_num` varchar(128) DEFAULT NULL COMMENT '物流/供应商快递单号',
  `params_snapshot` json DEFAULT NULL COMMENT '部件参数快照',
  `is_abnormal` tinyint(2) unsigned NOT NULL DEFAULT '0',
  `abnormal_reason` varchar(500) DEFAULT NULL,
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_item` (`custom_order_item_id`),
  KEY `idx_component` (`company_id`, `component_type`),
  KEY `idx_product` (`product_id`),
  KEY `idx_supplier` (`supplier_id`),
  KEY `idx_status` (`status`, `is_abnormal`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='定制履约部件';
```

建议通用部件状态：

```text
0  待处理
10 已锁定库存/已备齐
20 采购中
30 委外中
40 生产中
50 已完成/已入库
80 异常/无法处理
90 已取消
```

### 3. custom_item_process：履约工序

一条记录表示履约单元中的一个工序。工序可以关联现有生产、委外、仓库、快递等单据。

```sql
CREATE TABLE `hg_custom_item_process` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `company_id` char(18) NOT NULL,
  `custom_order_item_id` int(11) unsigned NOT NULL COMMENT '履约单元ID',
  `process_template_id` int(11) unsigned DEFAULT NULL COMMENT '工序模板ID',
  `process_type` varchar(64) NOT NULL COMMENT 'purchase/outsource/assemble/qc/stock_in/ship',
  `process_code` varchar(64) DEFAULT NULL,
  `process_title` varchar(128) DEFAULT NULL COMMENT '工序名称',
  `rank` smallint(6) unsigned NOT NULL DEFAULT '1',
  `status` tinyint(2) NOT NULL DEFAULT '0' COMMENT '工序状态',
  `main_admin_id` int(11) unsigned DEFAULT NULL COMMENT '执行人',
  `supplier_id` int(11) unsigned DEFAULT NULL COMMENT '供应商',
  `plan_start_time` datetime DEFAULT NULL,
  `plan_finish_time` datetime DEFAULT NULL,
  `actual_start_time` datetime DEFAULT NULL,
  `actual_finish_time` datetime DEFAULT NULL,
  `ref_module` varchar(64) DEFAULT NULL COMMENT '关联模块 entrust/production_receipt/repository_receipt',
  `ref_id` int(11) unsigned DEFAULT NULL COMMENT '关联单据ID',
  `params_snapshot` json DEFAULT NULL COMMENT '工序参数快照',
  `is_abnormal` tinyint(2) unsigned NOT NULL DEFAULT '0',
  `abnormal_reason` varchar(500) DEFAULT NULL,
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_item_rank` (`custom_order_item_id`, `rank`),
  KEY `idx_process` (`company_id`, `process_type`),
  KEY `idx_ref` (`ref_module`, `ref_id`),
  KEY `idx_status` (`status`, `is_abnormal`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='定制履约工序';
```

建议通用工序状态：

```text
0  待开始
10 进行中
20 已完成
80 异常
90 已跳过/取消
```

### 4. custom_process_log：履约日志

记录状态变化、异常、重做、回退、人工调整。

```sql
CREATE TABLE `hg_custom_process_log` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `company_id` char(18) NOT NULL,
  `custom_order_item_id` int(11) unsigned NOT NULL,
  `target_type` varchar(32) NOT NULL COMMENT 'item/component/process',
  `target_id` int(11) unsigned DEFAULT NULL,
  `event` varchar(64) NOT NULL COMMENT 'status_change/remake/rollback/abnormal/manual_adjust',
  `from_status` tinyint(2) DEFAULT NULL,
  `to_status` tinyint(2) DEFAULT NULL,
  `remark` varchar(1000) DEFAULT NULL,
  `operator_admin_id` int(11) unsigned DEFAULT NULL,
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_item` (`custom_order_item_id`),
  KEY `idx_event` (`company_id`, `event`),
  KEY `idx_target` (`target_type`, `target_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='定制履约流程日志';
```

## 流程配置表

### 5. custom_flow_template：行业/产品流程模板

```sql
CREATE TABLE `hg_custom_flow_template` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `company_id` char(18) NOT NULL,
  `industry_type` varchar(32) NOT NULL COMMENT '行业类型',
  `title` varchar(128) NOT NULL COMMENT '模板名称',
  `product_id` int(11) unsigned DEFAULT NULL COMMENT '绑定产品，可为空',
  `status` tinyint(2) unsigned NOT NULL DEFAULT '1',
  `admin_remark` varchar(500) DEFAULT NULL,
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_company_industry` (`company_id`, `industry_type`),
  KEY `idx_product` (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='定制流程模板';
```

### 6. custom_component_template：部件模板

```sql
CREATE TABLE `hg_custom_component_template` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `company_id` char(18) NOT NULL,
  `template_id` int(11) unsigned NOT NULL COMMENT '流程模板ID',
  `component_type` varchar(64) NOT NULL COMMENT '部件类型',
  `component_title` varchar(128) NOT NULL COMMENT '部件名称',
  `default_product_id` int(11) unsigned DEFAULT NULL COMMENT '默认产品',
  `source_type` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '默认来源',
  `is_required` tinyint(2) unsigned NOT NULL DEFAULT '1',
  `rank` smallint(6) unsigned NOT NULL DEFAULT '1',
  `status` tinyint(2) unsigned NOT NULL DEFAULT '1',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_template` (`template_id`),
  KEY `idx_component` (`company_id`, `component_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='定制部件模板';
```

### 7. custom_process_template：工序模板

```sql
CREATE TABLE `hg_custom_process_template` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `company_id` char(18) NOT NULL,
  `template_id` int(11) unsigned NOT NULL COMMENT '流程模板ID',
  `process_type` varchar(64) NOT NULL COMMENT '工序类型',
  `process_title` varchar(128) NOT NULL COMMENT '工序名称',
  `rank` smallint(6) unsigned NOT NULL DEFAULT '1',
  `lead_hours` int(11) unsigned DEFAULT NULL COMMENT '默认工期小时',
  `auto_start` tinyint(2) unsigned NOT NULL DEFAULT '0' COMMENT '是否自动开始',
  `auto_finish_rule` varchar(255) DEFAULT NULL COMMENT '自动完成规则',
  `is_required` tinyint(2) unsigned NOT NULL DEFAULT '1',
  `status` tinyint(2) unsigned NOT NULL DEFAULT '1',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_template_rank` (`template_id`, `rank`),
  KEY `idx_process` (`company_id`, `process_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='定制工序模板';
```

### 8. custom_param_schema：参数定义

定义不同行业、不同模板、不同部件/工序需要填写和解析的参数。

```sql
CREATE TABLE `hg_custom_param_schema` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `company_id` char(18) NOT NULL,
  `template_id` int(11) unsigned DEFAULT NULL COMMENT '流程模板ID',
  `scope` varchar(32) NOT NULL COMMENT 'item/component/process',
  `scope_type` varchar(64) DEFAULT NULL COMMENT '如 lens_left / assemble',
  `param_key` varchar(64) NOT NULL COMMENT '参数键',
  `param_title` varchar(128) NOT NULL COMMENT '参数名称',
  `param_type` varchar(32) NOT NULL DEFAULT 'string' COMMENT 'string/number/date/select/json',
  `options` json DEFAULT NULL COMMENT '选项',
  `is_required` tinyint(2) unsigned NOT NULL DEFAULT '0',
  `is_searchable` tinyint(2) unsigned NOT NULL DEFAULT '0',
  `rank` smallint(6) unsigned NOT NULL DEFAULT '1',
  `status` tinyint(2) unsigned NOT NULL DEFAULT '1',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_template_scope` (`template_id`, `scope`, `scope_type`),
  KEY `idx_param` (`company_id`, `param_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='定制参数定义';
```

## 行业扩展表示例

### 眼镜行业扩展

眼镜行业中，验光参数、镜片参数、供应商加工能力需要高频查询和预警，不建议只放 JSON。

可保留眼镜扩展表：

```text
hg_optical_prescription          # 客户验光档案
hg_optical_product_profile       # 产品眼镜属性
hg_optical_supplier_capability   # 镜片/镜框供应商能力
```

这些表与通用模型关系：

```text
custom_order_item.params_snapshot       # 保存下单快照
custom_item_component.params_snapshot   # 保存左/右镜片或镜框参数快照
optical_prescription                    # 客户历史处方
optical_supplier_capability             # 用于匹配供应商和计算交期
```

### 家具行业扩展示例

```text
hg_furniture_item_profile
- custom_order_item_id
- length
- width
- height
- material
- color
- drawing_file
```

### 服装行业扩展示例

```text
hg_apparel_measurement
- custom_order_item_id
- size
- height
- weight
- chest
- waist
- hip
- fabric
- color
```

## 不同行业模板示例

### 眼镜模板

```text
custom_order_item:
  一副眼镜

components:
  frame       镜框
  lens_left   左镜片
  lens_right  右镜片

processes:
  frame_prepare     镜框备货/采购
  lens_outsource    镜片委外
  lens_stock_in     镜片入库
  assemble          组装
  qc                质检
  stock_in          成品入库
  ship              发货

params:
  sphere, cylinder, axis, pd, add_power, lens_index, coating
```

### 家具模板

```text
custom_order_item:
  一件家具

components:
  board       板材
  hardware    五金
  soft_pack   软包
  package     包装

processes:
  material_prepare  备料
  cutting           开料
  edge_band         封边
  drilling          打孔
  assemble          组装
  qc                质检
  package           包装
  ship              发货

params:
  length, width, height, material, color, drawing_file
```

### 服装模板

```text
custom_order_item:
  一件服装

components:
  fabric       面料
  lining       里布
  button       纽扣
  zipper       拉链

processes:
  material_prepare  备料
  cutting           裁剪
  sewing            缝制
  ironing           整烫
  qc                质检
  package           包装
  ship              发货

params:
  size, height, chest, waist, hip, fabric, color
```

## 与现有系统的衔接

### 与 sale_order / sale_order_detail

- `sale_order` 是销售订单主表，不直接表达定制生产细节。
- `sale_order_detail` 是销售行项目，保留第三方 line item、SKU、数量、价格。
- `custom_order_item.sale_order_detail_id` 关联销售明细。
- `sale_order_detail.extra` 可保存第三方原始参数。
- `custom_order_item.params_snapshot` 保存解析后的履约参数快照。

### 与 product

- `product` 继续表示标准产品。
- 部件可通过 `custom_item_component.product_id` 关联产品。
- 行业属性不建议都塞入 `product`，可用行业扩展表或 `custom_param_schema` 补充。

### 与 supplier / supplier_product

- 供应商基础信息继续用 `supplier`。
- 标准产品报价继续用 `supplier_product`。
- 定制能力、加工交期、特殊规则建议使用行业能力表或通用能力表扩展。

### 与 purchase_order

- 部件库存不足时，复用 `purchase_order` 和 `purchase_order_detail`。
- 回写 `custom_item_component.purchase_order_detail_id`。
- 采购状态变化同步到对应部件状态。

### 与 repository

- 库存锁定复用 `repository_freeze_log`。
- 出入库复用 `repository_receipt` / `repository_receipt_detail`。
- 回写到 `custom_item_component.repository_freeze_log_id` 或 `repository_receipt_detail_id`。

### 与 entrust

- 委外加工复用 `entrust`。
- 回写 `custom_item_component.entrust_id` 或 `custom_item_process.ref_module = entrust`。

### 与 production

- 标准生产流程复用 `production_plan`、`production_receipt`、`production_receipt_process`、`qc_receipt`。
- 轻量流程可先用 `custom_item_process` 管理，后续再生成正式生产工单。

## 订单面板查询建议

订单列表展示不应直接拼所有业务表，而应提供一个聚合接口：

```text
sale_order
  custom_order_items
    components
    processes
    logs
```

示例：

```json
{
  "sale_order_id": 1001,
  "code": "SHOP_1001",
  "custom_summary": {
    "industry_type": "optical",
    "total": 2,
    "abnormal_total": 1,
    "status": "准备中"
  },
  "items": [
    {
      "item_no": 1,
      "status": "组装中",
      "components": [
        {
          "component_type": "frame",
          "component_title": "镜框",
          "status": "已备齐"
        },
        {
          "component_type": "lens_left",
          "component_title": "左镜片",
          "status": "已入库"
        },
        {
          "component_type": "lens_right",
          "component_title": "右镜片",
          "status": "已入库"
        }
      ],
      "processes": [
        {
          "process_type": "assemble",
          "process_title": "组装",
          "status": "进行中"
        }
      ]
    }
  ]
}
```

## 推荐实施顺序

1. 先建核心履约表：`custom_order_item`、`custom_item_component`、`custom_item_process`、`custom_process_log`。
2. 建模板表：`custom_flow_template`、`custom_component_template`、`custom_process_template`、`custom_param_schema`。
3. 先为眼镜行业配置模板，验证“一条销售明细生成多副眼镜”的流程。
4. 打通 Shopify 参数解析，将 `sale_order_detail.extra` 转为 `custom_order_item` 和 `custom_item_component`。
5. 打通库存锁定、采购、委外、入库、组装、质检、发货状态回写。
6. 再逐步建设行业扩展表，例如眼镜处方、眼镜供应商能力。
7. 最后增加跨行业报表和模板管理页面。

## 结论

如果只服务眼镜业务，`optical_*` 模型更直接；如果要兼容多行业定制销售生产流程，推荐以 `custom_*` 作为核心模型。

最终推荐采用：

```text
通用核心：custom_order_item / custom_item_component / custom_item_process / custom_process_log
配置模板：custom_flow_template / custom_component_template / custom_process_template / custom_param_schema
行业扩展：optical_prescription / furniture_item_profile / apparel_measurement 等
```

这样能保持系统泛用性，同时避免把所有行业差异都硬塞进销售订单明细或 JSON 字段。
