# 生产数据看板与大屏实施方案

## 需求来源

来自 `doc/jykj/软件开发需求功能点.md` 第 27-28 行：

| 模块 | 功能 | 需求 |
| --- | --- | --- |
| 生产管理 | 数据看板 | 支持查看整体销售订单的生产进度 |
| 生产管理 | 数据大屏 | 支持在大屏上展示整体进度以及每个生产车间/生产小组的生产数据 |

这两项需求属于定制眼镜生产履约的可视化层，不应重新定义生产流程。统计口径应复用现有定制履约数据。

## 现有基础

### 已有数据模型

| 模型 | 表 | 用途 |
| --- | --- | --- |
| `SaleOrder` | `sale_order` | 销售订单主单 |
| `SaleOrderDetail` | `sale_order_detail` | 销售订单明细 |
| `CustomOrderItem` | `custom_order_item` | 定制成品履约单元，一个销售订单明细可对应一个或多个定制履约对象 |
| `CustomItemComponent` | `custom_item_component` | 镜框、左镜片、右镜片等部件状态 |
| `CustomItemProcess` | `custom_item_process` | 镜片入库、组装、质检、成品入库等工序 |
| `CustomProcessLog` | `custom_process_log` | 履约状态变更、异常、返工、人工操作日志 |
| `AdminOrganization` | `admin_organization` | 员工所属部门/小组关系 |
| `Organization` | `organization` | 部门、车间、生产小组组织树 |

### 已有接口基础

| 控制器 | 说明 |
| --- | --- |
| `App\Controller\Admin\DashboardController` | 后台数据看板已有通用统计接口 |
| `App\Controller\Admin\DashboardController` | 后台数据看板和大屏接口统一定义在 Admin 接口组 |
| `App\Controller\Admin\CustomItemProcessController` | 已有生产工单列表、详情、分派、开始、完成、质检、异常等入口 |

### 已有状态基础

`custom_order_item.status` 建议作为履约单元主状态：

| 状态 | 含义 |
| --- | --- |
| `0` | 待准备 |
| `10` | 备料中 |
| `20` | 组装中 |
| `30` | 质检中 |
| `40` | 已生产入库 |
| `80` | 异常 |

`custom_item_process.status` 作为工序状态：

| 状态 | 含义 |
| --- | --- |
| `0` | 待开始 |
| `10` | 进行中 |
| `20` | 已完成 |
| `80` | 异常 |
| `90` | 取消 |

`custom_item_component.status` 作为部件齐套状态：

| 状态 | 含义 |
| --- | --- |
| `0` | 待处理 |
| `10` | 库存已锁定/已就绪 |
| `20` | 采购中 |
| `30` | 委外中 |
| `50` | 已完成/已入库 |
| `80` | 异常 |
| `90` | 取消 |

## 设计原则

1. 生产看板不创建新的业务状态，全部从定制履约表派生。
2. 统计最小粒度使用 `custom_order_item`，销售订单维度通过 `sale_order_id` 聚合。
3. 工序维度使用 `custom_item_process`，部件齐套维度使用 `custom_item_component`。
4. 后台看板展示可追溯明细，大屏展示聚合数据，不展示客户隐私、原始销售单号、SKU 价格等敏感字段。
5. 第一阶段实时查询即可；订单量较大后再引入缓存或快照表。
6. 生产车间/生产小组优先复用 `organization`，通过工序负责人 `main_admin_id` 反查其组织。

## 统计口径

### 生产进度对象

后台看板分两层：

| 层级 | 统计对象 | 说明 |
| --- | --- | --- |
| 销售订单层 | `sale_order` | 展示订单整体生产进度 |
| 履约单元层 | `custom_order_item` | 真实计算单元，适合定位明细 |

销售订单的生产状态按其下所有 `custom_order_item` 聚合：

| 场景 | 销售订单生产状态 |
| --- | --- |
| 任一履约单元异常 | 异常 |
| 全部履约单元已生产入库 | 已生产入库 |
| 任一履约单元质检中 | 质检中 |
| 任一履约单元组装中 | 组装中 |
| 任一履约单元备料中 | 备料中 |
| 尚未生成履约单元 | 未进入定制履约 |

