Files
MES/yawei-mes/.tasks/2025-11-11_连续制造业改进方案.md

2173 lines
76 KiB
Markdown
Raw Normal View History

2026-04-02 10:38:23 +08:00
# 连续制造业改进方案
## 1. 核心思路5分钟读懂
### 1.1 改进目标
在现有MES系统基础上支持连续制造业化工、钢铁、水泥等的生产报工流程实现按时间段/班次报工,兼容现有离散制造业模式。
### 1.2 核心设计:物料级别的类型标识
**关键点**:在 `md_material`物料表中新增2个字段作为"开关"
```sql
manufacture_type -- 制造类型: DISCRETE(离散) / CONTINUOUS(连续)
report_mode -- 报工模式: PROCESS(按工序) / TIME(按时间) / SHIFT(按班次) / QUANTITY(按产量) / BATCH(按批次)
```
**报工模式说明**
| 报工模式 | 适用场景 | 报工方式 | 示例 |
|---------|---------|---------|------|
| `PROCESS` | 离散制造业 | 按工序完成报工 | 工序1完成 → 报工1次 |
| `TIME` | 连续制造业 | 按固定时间段报工 | 每小时报工一次 |
| `SHIFT` | 连续制造业 | 按班次报工 | 每班次结束报工一次 |
| `QUANTITY` | 连续制造业 | 按达到的产量报工 | 每生产100吨报工一次 |
| `BATCH` | 连续制造业 | 按生产批次报工 | 每完成一个批次报工一次 |
**数据流转逻辑**
```
物料表设置类型
销售订单选择物料(继承物料的类型)
生成工单时判断物料类型 → 决定工单生成方式
报工时判断工单类型 → 决定报工方式
```
---
## 2. 完整业务流程(核心)
### 2.1 流程对比
| 环节 | 离散制造业 | 连续-按时间 | 连续-按班次 | 连续-按产量 | 连续-按批次 |
|------|-----------|-----------|-----------|-----------|-----------|
| **1. 物料设置** | `DISCRETE`<br>`PROCESS` | `CONTINUOUS`<br>`TIME` | `CONTINUOUS`<br>`SHIFT` | `CONTINUOUS`<br>`QUANTITY` | `CONTINUOUS`<br>`BATCH` |
| **2. 工单生成** | 按工序生成多个<br>`XS001-P01`, `P02` | 生成一个工单<br>`XS001` | 生成一个工单<br>`XS001` | 生成一个工单<br>`XS001` | 生成一个工单<br>`XS001` |
| **3. 工单分录** | 创建(每个工序) | **不创建** | **不创建** | **不创建** | **不创建** |
| **4. 报工方式** | 按工序完成 | 按时间段 | 按班次 | 按产量 | 按批次 |
| **5. 报工示例** | 工序1完成 → 报工 | 08:00-09:00<br>09:00-10:00 | 早班<br>中班<br>晚班 | 累计100吨<br>累计200吨 | 批次1<br>批次2 |
| **6. 报工频率** | 每个工序1次 | 每小时 | 每班次 | 达到产量 | 批次完成 |
| **7. 时间段** | 无 | 固定1小时 | 固定8小时 | 不固定 | 不固定 |
| **8. 班次字段** | 不使用 | 可选填写 | **必填** | 可选填写 | 可选填写 |
| **9. 完成判断** | 所有工序完成 | 累计 ≥ 计划 | 累计 ≥ 计划 | 累计 ≥ 计划 | 累计 ≥ 计划 |
### 2.2 详细流程图
#### 流程A离散制造业现有逻辑保持不变
```
1. 物料管理
└─ 设置物料manufacture_type = 'DISCRETE', report_mode = 'PROCESS'
└─ 关联工序路线route_id
2. 销售订单
└─ 创建订单明细,选择物料
└─ 查询物料信息(包含 manufacture_type, report_mode
3. 生成工单(关键判断点)
└─ 读取物料的 manufacture_type
└─ if (manufacture_type == 'DISCRETE') {
// 按工序路线生成多个工单
for (每个工序) {
创建工单batch_number = "XS001-P01", "XS001-P02"...
创建工单分录:关联工序信息
}
}
4. 报工
└─ 选择工单分录work_order_entry_id
└─ 提交报工:报工数量、合格数、不合格数
└─ 更新工单分录状态
└─ 检查:所有工序完成 → 工单完成
```
#### 流程B连续制造业新增逻辑
**共同点**
- 只生成一个工单(不按工序)
- 不创建工单分录
- 可以多次报工
- 累计报工数量达到计划数量即完成
**差异点**:根据 `report_mode` 决定报工触发方式
---
##### B1按时间报工TIME
**适用场景**:化工、钢铁等连续生产,按固定时间段统计产量
```
1. 物料管理
└─ 设置物料manufacture_type = 'CONTINUOUS', report_mode = 'TIME'
2. 生成工单
└─ 创建工单batch_number = "XS001", report_mode = 'TIME'
3. 报工(按时间段)
└─ 08:00-09:00 → 报工100吨
└─ 09:00-10:00 → 报工120吨
└─ 10:00-11:00 → 报工110吨
└─ 累计330吨
4. 报工信息
- report_period_start: 2025-01-11 08:00:00
- report_period_end: 2025-01-11 09:00:00
- shift_name: 早班
- report_quantity: 100
- equipment_id: 10
- downtime_minutes: 10停机10分钟
```
---
##### B2按班次报工SHIFT
**适用场景**:三班倒生产,按班次统计产量
```
1. 物料管理
└─ 设置物料manufacture_type = 'CONTINUOUS', report_mode = 'SHIFT'
2. 生成工单
└─ 创建工单batch_number = "XS001", report_mode = 'SHIFT'
3. 报工(按班次)
└─ 早班08:00-16:00→ 报工800吨
└─ 中班16:00-00:00→ 报工750吨
└─ 晚班00:00-08:00→ 报工700吨
└─ 累计2250吨
4. 报工信息
- report_period_start: 2025-01-11 08:00:00班次开始
- report_period_end: 2025-01-11 16:00:00班次结束
- shift_name: 早班(必填)
- report_quantity: 800本班次产量
- equipment_id: 10
- downtime_minutes: 30本班次停机30分钟
```
---
##### B3按产量报工QUANTITY
**适用场景**:水泥、食品等,按达到的产量节点报工
```
1. 物料管理
└─ 设置物料manufacture_type = 'CONTINUOUS', report_mode = 'QUANTITY'
2. 生成工单
└─ 创建工单batch_number = "XS001", report_mode = 'QUANTITY'
3. 报工(按产量节点)
└─ 累计达到100吨 → 报工1100吨08:00-09:30
└─ 累计达到200吨 → 报工2100吨09:30-11:00
└─ 累计达到300吨 → 报工3100吨11:00-12:20
└─ 累计300吨
4. 报工信息
- report_period_start: 2025-01-11 08:00:00开始时间
- report_period_end: 2025-01-11 09:30:00达到100吨的时间
- report_quantity: 100本次报工数量
- shift_name: 早班(可选)
- remark: 累计产量达到100吨
```
---
##### B4按批次报工BATCH
**适用场景**:制药、精细化工等,按生产批次报工
```
1. 物料管理
└─ 设置物料manufacture_type = 'CONTINUOUS', report_mode = 'BATCH'
2. 生成工单
└─ 创建工单batch_number = "XS001", report_mode = 'BATCH'
3. 报工(按批次)
└─ 批次1完成 → 报工150吨批次号20250111-001
└─ 批次2完成 → 报工248吨批次号20250111-002
└─ 批次3完成 → 报工352吨批次号20250111-003
└─ 累计150吨
4. 报工信息
- report_period_start: 2025-01-11 08:00:00批次开始
- report_period_end: 2025-01-11 10:30:00批次完成
- report_sequence: 1第1个批次
- report_quantity: 50本批次产量
- shift_name: 早班(可选)
- remark: 批次号20250111-001
```
---
**四种模式对比**
| 对比项 | 按时间TIME | 按班次SHIFT | 按产量QUANTITY | 按批次BATCH |
|-------|--------------|---------------|-----------------|---------------|
| 报工触发 | 固定时间到达 | 班次结束 | 产量达到节点 | 批次完成 |
| 时间段 | 固定如1小时 | 固定如8小时 | 不固定 | 不固定 |
| 产量 | 不固定 | 不固定 | 固定如100吨 | 不固定 |
| 班次字段 | 可选 | **必填** | 可选 | 可选 |
| 适用场景 | 连续稳定生产 | 三班倒生产 | 产量为主要指标 | 批次管理严格 |
| 报工频率 | 高(每小时) | 中(每班次) | 中(达到产量) | 低(批次完成) |
| 典型行业 | 化工、钢铁 | 制造业三班倒 | 水泥、食品 | 制药、精细化工 |
```
### 2.3 关键判断点代码逻辑
#### 判断点1生成工单时ProWorkorderService.generateWorkOrder
```java
// 1. 查询销售订单明细
SalOrderEntry entry = salOrderEntryMapper.selectById(entryId);
// 2. 查询物料信息(关键:获取 manufacture_type
Material material = materialMapper.selectById(entry.getMaterialId());
// 3. 根据制造类型分流
if ("CONTINUOUS".equals(material.getManufactureType())) {
// 连续制造业:生成一个工单
return generateContinuousWorkOrder(entry, material);
} else {
// 离散制造业:按工序生成多个工单(现有逻辑)
return generateDiscreteWorkOrders(entry, material);
}
```
#### 判断点2报工时ProReportService.submitReport
```java
// 1. 根据报工单获取工单信息
Long workOrderId = report.getWorkOrderId() != null ?
report.getWorkOrderId() :
getWorkOrderIdByEntryId(report.getWorkOrderEntryId());
ProWorkorder workOrder = workorderMapper.selectById(workOrderId);
// 2. 根据工单的制造类型分流
if ("CONTINUOUS".equals(workOrder.getManufactureType())) {
// 连续制造业报工逻辑
return submitContinuousReport(report, workOrder);
} else {
// 离散制造业报工逻辑(现有逻辑)
return submitDiscreteReport(report, workOrder);
}
```
### 2.4 数据关联关系
#### 离散制造业(现有)
```
md_material (物料)
↓ material_id
sal_order_entry (订单明细)
↓ 生成工单
pro_workorder (工单1: P01)
↓ workorder_id
pro_workorder_entry (工单分录: 工序1)
↓ work_order_entry_id
pro_report (报工单)
pro_workorder (工单2: P02)
↓ workorder_id
pro_workorder_entry (工单分录: 工序2)
↓ work_order_entry_id
pro_report (报工单)
```
#### 连续制造业(新增)
```
md_material (物料)
↓ material_id
sal_order_entry (订单明细)
↓ 生成工单
pro_workorder (工单: XS001)
↓ work_order_id (直接关联,跳过工单分录)
pro_report (报工单1: 08:00-09:00)
pro_report (报工单2: 09:00-10:00)
pro_report (报工单3: 10:00-11:00)
...
```
---
## 3. 设计原则
1. **向后兼容**:不影响现有离散制造业流程
2. **最小改动**:复用现有表结构,新增字段标识
3. **简化逻辑**:连续制造业不使用工单分录,报工直接关联工单
4. **物料驱动**:所有判断逻辑都基于物料的 `manufacture_type` 字段
---
## 3. 数据库表分析与评估
### 3.1 现有相关表梳理
#### ✅ 已存在可复用的表
| 表名 | 用途 | 是否满足需求 |
|------|------|------------|
| `md_material` | 物料主数据 | ✅ 需新增字段 |
| `md_workshop` | 车间管理 | ✅ 可直接使用 |
| `md_station` | 工位管理 | ✅ 可直接使用 |
| `dm_equipment` | 设备管理 | ✅ 可直接使用(已有完整设备表) |
| `pro_workorder` | 生产工单 | ✅ 需新增字段 |
| `pro_workorder_entry` | 工单分录 | ✅ 连续制造业不使用 |
| `pro_report` | 报工单 | ✅ 需新增字段 |
| `pro_process` | 工序定义 | ✅ 连续制造业不使用 |
| `pro_route` | 工序路线 | ✅ 连续制造业不使用 |
| `sal_order` | 销售订单 | ✅ 可直接使用 |
| `sal_order_entry` | 订单明细 | ✅ 可直接使用 |
#### 📊 设备表详情dm_equipment
系统已有完整的设备管理表,包含以下字段:
- `id`, `number`, `name` - 基础信息
- `brand`, `specification`, `type` - 设备属性
- `station_id`, `station_name` - 关联工位
- `equipment_status` - 设备状态
- `iot_sn` - 物联网序列号
**结论**:✅ 设备表完全满足需求,无需新建,直接使用 `dm_equipment`
### 3.2 需要新建的表评估
#### ❌ 不需要新建的表
| 表名 | 原因 |
|------|------|
| ~~设备表~~ | 已有 `dm_equipment` |
| ~~班次表~~ | 第一版使用字符串字段 `shift_name` 即可 |
| ~~停机记录表~~ | 停机信息直接记录在 `pro_report` 中 |
| ~~产线表~~ | 可使用 `md_workshop`(车间)代替 |
#### ⚠️ 可选新建的表(后续版本)
**1. 班次管理表md_shift** - 优先级P2
```sql
CREATE TABLE `md_shift` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
`shift_code` varchar(32) NOT NULL COMMENT '班次编码',
`shift_name` varchar(64) NOT NULL COMMENT '班次名称',
`start_time` time NOT NULL COMMENT '开始时间',
`end_time` time NOT NULL COMMENT '结束时间',
`is_cross_day` tinyint(1) DEFAULT 0 COMMENT '是否跨天',
`sort` int DEFAULT 0 COMMENT '排序',
`status` varchar(2) DEFAULT '0' COMMENT '状态',
`remark` varchar(255) COMMENT '备注',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_shift_code` (`shift_code`)
) COMMENT='班次管理表';
```
**说明**:第一版可以不建,使用字符串字段即可。后续如需班次时间管理再建。
**2. 停机记录表pro_downtime_record** - 优先级P2
```sql
CREATE TABLE `pro_downtime_record` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'id',
`work_order_id` bigint NOT NULL COMMENT '工单ID',
`equipment_id` bigint COMMENT '设备ID',
`downtime_start` datetime NOT NULL COMMENT '停机开始时间',
`downtime_end` datetime COMMENT '停机结束时间',
`downtime_minutes` int COMMENT '停机时长(分钟)',
`downtime_type` varchar(32) COMMENT '停机类型',
`downtime_reason` varchar(500) COMMENT '停机原因',
`handler` varchar(64) COMMENT '处理人',
`remark` text COMMENT '备注',
`create_time` datetime NOT NULL COMMENT '创建时间',
PRIMARY KEY (`id`),
KEY `idx_work_order_id` (`work_order_id`),
KEY `idx_equipment_id` (`equipment_id`),
KEY `idx_downtime_start` (`downtime_start`)
) COMMENT='停机记录表';
```
**说明**:第一版停机信息记录在 `pro_report` 中即可。后续如需详细停机分析再建独立表。
### 3.3 结论
**第一版实施方案(最简化)**
-**只修改3张表**`md_material`, `pro_workorder`, `pro_report`
-**复用现有表**`dm_equipment`(设备)、`md_workshop`(车间)
-**不新建表**:班次、停机等信息用字段记录
-**向后兼容**:所有新增字段都有默认值
**后续优化方向P2**
- 如需班次时间管理 → 建 `md_shift`
- 如需停机详细分析 → 建 `pro_downtime_record`
- 如需产线管理 → 扩展 `md_workshop` 或新建产线表
---
## 4. 数据库改动(按执行顺序)
### 步骤1物料表新增字段
> 基于现有 `md_material` 表结构
```sql
-- 标识物料的制造类型和报工配置
ALTER TABLE md_material
ADD COLUMN manufacture_type VARCHAR(20) DEFAULT 'DISCRETE' COMMENT '制造类型: DISCRETE-离散, CONTINUOUS-连续',
ADD COLUMN report_mode VARCHAR(20) COMMENT '报工模式: PROCESS-按工序, TIME-按时间, SHIFT-按班次, QUANTITY-按产量, BATCH-按批次';
-- 创建索引
CREATE INDEX idx_manufacture_type ON md_material(manufacture_type);
```
**说明**
- `route_id` 字段已存在,用于关联工序路线(离散制造业使用)
- 连续制造业的物料可以不设置 `route_id`
### 步骤2工单表新增字段
> 基于现有 `pro_workorder` 表结构
```sql
-- 支持连续制造业的工单管理
ALTER TABLE pro_workorder
ADD COLUMN manufacture_type VARCHAR(20) DEFAULT 'DISCRETE' COMMENT '制造类型: DISCRETE-离散, CONTINUOUS-连续',
ADD COLUMN report_mode VARCHAR(20) DEFAULT 'PROCESS' COMMENT '报工模式: PROCESS-按工序, TIME-按时间',
ADD COLUMN total_report_count INT DEFAULT 0 COMMENT '总报工次数',
ADD COLUMN total_reported_quantity DECIMAL(10,3) DEFAULT 0.000 COMMENT '累计报工数量',
ADD COLUMN equipment_id BIGINT COMMENT '关联设备ID',
ADD COLUMN equipment_name VARCHAR(255) COMMENT '设备名称',
ADD COLUMN actual_start_time DATETIME COMMENT '实际开始时间',
ADD COLUMN actual_end_time DATETIME COMMENT '实际结束时间',
ADD COLUMN downtime_minutes INT DEFAULT 0 COMMENT '累计停机时间(分钟)';
-- 创建索引
CREATE INDEX idx_manufacture_type ON pro_workorder(manufacture_type);
CREATE INDEX idx_equipment_id ON pro_workorder(equipment_id);
CREATE INDEX idx_report_mode ON pro_workorder(report_mode);
```
**说明**
- `last_report_time` 字段已存在,无需新增
- 数量字段使用 `DECIMAL(10,3)` 与现有字段保持一致
### 步骤3报工单表新增字段
> 基于现有 `pro_report` 表结构
```sql
-- 支持连续制造业的报工记录
ALTER TABLE pro_report
ADD COLUMN work_order_id BIGINT COMMENT '工单ID(连续制造业直接关联工单)',
ADD COLUMN report_period_start DATETIME COMMENT '报工时段开始',
ADD COLUMN report_period_end DATETIME COMMENT '报工时段结束',
ADD COLUMN report_sequence INT COMMENT '报工序号',
ADD COLUMN shift_name VARCHAR(64) COMMENT '班次名称',
ADD COLUMN equipment_id BIGINT COMMENT '设备ID',
ADD COLUMN equipment_name VARCHAR(255) COMMENT '设备名称',
ADD COLUMN downtime_minutes INT DEFAULT 0 COMMENT '该时段停机时间(分钟)',
ADD COLUMN downtime_reason VARCHAR(500) COMMENT '停机原因';
-- 说明:报工人使用现有的 report_user_id 和 report_user_name 字段
-- 创建索引
CREATE INDEX idx_work_order_id ON pro_report(work_order_id);
CREATE INDEX idx_report_period ON pro_report(report_period_start, report_period_end);
CREATE INDEX idx_shift_name ON pro_report(shift_name);
CREATE INDEX idx_equipment_id ON pro_report(equipment_id);
```
**说明**
- `remark` 字段已存在,无需新增
- `qualified_quantity``unqualified_quantity` 字段已存在
- `quality_status` 字段已存在
### 步骤4销售订单明细表新增字段可选
> 用于标识订单明细的生产状态
```sql
-- 扩展销售订单明细的状态管理
-- status字段已存在可以使用以下状态值
-- A: 待生产
-- B: 生产中
-- C: 部分完成
-- D: 已完成
-- 无需新增字段使用现有的status和extend_field即可
```
### 步骤5数据迁移
```sql
-- 为现有数据设置默认值(确保兼容性)
UPDATE md_material SET manufacture_type = 'DISCRETE' WHERE manufacture_type IS NULL;
UPDATE pro_workorder SET manufacture_type = 'DISCRETE' WHERE manufacture_type IS NULL;
UPDATE pro_workorder SET report_mode = 'PROCESS' WHERE report_mode IS NULL;
UPDATE pro_workorder SET total_report_count = 0 WHERE total_report_count IS NULL;
UPDATE pro_workorder SET total_reported_quantity = 0.000 WHERE total_reported_quantity IS NULL;
UPDATE pro_workorder SET downtime_minutes = 0 WHERE downtime_minutes IS NULL;
```
---
## 4. 代码改动清单重点XML改动
### 4.1 实体类改动
#### Material.java
**文件路径**`com.yavii.domain.Material`
```java
// 新增字段
private String manufactureType; // 制造类型: DISCRETE/CONTINUOUS
private String reportMode; // 报工模式: PROCESS/TIME
private Integer reportInterval; // 报工间隔(分钟)
// getter/setter 略
```
#### ProWorkorder.java
**文件路径**`com.yavii.domain.ProWorkorder`
```java
// 新增字段
private String manufactureType; // 制造类型
private String reportMode; // 报工模式
private Integer totalReportCount; // 总报工次数
private BigDecimal totalReportedQuantity; // 累计报工数量
private Long equipmentId; // 设备ID
private String equipmentName; // 设备名称
private Date actualStartTime; // 实际开始时间
private Date actualEndTime; // 实际结束时间
private Integer downtimeMinutes; // 累计停机时间
// getter/setter 略
```
#### ProReport.java
**文件路径**`com.yavii.domain.ProReport`
```java
// 新增字段
private Long workOrderId; // 工单ID(连续制造业直接关联)
private Date reportPeriodStart; // 报工时段开始
private Date reportPeriodEnd; // 报工时段结束
private Integer reportSequence; // 报工序号
private String shiftName; // 班次名称
private Long equipmentId; // 设备ID
private String equipmentName; // 设备名称
private Integer downtimeMinutes; // 停机时间(分钟)
private String downtimeReason; // 停机原因
// 说明:报工人使用现有的 reportUserId 和 reportUserName 字段
// getter/setter 略
```
---
### 4.2 Mapper XML 改动(核心重点)
#### ✅ MaterialMapper.xml
**文件路径**`resources/mapper/MaterialMapper.xml`
**改动1resultMap 增加字段映射**
```xml
<resultMap id="MaterialResult" type="Material">
<id property="id" column="id"/>
<result property="number" column="number"/>
<result property="name" column="name"/>
<result property="specification" column="specification"/>
<result property="unitId" column="unit_id"/>
<result property="classId" column="class_id"/>
<result property="typeId" column="type_id"/>
<result property="status" column="status"/>
<result property="routeId" column="route_id"/>
<!-- ========== 新增字段映射 ========== -->
<result property="manufactureType" column="manufacture_type"/>
<result property="reportMode" column="report_mode"/>
<!-- ================================= -->
<result property="remark" column="remark"/>
<result property="createBy" column="create_by"/>
<result property="createTime" column="create_time"/>
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
</resultMap>
```
**改动2查询SQL 增加字段**
```xml
<sql id="selectMaterialVo">
select id, number, name, specification, unit_id, class_id, type_id,
status, route_id,
manufacture_type, report_mode, <!-- 新增 -->
remark, create_by, create_time, update_by, update_time
from md_material
</sql>
```
**改动3插入SQL 增加字段**
```xml
<insert id="insertMaterial" parameterType="Material">
insert into md_material
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="number != null">number,</if>
<if test="name != null">name,</if>
<!-- 其他现有字段... -->
<if test="manufactureType != null">manufacture_type,</if>
<if test="reportMode != null">report_mode,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="number != null">#{number},</if>
<if test="name != null">#{name},</if>
<!-- 其他现有字段... -->
<if test="manufactureType != null">#{manufactureType},</if>
<if test="reportMode != null">#{reportMode},</if>
</trim>
</insert>
```
**改动4更新SQL 增加字段**
```xml
<update id="updateMaterial" parameterType="Material">
update md_material
<trim prefix="SET" suffixOverrides=",">
<if test="name != null">name = #{name},</if>
<!-- 其他现有字段... -->
<if test="manufactureType != null">manufacture_type = #{manufactureType},</if>
<if test="reportMode != null">report_mode = #{reportMode},</if>
</trim>
where id = #{id}
</update>
```
---
#### ✅ ProWorkorderMapper.xml
**文件路径**`resources/mapper/ProWorkorderMapper.xml`
**改动1resultMap 增加字段映射**
```xml
<resultMap id="ProWorkorderResult" type="ProWorkorder">
<id property="id" column="id"/>
<result property="number" column="number"/>
<result property="batchNumber" column="batch_number"/>
<result property="materialId" column="material_id"/>
<result property="materialName" column="material_name"/>
<result property="quantity" column="quantity"/>
<result property="proStatus" column="pro_status"/>
<result property="lastReportTime" column="last_report_time"/>
<!-- ========== 新增字段映射 ========== -->
<result property="manufactureType" column="manufacture_type"/>
<result property="reportMode" column="report_mode"/>
<result property="totalReportCount" column="total_report_count"/>
<result property="totalReportedQuantity" column="total_reported_quantity"/>
<result property="equipmentId" column="equipment_id"/>
<result property="equipmentName" column="equipment_name"/>
<result property="actualStartTime" column="actual_start_time"/>
<result property="actualEndTime" column="actual_end_time"/>
<result property="downtimeMinutes" column="downtime_minutes"/>
<!-- ================================= -->
<!-- 其他现有字段... -->
</resultMap>
```
**改动2查询SQL 增加字段(关键:关联物料表)**
```xml
<sql id="selectProWorkorderVo">
select w.id, w.number, w.batch_number, w.material_id, w.material_name,
w.specification, w.material_unit_id, w.material_unit_name,
w.quantity, w.begin_pro_date, w.plan_finish_date, w.real_finish_date,
w.pro_status, w.current_process, w.last_report_time,
w.route_id, w.source_info,
<!-- ========== 新增字段 ========== -->
w.manufacture_type, w.report_mode, w.total_report_count,
w.total_reported_quantity, w.equipment_id, w.equipment_name,
w.actual_start_time, w.actual_end_time, w.downtime_minutes,
<!-- 关联物料表,获取物料的制造类型配置 -->
m.manufacture_type as material_manufacture_type,
m.report_mode as material_report_mode,
<!-- ================================= -->
w.remark, w.create_by, w.create_time, w.update_by, w.update_time
from pro_workorder w
left join md_material m on w.material_id = m.id
</sql>
```
**改动3插入SQL 增加字段**
```xml
<insert id="insertProWorkorder" parameterType="ProWorkorder" useGeneratedKeys="true" keyProperty="id">
insert into pro_workorder
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="number != null">number,</if>
<if test="batchNumber != null">batch_number,</if>
<!-- 其他现有字段... -->
<if test="manufactureType != null">manufacture_type,</if>
<if test="reportMode != null">report_mode,</if>
<if test="totalReportCount != null">total_report_count,</if>
<if test="totalReportedQuantity != null">total_reported_quantity,</if>
<if test="equipmentId != null">equipment_id,</if>
<if test="equipmentName != null">equipment_name,</if>
<if test="actualStartTime != null">actual_start_time,</if>
<if test="actualEndTime != null">actual_end_time,</if>
<if test="downtimeMinutes != null">downtime_minutes,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="number != null">#{number},</if>
<if test="batchNumber != null">#{batchNumber},</if>
<!-- 其他现有字段... -->
<if test="manufactureType != null">#{manufactureType},</if>
<if test="reportMode != null">#{reportMode},</if>
<if test="totalReportCount != null">#{totalReportCount},</if>
<if test="totalReportedQuantity != null">#{totalReportedQuantity},</if>
<if test="equipmentId != null">#{equipmentId},</if>
<if test="equipmentName != null">#{equipmentName},</if>
<if test="actualStartTime != null">#{actualStartTime},</if>
<if test="actualEndTime != null">#{actualEndTime},</if>
<if test="downtimeMinutes != null">#{downtimeMinutes},</if>
</trim>
</insert>
```
**改动4更新SQL 增加字段**
```xml
<update id="updateProWorkorder" parameterType="ProWorkorder">
update pro_workorder
<trim prefix="SET" suffixOverrides=",">
<if test="proStatus != null">pro_status = #{proStatus},</if>
<!-- 其他现有字段... -->
<if test="manufactureType != null">manufacture_type = #{manufactureType},</if>
<if test="reportMode != null">report_mode = #{reportMode},</if>
<if test="totalReportCount != null">total_report_count = #{totalReportCount},</if>
<if test="totalReportedQuantity != null">total_reported_quantity = #{totalReportedQuantity},</if>
<if test="equipmentId != null">equipment_id = #{equipmentId},</if>
<if test="equipmentName != null">equipment_name = #{equipmentName},</if>
<if test="actualStartTime != null">actual_start_time = #{actualStartTime},</if>
<if test="actualEndTime != null">actual_end_time = #{actualEndTime},</if>
<if test="downtimeMinutes != null">downtime_minutes = #{downtimeMinutes},</if>
<if test="lastReportTime != null">last_report_time = #{lastReportTime},</if>
</trim>
where id = #{id}
</update>
```
**改动5新增查询方法查询连续制造业工单**
```xml
<!-- 查询连续制造业工单列表 -->
<select id="selectContinuousWorkOrders" resultMap="ProWorkorderResult">
<include refid="selectProWorkorderVo"/>
where w.manufacture_type = 'CONTINUOUS'
<if test="proStatus != null and proStatus != ''">
and w.pro_status = #{proStatus}
</if>
order by w.create_time desc
</select>
```
---
#### ✅ ProReportMapper.xml
**文件路径**`resources/mapper/ProReportMapper.xml`
**改动1resultMap 增加字段映射**
```xml
<resultMap id="ProReportResult" type="ProReport">
<id property="id" column="id"/>
<result property="number" column="number"/>
<result property="workOrderEntryId" column="work_order_entry_id"/>
<result property="reportQuantity" column="report_quantity"/>
<result property="qualifiedQuantity" column="qualified_quantity"/>
<result property="unqualifiedQuantity" column="unqualified_quantity"/>
<result property="reportTime" column="report_time"/>
<result property="qualityStatus" column="quality_status"/>
<!-- ========== 新增字段映射 ========== -->
<result property="workOrderId" column="work_order_id"/>
<result property="reportPeriodStart" column="report_period_start"/>
<result property="reportPeriodEnd" column="report_period_end"/>
<result property="reportSequence" column="report_sequence"/>
<result property="shiftName" column="shift_name"/>
<result property="equipmentId" column="equipment_id"/>
<result property="equipmentName" column="equipment_name"/>
<result property="downtimeMinutes" column="downtime_minutes"/>
<result property="downtimeReason" column="downtime_reason"/>
<!-- ================================= -->
<result property="remark" column="remark"/>
<!-- 其他现有字段... -->
</resultMap>
```
**改动2查询SQL 增加字段(关键:关联工单表判断制造类型)**
```xml
<sql id="selectProReportVo">
select r.id, r.number, r.work_order_entry_id, r.report_user_id, r.report_user_name,
r.report_time, r.report_quantity, r.qualified_quantity, r.unqualified_quantity,
r.quality_status, r.workshop_id, r.workshop_name, r.station_id, r.station_name,
<!-- ========== 新增字段 ========== -->
r.work_order_id, r.report_period_start, r.report_period_end,
r.report_sequence, r.shift_name,
r.equipment_id, r.equipment_name, r.downtime_minutes, r.downtime_reason,
<!-- 关联工单表,获取工单信息 -->
w.number as workorder_number,
w.manufacture_type as workorder_manufacture_type,
w.material_name as workorder_material_name,
<!-- ================================= -->
r.remark, r.create_by, r.create_time
from pro_report r
<!-- 左关联工单(连续制造业直接关联) -->
left join pro_workorder w on r.work_order_id = w.id
<!-- 左关联工单分录(离散制造业通过分录关联) -->
left join pro_workorder_entry e on r.work_order_entry_id = e.id
left join pro_workorder w2 on e.workorder_id = w2.id
</sql>
```
**改动3插入SQL 增加字段**
```xml
<insert id="insertProReport" parameterType="ProReport" useGeneratedKeys="true" keyProperty="id">
insert into pro_report
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="number != null">number,</if>
<if test="workOrderEntryId != null">work_order_entry_id,</if>
<!-- 其他现有字段... -->
<if test="workOrderId != null">work_order_id,</if>
<if test="reportPeriodStart != null">report_period_start,</if>
<if test="reportPeriodEnd != null">report_period_end,</if>
<if test="reportSequence != null">report_sequence,</if>
<if test="shiftName != null">shift_name,</if>
<if test="equipmentId != null">equipment_id,</if>
<if test="equipmentName != null">equipment_name,</if>
<if test="downtimeMinutes != null">downtime_minutes,</if>
<if test="downtimeReason != null">downtime_reason,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="number != null">#{number},</if>
<if test="workOrderEntryId != null">#{workOrderEntryId},</if>
<!-- 其他现有字段... -->
<if test="workOrderId != null">#{workOrderId},</if>
<if test="reportPeriodStart != null">#{reportPeriodStart},</if>
<if test="reportPeriodEnd != null">#{reportPeriodEnd},</if>
<if test="reportSequence != null">#{reportSequence},</if>
<if test="shiftName != null">#{shiftName},</if>
<if test="equipmentId != null">#{equipmentId},</if>
<if test="equipmentName != null">#{equipmentName},</if>
<if test="downtimeMinutes != null">#{downtimeMinutes},</if>
<if test="downtimeReason != null">#{downtimeReason},</if>
</trim>
</insert>
```
**改动4新增查询方法按工单ID查询报工记录**
```xml
<!-- 根据工单ID查询报工记录连续制造业使用 -->
<select id="selectReportsByWorkOrderId" resultMap="ProReportResult">
<include refid="selectProReportVo"/>
where r.work_order_id = #{workOrderId}
order by r.report_period_start desc, r.report_sequence desc
</select>
<!-- 检查时间段是否重叠 -->
<select id="checkTimeOverlap" resultType="int">
select count(1)
from pro_report
where work_order_id = #{workOrderId}
and (
(report_period_start &lt;= #{periodStart} and report_period_end &gt; #{periodStart})
or (report_period_start &lt; #{periodEnd} and report_period_end &gt;= #{periodEnd})
or (report_period_start &gt;= #{periodStart} and report_period_end &lt;= #{periodEnd})
)
</select>
<!-- 获取下一个报工序号 -->
<select id="getNextSequence" resultType="int">
select IFNULL(MAX(report_sequence), 0) + 1
from pro_report
where work_order_id = #{workOrderId}
</select>
```
---
#### ✅ SalOrderMapper.xml可选改动
**文件路径**`resources/mapper/SalOrderMapper.xml`
**改动:查询订单明细时关联物料的制造类型**
```xml
<sql id="selectSalOrderEntryVo">
select e.id, e.main_id, e.material_id, e.material_name, e.material_specification,
e.quantity, e.status, e.delivery_date,
<!-- 关联物料表,获取制造类型 -->
m.manufacture_type as material_manufacture_type,
m.report_mode as material_report_mode,
<!-- 其他字段... -->
from sal_order_entry e
left join md_material m on e.material_id = m.id
</sql>
```
---
### 4.3 Mapper 接口改动
#### ProWorkorderMapper.java
```java
/**
* 查询连续制造业工单列表
*/
List<ProWorkorder> selectContinuousWorkOrders(@Param("proStatus") String proStatus);
```
#### ProReportMapper.java
```java
/**
* 根据工单ID查询报工记录
*/
List<ProReport> selectReportsByWorkOrderId(@Param("workOrderId") Long workOrderId);
/**
* 检查时间段是否重叠
*/
int checkTimeOverlap(@Param("workOrderId") Long workOrderId,
@Param("periodStart") Date periodStart,
@Param("periodEnd") Date periodEnd);
/**
* 获取下一个报工序号
*/
int getNextSequence(@Param("workOrderId") Long workOrderId);
```
---
## 5. 业务流程改动
### 4.1 连续制造业流程
```
销售订单 → 生成一个工单(不按工序) → 按时间段报工(多次) → 累计达到计划数量 → 完成
```
### 4.2 工单生成逻辑
**离散制造业(保持不变)**
```java
// 按工序路线生成多个工单
for (RouteProcess process : routeProcessList) {
WorkOrder workOrder = new WorkOrder();
workOrder.setManufactureType("DISCRETE");
workOrder.setBatchNumber(orderNumber + "-P" + processSort);
// ...
}
```
**连续制造业(新增)**
```java
// 只生成一个工单
WorkOrder workOrder = new WorkOrder();
workOrder.setManufactureType("CONTINUOUS");
workOrder.setReportMode(material.getReportMode()); // 从物料继承
workOrder.setBatchNumber(orderNumber); // 不使用-P序号
workOrder.setEquipmentId(dto.getEquipmentId());
// 不创建工单分录
```
### 4.3 报工逻辑
**离散制造业(保持不变)**
```java
// 按工序报工,关联工单分录
Report report = new Report();
report.setWorkOrderEntryId(entryId);
report.setReportQuantity(quantity);
```
**连续制造业(新增)**
```java
// 按时间段报工,直接关联工单
Report report = new Report();
report.setWorkOrderId(workOrderId); // 直接关联工单
report.setReportPeriodStart(periodStart);
report.setReportPeriodEnd(periodEnd);
report.setReportSequence(sequence);
report.setShiftName(shiftName);
report.setReportQuantity(quantity);
// 更新工单累计数量
workOrder.setTotalReportedQuantity(
workOrder.getTotalReportedQuantity().add(quantity)
);
workOrder.setTotalReportCount(workOrder.getTotalReportCount() + 1);
// 检查是否完成
if (workOrder.getTotalReportedQuantity().compareTo(workOrder.getQuantity()) >= 0) {
workOrder.setProStatus("D"); // 已完成
}
```
---
## 5. 前端改动
### 5.1 物料管理页面
**新增字段**
- 制造类型:单选(离散制造业/连续制造业)
- 报工模式:下拉(按工序/按时间/按产量)
- 报工间隔:数字输入(分钟)
### 5.2 新增页面:连续制造业报工
**路由**`/production/continuous-report`
**页面布局**
```
┌─────────────────────────────────────┐
│ 1. 工单选择区 │
│ - 显示工单信息、设备、进度 │
├─────────────────────────────────────┤
│ 2. 快速报工表单 │
│ - 时间段、班次、操作员 │
│ - 产量、合格数、不合格数 │
│ - 停机时间、停机原因 │
│ - 备注 │
├─────────────────────────────────────┤
│ 3. 报工历史列表 │
│ - 显示已报工记录 │
├─────────────────────────────────────┤
│ 4. 产量趋势图表 │
│ - 按小时/班次显示产量趋势 │
└─────────────────────────────────────┘
```
### 5.3 工单管理页面
**新增列**
- 制造类型(标签显示)
- 累计报工数量(连续制造业显示)
- 报工次数(连续制造业显示)
---
## 6. 后端接口(新增)
### 6.1 获取连续制造业工单列表
```
GET /api/production/continuous/workorders
参数: status, equipmentId, pageNum, pageSize
```
### 6.2 提交连续制造业报工
```
POST /api/production/continuous/report/submit
参数: {
workOrderId,
reportPeriodStart,
reportPeriodEnd,
reportQuantity,
qualifiedQuantity,
unqualifiedQuantity,
shiftName,
downtimeMinutes,
downtimeReason,
remark
}
```
### 6.3 获取报工历史
```
GET /api/production/continuous/report/history
参数: workOrderId, startTime, endTime
```
### 6.4 获取产量趋势
```
GET /api/production/continuous/report/trend
参数: workOrderId, startTime, endTime, groupBy(HOUR/SHIFT/DAY)
```
---
## 7. 实施步骤
### 第一阶段数据库改动1天
1. 执行SQL脚本新增字段
2. 执行数据迁移脚本
3. 验证现有功能不受影响
### 第二阶段后端开发5天
#### 2.1 实体类和Mapper改动1天
1. 修改 `Material.java`:新增 `manufactureType`, `reportMode`
2. 修改 `ProWorkorder.java`:新增 9个字段
3. 修改 `ProReport.java`:新增 9个字段
4. 修改 `MaterialMapper.xml`:新增字段映射
5. 修改 `ProWorkorderMapper.xml`:新增字段映射
6. 修改 `ProReportMapper.xml`:新增字段映射
#### 2.2 Service层改动2天
1. **AutoCompleteService**
- 修改 `autoCompleteSaleOrder` 方法,增加制造类型判断
- 新增 `autoCompleteContinuous` 方法(连续制造业一键完成)
- 新增 `autoCompleteDiscrete` 方法(重构现有逻辑)
2. **TimedCompleteService**
- 修改 `batchExecute` 方法,增加制造类型判断
- 跳过或特殊处理连续制造业订单
3. **ReportService**
- 修改 `submitReport` 方法,增加制造类型判断
- 新增 `submitContinuousReport` 方法(连续制造业报工)
- 新增 `submitDiscreteReport` 方法(重构现有逻辑)
4. **WorkOrderService**
- 修改工单生成逻辑,支持连续制造业
- 新增工单完成判断逻辑(累计数量 >= 计划数量)
#### 2.3 Controller和API1天
1. 新增连续制造业报工接口
2. 新增报工历史查询接口
3. 新增产量趋势统计接口
4. 修改现有接口,支持新字段
#### 2.4 单元测试1天
1. 测试连续制造业工单生成
2. 测试连续制造业报工
3. 测试一键完成功能(离散+连续)
4. 测试定时完成功能(离散+连续)
### 第三阶段前端开发5天
#### 3.1 物料管理页面1天
1. 新增制造类型字段(下拉框)
2. 新增报工模式字段(下拉框)
3. 实现字段联动逻辑
4. 表单验证
#### 3.2 报工页面改造2天
1. **创建连续制造业报工页面**
- 按时间报工界面(时段选择)
- 按班次报工界面(班次选择)
- 按产量报工界面(累计产量)
- 按批次报工界面(批次号)
2. **改造现有报工页面**
- 增加制造类型判断
- 根据报工模式动态显示控件
- 报工序号自动生成
#### 3.3 工序执行情况页面改造1天
1. **一键完成功能改造**
- 增加制造类型判断
- 连续制造业:只生成工单,不自动报工
- 离散制造业:保持现有逻辑
2. **定时完成功能改造**
- 增加制造类型判断
- 连续制造业:跳过或仅生成工单
3. **进度条显示改造**
- 离散制造业:显示工序进度条
- 连续制造业:显示报工次数和累计产量
#### 3.4 工单管理页面0.5天)
1. 列表新增列:制造类型、报工模式、报工次数、累计报工数量
2. 详情页显示:设备信息、实际时间、停机时间
3. 连续制造业:显示报工历史列表
#### 3.5 集成测试0.5天)
1. 端到端测试(物料 → 订单 → 工单 → 报工)
2. 兼容性测试(离散制造业功能不受影响)
### 第四阶段测试上线2天
1. 功能测试(所有报工模式)
2. 兼容性测试(离散制造业)
3. 性能测试(批量报工)
4. 用户培训
5. 上线部署
---
## 8. 注意事项
### 8.1 兼容性
- ✅ 现有离散制造业流程完全不受影响
- ✅ 新增字段都有默认值
- ✅ 接口向后兼容
### 8.2 性能优化
- 创建必要的索引已在SQL中体现
- 报工历史查询限制时间范围最近30天
- 使用分页查询
### 8.3 数据验证
- 报工时间段不能重叠
- 累计报工数量不能超过计划数量太多允许10%误差)
- 合格数量 + 不合格数量 = 报工数量
### 8.4 关键实现细节
#### 8.4.1 字段使用说明
**pro_report 表字段使用规则**
- **离散制造业**
- 必填:`work_order_entry_id`工单分录ID
- 不填:`work_order_id`, `report_period_start/end`, `report_sequence`, `shift_name`
- **连续制造业**
- 必填:`work_order_id`工单ID
- 可选:`report_period_start/end`TIME/SHIFT模式使用
- 可选:`shift_name`SHIFT模式使用
- 可选:`report_sequence`(所有模式建议使用,便于排序)
- 不填:`work_order_entry_id`设为NULL或0
#### 8.4.2 报工人字段说明
- 使用现有字段 `report_user_id``report_user_name` 记录主报工人
- 如需记录多人协作,可在 `remark` 字段中补充说明
- **已删除字段**`operator_ids`(与现有字段重复,已移除)
#### 8.4.3 工单完成判断逻辑
```java
// 连续制造业工单完成判断
if (workOrder.getTotalReportedQuantity() >= workOrder.getQuantity()) {
workOrder.setProStatus("FINISHED");
workOrder.setRealFinishDate(new Date());
}
```
#### 8.4.4 报工序号自动生成
```java
// 获取当前工单的最大报工序号
Integer maxSequence = reportMapper.selectMaxSequence(workOrderId);
report.setReportSequence(maxSequence == null ? 1 : maxSequence + 1);
```
#### 8.4.5 设备关联说明
- `pro_workorder.equipment_id``pro_report.equipment_id` 都关联到 `dm_equipment.id`
- 工单级别的设备ID表示主要使用的设备
- 报工级别的设备ID允许不同支持多设备轮换
#### 8.4.6 销售订单表无需修改
- `sal_order` 表**不需要**新增任何字段
- 制造类型信息存储在 `md_material` 表中
- 工单生成时从物料表读取 `manufacture_type``report_mode`
- 保持销售订单与制造类型解耦
### 8.5 前端界面改动
#### 8.5.1 报工页面控件差异
**核心原则**:报工页面根据**工单的制造类型和报工模式**动态显示不同控件。
**实现逻辑**
```javascript
// 1. 选择工单后,查询工单详情
getWorkOrderDetail(workOrderId) {
getWorkOrder(workOrderId).then(response => {
this.workOrder = response.data;
this.manufactureType = response.data.manufactureType; // DISCRETE 或 CONTINUOUS
this.reportMode = response.data.reportMode; // PROCESS, TIME, SHIFT, QUANTITY, BATCH
});
}
// 2. 根据制造类型和报工模式显示不同控件
<template v-if="manufactureType === 'DISCRETE'">
<!-- 离散制造业:显示工序选择 -->
</template>
<template v-if="manufactureType === 'CONTINUOUS' && reportMode === 'TIME'">
<!-- 连续制造业-按时间:显示时段选择 -->
</template>
```
**离散制造业PROCESS模式**
- ✅ 显示工序选择下拉框
- ✅ 显示工单分录列表
- ✅ 报工时间:单个时间点
- ✅ 报工数量:单次报工数量
- ✅ 字段:`workOrderEntryId`(关联工单分录)
- ❌ 不显示:时段选择、班次选择、报工序号
**连续制造业 - 按时间TIME模式**
- ❌ 不显示工序选择
- ✅ 显示时段选择:开始时间 + 结束时间
- ✅ 显示报工序号后端自动生成第1次、第2次...
- ✅ 显示设备选择
- ✅ 显示停机时间和停机原因
- ✅ 报工数量:该时段产量
- ✅ 字段:`workOrderId`, `reportPeriodStart`, `reportPeriodEnd`, `equipmentId`, `downtimeMinutes`
**连续制造业 - 按班次SHIFT模式**
- ❌ 不显示工序选择
- ✅ 显示班次选择下拉框(使用字典:`sys_shift_type`
- ✅ 显示时段选择:班次开始时间 + 结束时间
- ✅ 显示报工序号(后端自动生成)
- ✅ 显示设备选择
- ✅ 报工数量:该班次产量
- ✅ 字段:`workOrderId`, `shiftName`, `reportPeriodStart`, `reportPeriodEnd`, `equipmentId`
**连续制造业 - 按产量QUANTITY模式**
- ❌ 不显示工序选择
- ❌ 不显示时段选择
- ✅ 显示累计产量(只读,从工单获取)
- ✅ 显示报工序号(后端自动生成)
- ✅ 显示设备选择
- ✅ 报工数量:本次报工产量
- ✅ 系统自动计算:累计产量 = 前次累计 + 本次报工
- ✅ 字段:`workOrderId`, `reportQuantity`, `equipmentId`
**连续制造业 - 按批次BATCH模式**
- ❌ 不显示工序选择
- ✅ 显示批次说明输入框
- ✅ 显示报工序号(后端自动生成)
- ✅ 显示设备选择
- ✅ 报工数量:该批次产量
- ✅ 备注字段:记录批次详细信息
- ✅ 字段:`workOrderId`, `reportQuantity`, `remark`, `equipmentId`
**字段映射关系**
| 制造类型 | 报工模式 | 关键字段 | 后端判断依据 |
|---------|---------|---------|------------|
| DISCRETE | PROCESS | `workOrderEntryId` | `workOrderEntryId != null` |
| CONTINUOUS | TIME | `workOrderId`, `reportPeriodStart/End` | `workOrderId != null` |
| CONTINUOUS | SHIFT | `workOrderId`, `shiftName` | `workOrderId != null` |
| CONTINUOUS | QUANTITY | `workOrderId` | `workOrderId != null` |
| CONTINUOUS | BATCH | `workOrderId` | `workOrderId != null` |
**前端示例代码**
```vue
<template>
<el-form ref="reportForm" :model="reportForm">
<!-- 公共字段 -->
<el-form-item label="报工数量" prop="reportQuantity">
<el-input-number v-model="reportForm.reportQuantity" :min="0" />
</el-form-item>
<!-- 离散制造业:工序选择 -->
<template v-if="manufactureType === 'DISCRETE'">
<el-form-item label="工序" prop="workOrderEntryId">
<el-select v-model="reportForm.workOrderEntryId">
<el-option v-for="entry in workOrderEntryList"
:key="entry.id"
:label="entry.processName"
:value="entry.id" />
</el-select>
</el-form-item>
</template>
<!-- 连续制造业-按时间 -->
<template v-if="manufactureType === 'CONTINUOUS' && reportMode === 'TIME'">
<el-form-item label="报工时段">
<el-date-picker v-model="reportForm.reportPeriod"
type="datetimerange"
value-format="yyyy-MM-dd HH:mm:ss" />
</el-form-item>
<el-form-item label="设备">
<el-select v-model="reportForm.equipmentId">
<el-option v-for="equip in equipmentList"
:key="equip.id"
:label="equip.name"
:value="equip.id" />
</el-select>
</el-form-item>
<el-form-item label="停机时间(分钟)">
<el-input-number v-model="reportForm.downtimeMinutes" :min="0" />
</el-form-item>
</template>
<!-- 连续制造业-按班次 -->
<template v-if="manufactureType === 'CONTINUOUS' && reportMode === 'SHIFT'">
<el-form-item label="班次">
<el-select v-model="reportForm.shiftName">
<el-option v-for="dict in dict.type.sys_shift_type"
:key="dict.value"
:label="dict.label"
:value="dict.label" />
</el-select>
</el-form-item>
</template>
</el-form>
</template>
<script>
export default {
dicts: ['sys_shift_type'], // 使用班次字典
data() {
return {
manufactureType: null,
reportMode: null,
reportForm: {
workOrderId: null, // 连续制造业使用
workOrderEntryId: null, // 离散制造业使用
reportQuantity: null,
reportPeriodStart: null,
reportPeriodEnd: null,
shiftName: null,
equipmentId: null,
downtimeMinutes: 0
}
}
}
}
</script>
```
#### 8.5.2 工序执行情况页面改动
**现有功能**
- 一键完成:为离散制造业订单自动生成工单和报工
- 定时完成(批量完成):定时批量处理超期订单
**需要修改的逻辑**
1. **一键完成功能**
```java
// 现有逻辑:按工序路线生成多个工单
if (DISCRETE) {
for (每个工序) {
创建工单batch_number = "XS001-P01", "XS001-P02"
创建工单分录
创建报工单
}
}
// 新增逻辑:连续制造业生成单个工单
if (CONTINUOUS) {
创建工单batch_number = "XS001"(不按工序)
不创建工单分录
不自动创建报工单(由用户按时间/班次/产量/批次报工)
}
```
2. **定时完成(批量完成)功能**
```java
// 需要判断制造类型
if (material.getManufactureType() == 'DISCRETE') {
// 使用现有逻辑:自动完成所有工序
autoCompleteAllProcesses();
} else if (material.getManufactureType() == 'CONTINUOUS') {
// 连续制造业:只生成工单,不自动报工
// 或者:跳过连续制造业订单
skipOrGenerateWorkOrderOnly();
}
```
3. **工序进度条显示**
```javascript
// 离散制造业:显示工序进度条
if (order.manufactureType === 'DISCRETE') {
显示工序步骤条工序1 → 工序2 → 工序3
}
// 连续制造业:显示报工次数进度
if (order.manufactureType === 'CONTINUOUS') {
显示报工进度:已报工 X 次,累计 Y 吨 / 计划 Z 吨
}
```
#### 8.5.3 物料管理页面改动
**新增字段**
- 制造类型:下拉框(离散/连续)
- 报工模式:下拉框(按工序/按时间/按班次/按产量/按批次)
- 当制造类型=离散时,报工模式固定为"按工序"
- 当制造类型=连续时报工模式可选其他4种
**字段联动**
```javascript
onManufactureTypeChange(value) {
if (value === 'DISCRETE') {
this.form.reportMode = 'PROCESS'
this.reportModeDisabled = true
} else {
this.reportModeDisabled = false
this.form.reportMode = 'TIME' // 默认按时间
}
}
```
#### 8.5.4 工单管理页面改动
**列表显示**
- 新增列:制造类型(离散/连续)
- 新增列:报工模式
- 新增列:报工次数(连续制造业显示)
- 新增列:累计报工数量(连续制造业显示)
**详情页面**
- 显示设备信息
- 显示实际开始/结束时间
- 显示累计停机时间
- 连续制造业:显示报工历史列表
### 8.6 统计报表功能改动
#### 8.6.1 WorkOrderExecutionService工单执行情况统计
**文件**`WorkOrderExecutionServiceImpl.java``WorkOrderExecutionMapper.xml`
**需要修改的功能**
1. **工单列表查询**
```xml
<!-- 需要增加制造类型和报工模式字段 -->
<select id="selectWorkOrderExecutionList">
SELECT
pw.id, pw.number, pw.material_name, pw.specification,
pw.quantity, pw.batch_number, pw.pro_status,
<!-- 新增字段 -->
pw.manufacture_type, pw.report_mode,
pw.total_report_count, pw.total_reported_quantity,
<!-- 现有字段 -->
pw.begin_pro_date, pw.plan_finish_date,
pw.current_process_name, pw.completion_rate
FROM pro_workorder pw
WHERE pw.status = 'A'
</select>
```
2. **工序列表查询(离散制造业)**
```xml
<!-- 现有逻辑:查询工单分录 -->
<select id="selectProcessListByWorkOrderId">
SELECT pwe.id, pwe.process_name, pwe.process_sort,
pwe.report_quantity, pwe.reported_quantity
FROM pro_workorder_entry pwe
WHERE pwe.workorder_id = #{workOrderId}
AND pwe.type = 'report'
ORDER BY pwe.process_sort ASC
</select>
```
3. **报工历史查询(连续制造业)**
```xml
<!-- 新增:直接查询工单的报工记录 -->
<select id="selectReportListByWorkOrderId">
SELECT pr.id, pr.number, pr.report_user_name,
pr.report_time, pr.report_quantity,
pr.report_sequence, pr.shift_name,
pr.report_period_start, pr.report_period_end
FROM pro_report pr
WHERE pr.work_order_id = #{workOrderId}
AND pr.status = 'A'
ORDER BY pr.report_sequence ASC
</select>
```
4. **Service层适配**
```java
@Override
public List<ProcessExecutionVO> selectProcessListByWorkOrderId(Long workOrderId) {
// 1. 查询工单信息,判断制造类型
WorkOrder workOrder = workOrderMapper.selectById(workOrderId);
if ("DISCRETE".equals(workOrder.getManufactureType())) {
// 离散制造业:查询工序列表
return workOrderExecutionMapper.selectProcessListByWorkOrderId(workOrderId);
} else if ("CONTINUOUS".equals(workOrder.getManufactureType())) {
// 连续制造业:返回报工历史摘要
return buildContinuousProcessSummary(workOrderId, workOrder);
}
}
// 新增方法:构建连续制造业的"工序"摘要(实际是报工摘要)
private List<ProcessExecutionVO> buildContinuousProcessSummary(Long workOrderId, WorkOrder workOrder) {
List<ProcessExecutionVO> summary = new ArrayList<>();
// 创建一个虚拟的"连续生产"工序
ProcessExecutionVO continuousProcess = new ProcessExecutionVO();
continuousProcess.setId(workOrderId);
continuousProcess.setProcessName("连续生产");
continuousProcess.setProcessSort(1);
continuousProcess.setPlanQuantity(workOrder.getQuantity());
continuousProcess.setReportedQuantity(workOrder.getTotalReportedQuantity());
// 计算完成率
BigDecimal completionRate = BigDecimal.ZERO;
if (workOrder.getQuantity().compareTo(BigDecimal.ZERO) > 0) {
completionRate = workOrder.getTotalReportedQuantity()
.divide(workOrder.getQuantity(), 2, RoundingMode.HALF_UP)
.multiply(new BigDecimal("100"));
}
continuousProcess.setCompletionRate(completionRate);
// 设置状态
if (completionRate.compareTo(new BigDecimal("100")) >= 0) {
continuousProcess.setStatus("已完成");
} else if (completionRate.compareTo(BigDecimal.ZERO) > 0) {
continuousProcess.setStatus("进行中");
} else {
continuousProcess.setStatus("未开始");
}
// 设置报工次数
continuousProcess.setReportCount(workOrder.getTotalReportCount());
summary.add(continuousProcess);
return summary;
}
```
### 8.7 前端统计页面改动
#### 8.7.1 工单执行情况页面
**文件**`mes-ui/src/views/mes/statement/workOrderExecution/index.vue`
**需要修改的显示逻辑**
1. **工单卡片显示**
```javascript
// 在工单卡片中显示制造类型和报工模式
<div class="header-right">
<span class="manufacture-type">
{{ workOrder.manufactureType === 'DISCRETE' ? '离散制造' : '连续制造' }}
</span>
<span class="report-mode">
{{ getReportModeText(workOrder.reportMode) }}
</span>
<span class="batch-number">批次:{{ workOrder.batchNumber }}</span>
<span class="quantity">数量:{{ workOrder.quantity }}</span>
</div>
// 方法:获取报工模式文本
getReportModeText(mode) {
const modeMap = {
'PROCESS': '按工序',
'TIME': '按时间',
'SHIFT': '按班次',
'QUANTITY': '按产量',
'BATCH': '按批次'
};
return modeMap[mode] || mode;
}
```
2. **进度条显示差异**
```javascript
// 离散制造业:显示工序进度
<div v-if="workOrder.manufactureType === 'DISCRETE'" class="process-progress">
<span class="current-process">
当前工序:{{ workOrder.currentProcess || '未开始' }}
</span>
<span class="process-count">
{{ workOrder.completedProcessCount }}/{{ workOrder.totalProcessCount }} 个工序
</span>
</div>
// 连续制造业:显示报工次数和累计产量
<div v-else class="continuous-progress">
<span class="report-count">
已报工:{{ workOrder.totalReportCount }} 次
</span>
<span class="reported-quantity">
累计产量:{{ workOrder.totalReportedQuantity }}/{{ workOrder.quantity }}
</span>
</div>
```
3. **展开内容差异**
```javascript
// 离散制造业:显示工序列表
<el-table v-if="workOrder.manufactureType === 'DISCRETE'"
:data="workOrder.processList">
<el-table-column label="工序名称" prop="processName" />
<el-table-column label="完成率" prop="completionRate" />
<!-- ... 其他工序字段 -->
</el-table>
// 连续制造业:显示报工历史
<el-table v-else :data="workOrder.reportList">
<el-table-column label="报工序号" prop="reportSequence" />
<el-table-column label="报工时间" prop="reportTime" />
<el-table-column label="报工数量" prop="reportQuantity" />
<el-table-column label="班次" prop="shiftName" />
<el-table-column label="时段" width="200">
<template slot-scope="scope">
{{ scope.row.reportPeriodStart }} ~ {{ scope.row.reportPeriodEnd }}
</template>
</el-table-column>
</el-table>
```
### 8.8 后端Service改动
#### 8.6.1 AutoCompleteService一键完成
**文件**`AutoCompleteServiceImpl.java`
**需要修改的方法**
```java
@Override
@Transactional
public AjaxResult autoCompleteSaleOrder(AutoCompleteDTO dto) {
// 1. 查询物料信息
Material material = materialMapper.selectById(salOrderEntry.getMaterialId());
// 2. 判断制造类型
if ("DISCRETE".equals(material.getManufactureType())) {
// 现有逻辑:按工序生成多个工单 + 自动报工
return autoCompleteDiscrete(dto, material);
} else if ("CONTINUOUS".equals(material.getManufactureType())) {
// 新增逻辑:生成单个工单,不自动报工
return autoCompleteContinuous(dto, material);
}
}
// 新增方法:连续制造业一键完成
private AjaxResult autoCompleteContinuous(AutoCompleteDTO dto, Material material) {
// 1. 生成单个工单(不按工序)
WorkOrder workOrder = new WorkOrder();
workOrder.setBatchNumber(salOrder.getNumber()); // 批次号 = 订单号
workOrder.setManufactureType("CONTINUOUS");
workOrder.setReportMode(material.getReportMode());
workOrder.setQuantity(salOrderEntry.getQuantity());
// ... 其他字段
workOrderMapper.insert(workOrder);
// 2. 不创建工单分录
// 3. 不自动创建报工单
// 4. 更新订单状态为"已生成工单"(不是"生产完成"
return AjaxResult.success("工单生成成功,请手动报工");
}
```
#### 8.6.2 TimedCompleteService定时完成
**文件**`TimedCompleteServiceImpl.java`
**需要修改的方法**
```java
@Override
public AjaxResult batchExecute(BatchExecuteDTO dto) {
List<OverdueOrderVO> orders = getOverdueOrders(...);
for (OverdueOrderVO order : orders) {
// 查询物料信息
Material material = materialMapper.selectById(order.getMaterialId());
// 判断制造类型
if ("DISCRETE".equals(material.getManufactureType())) {
// 自动完成
autoCompleteService.autoCompleteSaleOrder(...);
} else if ("CONTINUOUS".equals(material.getManufactureType())) {
// 跳过或仅生成工单
log.info("跳过连续制造业订单: " + order.getOrderNumber());
skipCount++;
}
}
}
```
#### 8.6.3 ReportService报工服务
**需要修改的方法**
```java
@Override
public AjaxResult submitReport(Report report) {
// 1. 判断是离散还是连续制造业
if (report.getWorkOrderEntryId() != null) {
// 离散制造业:现有逻辑
return submitDiscreteReport(report);
} else if (report.getWorkOrderId() != null) {
// 连续制造业:新逻辑
return submitContinuousReport(report);
}
}
// 新增方法:连续制造业报工
private AjaxResult submitContinuousReport(Report report) {
// 1. 查询工单
WorkOrder workOrder = workOrderMapper.selectById(report.getWorkOrderId());
// 2. 自动生成报工序号
Integer maxSequence = reportMapper.selectMaxSequence(report.getWorkOrderId());
report.setReportSequence(maxSequence == null ? 1 : maxSequence + 1);
// 3. 插入报工单
reportMapper.insert(report);
// 4. 更新工单累计数据
workOrder.setTotalReportCount(workOrder.getTotalReportCount() + 1);
workOrder.setTotalReportedQuantity(
workOrder.getTotalReportedQuantity().add(report.getReportQuantity())
);
// 5. 判断是否完成
if (workOrder.getTotalReportedQuantity().compareTo(workOrder.getQuantity()) >= 0) {
workOrder.setProStatus("FINISHED");
workOrder.setRealFinishDate(new Date());
}
workOrderMapper.updateById(workOrder);
return AjaxResult.success("报工成功");
}
```
#### 8.6.4 WorkOrderService工单服务
**需要修改的方法**
1. **insertWorkOrder 方法**
```java
@Override
public int insertWorkOrder(WorkOrder workOrder) {
// ... 现有逻辑 ...
// 新增:保存工单后,判断是否需要创建工单分录
int rows = workOrderMapper.insert(workOrder);
// 判断制造类型
if ("DISCRETE".equals(workOrder.getManufactureType())) {
// 离散制造业:创建工单分录(按工序)
insertWorkOrderEntry(workOrder);
} else if ("CONTINUOUS".equals(workOrder.getManufactureType())) {
// 连续制造业:不创建工单分录
// 工单直接可用于报工
}
return rows;
}
```
2. **insertWorkOrderEntry 方法**
```java
public void insertWorkOrderEntry(WorkOrder workOrder) {
// 检查制造类型
if ("CONTINUOUS".equals(workOrder.getManufactureType())) {
// 连续制造业不创建工单分录
return;
}
// ... 现有逻辑(为离散制造业创建工单分录)...
}
```
3. **批量生成工单方法**
```java
// 从销售订单批量生成工单时,需要判断物料的制造类型
public List<WorkOrder> generateWorkOrdersFromSaleOrder(SalOrder salOrder) {
List<WorkOrder> workOrders = new ArrayList<>();
for (SalOrderEntry entry : salOrder.getEntries()) {
// 查询物料信息
Material material = materialMapper.selectById(entry.getMaterialId());
if ("DISCRETE".equals(material.getManufactureType())) {
// 离散制造业:按工序路线生成多个工单
List<WorkOrder> processWorkOrders = generateDiscreteWorkOrders(entry, material);
workOrders.addAll(processWorkOrders);
} else if ("CONTINUOUS".equals(material.getManufactureType())) {
// 连续制造业:生成单个工单
WorkOrder workOrder = generateContinuousWorkOrder(entry, material);
workOrders.add(workOrder);
}
}
return workOrders;
}
```
---
## 9. 班次管理说明
### 9.1 班次名称配置
**问题**:连续制造业按班次报工时,需要选择班次名称(如:早班、中班、晚班)。
**解决方案**:使用系统现有的**数据字典功能**管理班次选项。
### 9.2 实现方式
#### 数据库配置
已在SQL脚本中添加班次字典配置
```sql
-- 添加班次类型字典
INSERT INTO `sys_dict_type` (`dict_name`, `dict_type`, `status`, `create_by`, `create_time`, `remark`)
VALUES ('班次类型', 'sys_shift_type', '0', 'admin', NOW(), '用于连续制造业按班次报工');
-- 添加默认班次选项
INSERT INTO `sys_dict_data` (`dict_sort`, `dict_label`, `dict_value`, `dict_type`, `css_class`, `list_class`, `is_default`, `status`, `create_by`, `create_time`, `remark`) VALUES
(1, '早班', 'MORNING', 'sys_shift_type', '', 'default', 'Y', '0', 'admin', NOW(), '08:00-16:00'),
(2, '中班', 'AFTERNOON', 'sys_shift_type', '', 'default', 'N', '0', 'admin', NOW(), '16:00-00:00'),
(3, '晚班', 'NIGHT', 'sys_shift_type', '', 'default', 'N', '0', 'admin', NOW(), '00:00-08:00');
```
#### 前端使用方式
报工页面使用字典组件:
```vue
<template>
<!-- 班次选择仅SHIFT模式显示 -->
<el-form-item v-if="reportMode === 'SHIFT'" label="班次" prop="shiftName">
<el-select v-model="form.shiftName" placeholder="请选择班次">
<el-option
v-for="dict in dict.type.sys_shift_type"
:key="dict.value"
:label="dict.label"
:value="dict.label"
/>
</el-select>
</el-form-item>
</template>
<script>
export default {
// 声明使用的字典类型
dicts: ['sys_shift_type'],
data() {
return {
form: {
shiftName: ''
}
}
}
}
</script>
```
### 9.3 配置管理
企业可以在**系统管理 → 字典管理**中灵活配置:
1. **修改班次名称**:将"早班"改为"白班"、"A班"等
2. **调整班次数量**:支持两班制、三班制、四班制
3. **设置默认班次**`is_default='Y'`
4. **记录时间段**:在`remark`字段记录班次时间(仅供参考)
**示例配置**
- 两班制白班08:00-20:00、夜班20:00-08:00
- 三班制:早班、中班、晚班
- 四班制A班、B班、C班、D班
### 9.4 优点
1.**无需新建表**:利用现有字典功能
2.**灵活配置**:可在系统管理中修改,无需改代码
3.**实施简单**只需执行SQL脚本
4.**满足需求**:支持各种班次制度
## 10. 废弃内容说明
以下内容在简化方案中**不实施**
❌ 工单分录表的复杂改动(连续制造业不使用工单分录)
❌ 设备管理表(如果系统已有设备模块则复用,否则暂不实施)
❌ 停机记录表(停机信息直接记录在报工单中)
❌ 复杂的批次号规则(第一版只支持订单号作为批次号)
❌ 质量状态字段(第一版只记录合格/不合格数量)
这些功能可在后续版本中根据实际需求逐步添加。
---
**文档版本**: v3.0(完整版)
**创建日期**: 2025-11-11
**最后更新**: 2025-11-11
**状态**: 待实施
**预计工期**: 13个工作日数据库1天 + 后端5天 + 前端5天 + 测试2天
---
## 附录:影响范围总结
### 数据库改动
-`md_material`2个字段
-`pro_workorder`9个字段
-`pro_report`9个字段
- ✅ 索引8个
- ✅ 数据字典1个字典类型 + 3个字典数据班次配置
### 后端文件改动
- ✅ 实体类3个Material, ProWorkorder, ProReport
- ✅ Mapper XML4个MaterialMapper, ProWorkorderMapper, ProReportMapper, WorkOrderExecutionMapper
- ✅ Service5个AutoCompleteService, TimedCompleteService, ReportService, WorkOrderService, WorkOrderExecutionService
- ✅ Controller2个ReportController, WorkOrderExecutionController
### 前端页面改动
- ✅ 物料管理页面新增2个字段 + 联动逻辑
- ✅ 报工页面新建或改造支持5种报工模式使用字典组件获取班次选项
- ✅ 工序执行情况页面:一键完成 + 定时完成 + 进度条显示
- ✅ 工单管理页面:新增列 + 详情页改造
- ✅ 工单执行情况统计页面:制造类型显示 + 进度条差异 + 展开内容差异
### 关键功能点
1. ✅ 制造类型判断(离散/连续)
2. ✅ 报工模式支持5种
3. ✅ 报工控件差异化
4. ✅ 一键完成功能改造
5. ✅ 定时完成功能改造
6. ✅ 工序进度条改造
7. ✅ 报工序号自动生成
8. ✅ 累计数量统计
9. ✅ 工单完成判断
10. ✅ 统计报表适配(工单执行情况)
---
## 附录B实施过程中的Bug修复记录
### Bug #1工单查询未返回 `manufactureType` 字段
**日期**2025-11-12
**问题描述**
- 前端工单列表页面点击"报工"按钮时,`workOrder.manufactureType``undefined`
- 导致连续制造业工单无法正确识别,仍然提示"该工单没有工序分录,无法报工!"
**根本原因**
`WorkOrderMapper.xml` 中的查询SQL和resultMap缺少 `manufacture_type` 等连续制造业相关字段的映射
**修复方案**
1.`WorkOrderMapper.xml``resultMap` 中添加字段映射:
```xml
<result property="manufactureType" column="manufacture_type"/>
<result property="reportMode" column="report_mode"/>
<result property="totalReportCount" column="total_report_count"/>
<result property="totalReportedQuantity" column="total_reported_quantity"/>
<result property="equipmentId" column="equipment_id"/>
<result property="equipmentName" column="equipment_name"/>
<result property="actualStartTime" column="actual_start_time"/>
<result property="actualEndTime" column="actual_end_time"/>
<result property="downtimeMinutes" column="downtime_minutes"/>
```
2.`selectWorkOrderVo` SQL中添加查询字段
```xml
a.manufacture_type,
a.report_mode,
a.total_report_count,
a.total_reported_quantity,
a.equipment_id,
a.equipment_name,
a.actual_start_time,
a.actual_end_time,
a.downtime_minutes,
```
**影响范围**
- ✅ 不影响现有功能
- ✅ 只是补充缺失的字段映射
---
### Bug #2报工记录查询返回所有工单的数据804条
**日期**2025-11-12
**问题描述**
- 连续制造业工单点击"报工"后,页面卡死
- 控制台显示查询到804条报工记录实际应该是0条或该工单的报工记录
- 原因是查询参数 `workOrderId` 没有传递到后端SQL
**根本原因**
1. **前端问题**`form.vue` 中使用动态导入API失败且使用路由参数ID而不是工单真实ID
2. **后端问题**`ReportMapper.xml` 中缺少 `workOrderId` 的查询条件
**修复方案**
**前端修复** (`mes-ui/src/views/mes/production/report/form.vue`)
1. 添加API导入
```javascript
import {getEntryRealSort, getWorkOrderByEntryId, getWorkOrder} from '@/api/mes/production/workOrder'
```
2. 修复API调用和参数传递
```javascript
// 正确调用API
const apiCall = isContinuous ? getWorkOrder(id) : getWorkOrderByEntryId(id);
// 使用响应数据中的真实工单ID
if (isContinuous) {
this.form.workOrderId = response.data.id; // 使用真实ID
this.form.workOrderEntryId = null;
}
// 查询报工记录时使用真实ID
const queryParams = isContinuous ?
{ pageSize:99999, workOrderId: response.data.id } :
{ pageSize:99999, workOrderEntryId:id };
```
**后端修复** (`ReportMapper.xml`)
`selectReportList``selectReportListCount` 两个查询中都添加:
```xml
<if test="workOrderId != null and workOrderId != ''"> and prp.work_order_id =#{workOrderId}</if>
```
**影响范围**
- ✅ 不影响离散制造业(仍使用 `workOrderEntryId` 查询)
- ✅ 修复连续制造业报工查询性能问题
---
### Bug #3报工保存失败 - `work_order_entry_id` 字段无默认值
**日期**2025-11-12
**问题描述**
- 连续制造业报工点击"保存"按钮时报错
- 错误信息:`Field 'work_order_entry_id' doesn't have a default value`
- 原因是数据库表 `pro_report` 中的 `work_order_entry_id` 字段不允许为空
**根本原因**
数据库表设计时,`work_order_entry_id` 字段被设置为 `NOT NULL`,但连续制造业报工不使用工序分录,此字段应该为 `NULL`
**修复方案**
执行SQL修改表结构
```sql
-- 修改 pro_report 表,让 work_order_entry_id 可以为空
ALTER TABLE pro_report
MODIFY COLUMN work_order_entry_id BIGINT(20) NULL COMMENT '工单子表ID离散制造业使用';
-- 为 work_order_entry_id 添加索引(如果不存在)
CREATE INDEX IF NOT EXISTS idx_work_order_entry_id ON pro_report(work_order_entry_id);
```
**影响分析**
-**离散制造业**:不受影响,仍然传递 `work_order_entry_id`,值不为空
-**连续制造业**:可以将 `work_order_entry_id` 设置为 `NULL`
-**现有报工记录**:不受影响,已有数据保持不变
-**查询功能**:不受影响,`WHERE work_order_entry_id = ?` 仍然有效
-**关联查询**:不受影响,`LEFT JOIN` 仍然正常工作
**验证SQL**
```sql
-- 验证修改是否成功
SHOW CREATE TABLE pro_report;
-- 验证现有数据不受影响
SELECT
COUNT(*) as total_reports,
SUM(CASE WHEN work_order_id IS NOT NULL THEN 1 ELSE 0 END) as continuous_report_count,
SUM(CASE WHEN work_order_entry_id IS NOT NULL THEN 1 ELSE 0 END) as discrete_report_count
FROM pro_report;
```
---
### Bug修复总结
| Bug编号 | 问题 | 影响 | 修复文件 | 状态 |
|--------|------|------|---------|------|
| #1 | 工单查询缺少字段映射 | 前端无法识别制造类型 | `WorkOrderMapper.xml` | ✅ 已修复 |
| #2 | 报工记录查询无过滤条件 | 查询所有报工记录,页面卡死 | `form.vue`, `ReportMapper.xml` | ✅ 已修复 |
| #3 | 字段不允许为空 | 连续制造业报工无法保存 | 数据库表结构 | ✅ 已修复 |
**重要提示**
- 所有修复都向后兼容,不影响现有离散制造业功能
- 修复后需要重启后端服务和刷新前端页面
- 建议在测试环境验证后再部署到生产环境
---
## 功能增强总结
| 功能编号 | 功能描述 | 实现方案 | 影响文件 | 状态 |
|----------|----------|----------|----------|------|
| Feature #1 | 工单列表数量显示优化 | 显示格式改为"已报工数量/总数量",类似销售订单 | `jinzhong.js` | ✅ 已完成 |
| Feature #2 | 列表页面空值显示优化 | 统一将空值显示为"-" | `jinzhong.js`, `index.vue` | ✅ 已完成 |
| Feature #3 | 连续制造业销售订单状态自动更新 | 连续制造业工单状态变更时自动同步销售订单状态:开始排产→生产中,报工完成→已完成 | `ReportServiceImpl.java`, `WorkOrderServiceImpl.java` | ✅ 已完成 |