### 生产阶段

看板和大屏统一使用以下阶段：

| 阶段编码 | 展示名称 | 数据条件 |
| --- | --- | --- |
| `pending` | 待准备 | `custom_order_item.status = 0` |
| `preparing` | 备料中 | `custom_order_item.status = 10` |
| `assemble` | 组装中 | `custom_order_item.status = 20` |
| `qc` | 质检中 | `custom_order_item.status = 30` |
| `stocked` | 已生产入库 | `custom_order_item.status = 40` |
| `abnormal` | 异常 | `custom_order_item.status = 80` 或 `abnormal_status = 1` |

### 部件进度

部件看板按部件类型统计：

| 部件 | 条件 |
| --- | --- |
| 镜框 | `component_type = frame` |
| 左镜片 | `component_type = lens_left` |
| 右镜片 | `component_type = lens_right` |
| 镜片对 | `component_type = lens_pair` |

部件齐套率：

```text
ready_component_count / required_component_count
```

齐套定义：

```text
component.status >= 50
或
component_type = frame 且 source_type = 1 且 repository_freeze_log_id 不为空且 status >= 10
```

### 超期口径

| 类型 | 判断字段 | 说明 |
| --- | --- | --- |
| 委外超期 | `custom_item_component.expected_finish_time` / `supplier_eta_time` | 镜片未完成且预计时间早于当前时间 |
| 工序超期 | `custom_item_process.plan_finish_time` | 工序未完成且计划完成时间早于当前时间 |
| 订单超期 | `sale_order.delivery_date` | 定制履约未生产入库且交付日期早于当前日期 |

### 工时口径

| 指标 | 计算方式 |
| --- | --- |
| 组装平均耗时 | `assemble.actual_finish_time - assemble.actual_start_time` |
| 质检平均耗时 | `qc.actual_finish_time - qc.actual_start_time` |
| 生产总耗时 | `stock_in.actual_finish_time - custom_order_item.create_time` |
| 今日完成数 | `stock_in.actual_finish_time` 在今天且 `stock_in.status = 20` |

## 后台数据看板

### 页面目标

后台数据看板面向生产主管、客服、运营和管理人员，核心目标是：

1. 快速看到整体订单生产进度。
2. 快速定位超期、异常、卡住的订单。
3. 能下钻到销售订单、履约单元、部件和工序。
4. 能按订单负责人、工序负责人、生产小组、订单渠道、时间范围筛选。

### 看板模块

| 区域 | 内容 |
| --- | --- |
| 顶部指标 | 定制订单数、履约单元数、进行中、已生产入库、异常、超期 |
| 阶段分布 | 待准备、备料中、组装中、质检中、已生产入库、异常 |
| 部件进度 | 镜框就绪、镜片委外中、镜片已入库、部件异常 |
| 工序进度 | 待组装、组装中、待质检、质检中、待成品入库 |
| 预警列表 | 委外超期、组装超期、质检超期、订单交期超期 |
| 订单明细 | 销售订单、客户、产品、当前阶段、负责人、预计交付、异常原因 |

### 推荐接口

新增到 `App\Controller\Admin\DashboardController`：

```text
GET /admin/dashboard/customProductionOverview
GET /admin/dashboard/customProductionStage
GET /admin/dashboard/customProductionComponent
GET /admin/dashboard/customProductionProcess
GET /admin/dashboard/customProductionWarning
GET /admin/dashboard/customProductionOrderList
GET /admin/dashboard/customProductionTrend
GET /admin/dashboard/customProductionGroup
```

通用查询参数：

| 参数 | 说明 |
| --- | --- |
| `start_date` | 订单开始日期 |
| `end_date` | 订单结束日期 |
| `time` | 快捷时间，如 `today`、`week`、`month` |
| `owner_admin_id` | 销售订单负责人 |
| `main_admin_id` | 工序负责人 |
| `organization_id` | 生产车间/小组 |
| `status` | 履约单元状态 |
| `process_type` | 工序类型 |
| `only_abnormal` | 只看异常 |
| `only_overdue` | 只看超期 |
| `channel_id` | 订单渠道 |

### 接口返回建议

`customProductionOverview`：

```json
{
  "order_count": 120,
  "item_count": 136,
  "running_count": 68,
  "stocked_count": 42,
  "abnormal_count": 5,
  "overdue_count": 8,
  "completion_rate": 30.88,
  "abnormal_rate": 3.67
}
```

`customProductionStage`：

```json
[
  {"stage": "preparing", "title": "备料中", "count": 30},
  {"stage": "assemble", "title": "组装中", "count": 18},
  {"stage": "qc", "title": "质检中", "count": 12},
  {"stage": "stocked", "title": "已生产入库", "count": 42},
  {"stage": "abnormal", "title": "异常", "count": 5}
]
```

`customProductionOrderList`：

```json
{
  "data": [
    {
      "sale_order_id": 1001,
      "custom_order_item_id": 501,
      "current_stage": "qc",
      "current_stage_title": "质检中",
      "product_id": 3001,
      "owner_admin_id": 8,
      "main_admin_id": 12,
      "delivery_date": "2026-07-30",
      "is_overdue": 0,
      "abnormal_reason": null
    }
  ]
}
```

## 数据大屏

### 大屏目标

大屏面向生产车间现场展示，重点是“整体进度”和“各车间/小组产能”。它不用于操作，只用于展示。

### 展示内容

| 区域 | 内容 |
| --- | --- |
| 总览数字 | 今日新增、今日完成、进行中、异常、超期 |
| 进度漏斗 | 备料中、组装中、质检中、成品入库 |
| 车间/小组排名 | 进行中数量、今日完成、异常、超期 |
| 工序实时列表 | 最近进入组装、质检、入库的工单 |
| 异常滚动 | 异常订单、异常工序、异常原因 |
| 趋势图 | 最近 7 天完成量、异常量、平均生产耗时 |

### 生产车间/小组归属

第一阶段建议复用组织架构：

```text
custom_item_process.main_admin_id
  -> admin_organization.admin_id
  -> admin_organization.organization_id
  -> organization.title
```

这能快速满足“每个生产车间/生产小组”的展示需求，不需要先新增车间表。

如果后续需要严格保留历史归属快照，建议在 `custom_item_process` 增加字段：

```sql
ALTER TABLE `hg_custom_item_process`
  ADD `work_group_id` varchar(32) DEFAULT NULL COMMENT '生产车间/小组组织ID' AFTER `main_admin_id`;
```

原因：员工调组后，历史工单如果只通过 `admin_organization` 反查，会跟随当前组织变化，历史统计会漂移。

### 推荐接口

新增到 `App\Controller\Admin\DashboardController`，或单独新增 `App\Controller\Admin\ProductionScreenController`：

```text
GET /admin/dashboard/customProductionScreen
GET /admin/dashboard/customProductionScreenTrend
GET /admin/dashboard/customProductionScreenGroup
GET /admin/dashboard/customProductionScreenWarning
```

大屏接口参数：

| 参数 | 说明 |
| --- | --- |
| `organization_id` | 可选，限定某个车间/组织 |
| `refresh_seconds` | 前端刷新间隔，默认 30 秒 |

安全要求：

1. 大屏接口必须定义在 Admin 接口组，走后台登录、菜单权限和公司权限校验。
2. 返回数据不包含客户手机号、客户详细地址、原始 Shopify 订单号、SKU 价格、成本价。
3. 大屏只返回聚合和脱敏列表。
4. 大屏页面可以做全屏展示，但数据接口不绕过后台权限体系。

## 服务层设计

建议新增服务：

```text
App\Service\Common\CustomProductionDashboardService
```

职责：

| 方法 | 说明 |
| --- | --- |
| `buildFilter(array $query, string $companyId): array` | 统一处理时间、负责人、组织等过滤条件 |
| `overview(array $filter): array` | 总览指标 |
| `stageDistribution(array $filter): array` | 阶段分布 |
| `componentProgress(array $filter): array` | 部件进度 |
| `processProgress(array $filter): array` | 工序进度 |
| `warningList(array $filter): array` | 超期和异常列表 |
| `orderList(array $filter): array` | 可下钻订单列表 |
| `trend(array $filter): array` | 趋势数据 |
| `groupStats(array $filter): array` | 车间/小组统计 |
| `screen(array $filter): array` | 大屏聚合数据 |

后台控制器和大屏控制器都调用同一个服务，避免后台看板和大屏口径不一致。

## 查询实现建议

### 基础查询

以 `custom_order_item` 为主表：

```text
custom_order_item
  left join sale_order on sale_order.id = custom_order_item.sale_order_id
  left join custom_item_process on custom_item_process.custom_order_item_id = custom_order_item.id
  left join custom_item_component on custom_item_component.custom_order_item_id = custom_order_item.id
```

常用过滤：

```text
custom_order_item.company_id = 当前公司
sale_order.order_time between start_time and end_time
custom_order_item.deleted_at is null
sale_order.deleted_at is null
```

### 组织过滤

当传入 `organization_id`：

```text
admin_organization.organization_id like {organization_id}%
custom_item_process.main_admin_id = admin_organization.admin_id
```

如果已落地 `work_group_id`：

```text
custom_item_process.work_group_id like {organization_id}%
```

### 今日完成

```text
process_type = stock_in
status = 20
actual_finish_time between 今日开始 and 今日结束
```

### 超期工序

```text
custom_item_process.status < 20
custom_item_process.plan_finish_time < now()
```

### 异常工单

```text
custom_order_item.status = 80
or custom_order_item.abnormal_status = 1
or custom_item_process.status = 80
or custom_item_component.status = 80
```

## 性能方案

### 第一阶段：实时查询

适用条件：

1. 单公司定制履约单元数量在几十万以内。
2. 大屏刷新间隔 30 秒以上。
3. 查询条件以近 7 天、近 30 天为主。

需要补充索引：

```sql
ALTER TABLE `hg_custom_order_item`
  ADD KEY `idx_company_status_order` (`company_id`, `status`, `sale_order_id`),
  ADD KEY `idx_company_owner_status` (`company_id`, `owner_admin_id`, `status`);

ALTER TABLE `hg_custom_item_process`
  ADD KEY `idx_company_type_status_time` (`company_id`, `process_type`, `status`, `actual_finish_time`),
  ADD KEY `idx_company_main_status` (`company_id`, `main_admin_id`, `status`),
  ADD KEY `idx_plan_finish` (`company_id`, `status`, `plan_finish_time`);

ALTER TABLE `hg_custom_item_component`
  ADD KEY `idx_company_type_status` (`company_id`, `component_type`, `status`),
  ADD KEY `idx_supplier_eta` (`company_id`, `supplier_id`, `supplier_eta_time`, `status`);
```

### 第二阶段：缓存

大屏接口增加缓存：

| 数据 | 缓存时间 |
| --- | --- |
| 总览 | 30 秒 |
| 阶段分布 | 30 秒 |
| 小组排名 | 60 秒 |
| 趋势 | 5 分钟 |

缓存 key：

```text
custom_production_dashboard:{company_id}:{hash(filter)}
custom_production_screen:{company_id}:{organization_id}
```

### 第三阶段：统计快照

当实时查询慢时，再新增快照表：

```sql
CREATE TABLE `hg_custom_production_stat_snapshot` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `company_id` varchar(32) NOT NULL COMMENT '公司ID',
  `stat_date` date NOT NULL COMMENT '统计日期',
  `scope_type` varchar(32) NOT NULL DEFAULT 'company' COMMENT 'company/organization/admin',
  `scope_id` varchar(32) DEFAULT NULL COMMENT '范围ID',
  `metrics` json DEFAULT NULL COMMENT '指标快照',
  `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `uni_scope_date` (`company_id`, `stat_date`, `scope_type`, `scope_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='定制生产统计快照';
```

定时任务每 5 分钟刷新当天数据，每天凌晨固化前一天数据。

## 权限与菜单

### 后台看板

建议新增菜单：

```text
生产管理 / 数据看板
```

权限点：

| 权限 | 接口 |
| --- | --- |
| 查看总览 | `/admin/dashboard/customProductionOverview` |
| 查看明细 | `/admin/dashboard/customProductionOrderList` |
| 查看异常 | `/admin/dashboard/customProductionWarning` |
| 查看小组数据 | `/admin/dashboard/customProductionGroup` |

### 大屏

建议新增菜单：

```text
生产管理 / 数据大屏
```

后台菜单打开大屏页面，接口请求继续携带后台登录态，由 Admin 中间件完成身份、公司和菜单权限校验。

## 前端页面建议

### 数据看板

后台页面应偏操作型：

1. 顶部指标卡。
2. 阶段分布图。
3. 部件齐套进度图。
4. 超期/异常列表。
5. 订单明细表，可跳转到销售订单、履约单元、工单详情。
6. 筛选区：日期、负责人、工序负责人、组织、小组、状态、是否异常、是否超期。

### 数据大屏

大屏页面应偏展示型：

1. 全屏自适应。
2. 30 秒自动刷新。
3. 使用聚合指标，不展示敏感字段。
4. 支持按 `organization_id` 展示某个车间大屏。
5. 网络异常时保留上一次数据并提示最后刷新时间。

## 实施步骤

## 当前落地结果

第一版后端已按实时查询方案落地：

| 类型 | 文件/接口 |
| --- | --- |
| 统计服务 | `App\Service\Common\CustomProductionDashboardService` |
| 后台控制器 | `App\Controller\Admin\DashboardController` |
| 总览 | `GET /admin/dashboard/customProductionOverview` |
| 阶段分布 | `GET /admin/dashboard/customProductionStage` |
| 部件进度 | `GET /admin/dashboard/customProductionComponent` |
| 工序进度 | `GET /admin/dashboard/customProductionProcess` |
| 预警列表 | `GET /admin/dashboard/customProductionWarning` |
| 订单明细 | `GET /admin/dashboard/customProductionOrderList` |
| 趋势 | `GET /admin/dashboard/customProductionTrend` |
| 车间/小组统计 | `GET /admin/dashboard/customProductionGroup` |
| 大屏聚合 | `GET /admin/dashboard/customProductionScreen` |
| 大屏趋势 | `GET /admin/dashboard/customProductionScreenTrend` |
| 大屏小组 | `GET /admin/dashboard/customProductionScreenGroup` |
| 大屏预警 | `GET /admin/dashboard/customProductionScreenWarning` |

同时已在 `sql/2026-07.sql` 追加第一阶段实时查询所需索引。

### 第一阶段：后台看板

1. 新增 `CustomProductionDashboardService`。
2. 在 `Admin\DashboardController` 增加生产看板接口。
3. 补充路由 `/admin/dashboard/customProduction*`。
4. 使用实时查询实现总览、阶段分布、部件进度、工序进度、异常和订单列表。
5. 补充必要索引。

### 第二阶段：大屏

1. 在 `Admin\DashboardController` 或独立 Admin 大屏控制器增加大屏接口。
2. 接入后台登录、菜单权限和公司权限校验。
3. 复用 `CustomProductionDashboardService::screen()` 返回大屏聚合数据。
4. 前端实现全屏大屏页面和自动刷新。

### 第三阶段：小组/车间统计增强

1. 第一阶段使用 `main_admin_id -> admin_organization` 计算小组。
2. 如果历史归属需要稳定，新增 `custom_item_process.work_group_id`。
3. 工单分派时写入 `work_group_id`。
4. 大屏按 `work_group_id` 聚合。

### 第四阶段：缓存与快照

1. 为大屏接口增加 30-60 秒缓存。
2. 数据量增长后新增 `custom_production_stat_snapshot`。
3. 定时任务定期刷新统计快照。

## 验收标准

1. 后台能查看定制销售订单的整体生产进度。
2. 后台能按阶段、负责人、生产小组、日期筛选。
3. 后台能查看异常和超期订单，并能下钻到工单详情。
4. 大屏能展示整体进度、今日完成、异常、超期。
5. 大屏能展示每个生产车间/生产小组的生产数据。
6. 大屏接口不返回敏感客户信息和价格成本信息。
7. 后台看板和大屏使用同一服务层口径，同一时间范围下核心数字一致。
