# 工序执行情况表重做 - 完整实施方案 > **文档版本:** v3.0(销售订单版) > **创建日期:** 2025-10-30 > **适用场景:** 销售订单 → 工单 → 工序 → 报工单 四级展示 --- ## 目录 1. [需求说明](#一需求说明) 2. [数据库设计](#二数据库设计) 3. [后端实现](#三后端实现) 4. [前端实现](#四前端实现) 5. [性能优化](#五性能优化) 6. [实施步骤](#六实施步骤) --- ## 一、需求说明 ### 1.1 核心需求 - ✅ 以**销售订单为主数据**展示 - ✅ 订单头部显示**整体工序进度条**(汇总所有工单的工序进度) - ✅ 订单编号左侧显示**小三角展开按钮**(▶/▼),点击头部即可展开/收起 - ✅ 展开后显示订单关联的所有**工单表格** - ✅ 每个工单显示**工序进度条**(列出所有工序,用颜色区分状态) - ✅ 工单显示**完成率进度条**(可视化显示百分比) - ✅ 工单后有**报工单按钮**,点击查看该工单的所有报工单 - ✅ 未报工的显示**"未完成"**状态 ### 1.2 页面布局 ``` 销售订单卡片 ├── 【▶】订单信息(订单号、客户、产品、数量、日期、工单数、完成率)← 点击展开 ├── 整体工序进度条:[吹膜 80%✓] → [涂胶 40%▶] → [分切 0%⏱] → [检验 0%⏱] │ └── (汇总该订单下所有工单的平均工序完成率) └── 【▼】展开后显示工单表格: ├─────┬────────┬──────┬────────────────────────────────┬─────────┬────────┬────────┐ │ 状态 │ 工单号 │ 批次 │ 工序进度 │ 当前工序 │ 完成率 │ 操作 │ ├─────┼────────┼──────┼────────────────────────────────┼─────────┼────────┼────────┤ │生产中│ WO001 │ B001 │ [吹膜✓] → [涂胶▶] → [分切⏱] │ 涂胶 │ ████ 60%│【报工单】│ │待排产│ WO002 │ B002 │ [吹膜⏱] → [涂胶⏱] → [分切⏱] │ 未开始 │ ░░░░ 0% │【报工单】│ └─────┴────────┴──────┴────────────────────────────────┴─────────┴────────┴────────┘ ``` ### 1.2.1 UI交互说明 - **小三角展开按钮**:位于订单编号左侧,▶表示收起,▼表示展开 - **整体工序进度条**: - 📊 显示在订单头部下方(红色圈出区域) - 🎨 用颜色区分状态: - ✅ **绿色(100%)**:工序已完成 - 🔵 **蓝色(1-99%)**:工序进行中 - ⚪ **灰色(0%)**:工序未开始 - 📈 显示每个工序的平均完成率 - 🔄 自动计算:取该订单下所有工单的同名工序完成率的平均值 - **工单表格**: - 点击订单头部展开,显示为表格形式 - 每行一个工单,包含状态、编号、批次、工序进度、当前工序、完成率、操作 - 工序进度条采用 `el-steps` 组件,横向展示 - 完成率使用 `el-progress` 进度条可视化展示 ### 1.2 性能要求 - ✅ 工单列表查询(20条):**< 100ms** - ✅ 工序列表查询(8个工序):**< 50ms** - ✅ 报工单查询(20条):**< 50ms** - ✅ 支持10万级数据不降速 ### 1.3 技术方案 - **数据库优化**:新增缓存字段 + 完善索引 - **查询策略**:零子查询,纯字段查询 - **加载策略**:懒加载,按需获取数据 - **更新机制**:触发器实时更新 + 定时任务兜底 --- ## 二、数据库设计 ### 2.1 表结构关系 ``` sal_order (销售订单主表) ← 新的主数据! ├── id 主键 ├── number 订单号 ├── customer_name 客户名称 ├── sale_date 销售日期 └── status 状态 sal_order_entry (销售订单子表) ├── id 主键 ├── main_id 订单ID(外键) ├── material_name 产品名称 ├── quantity 数量 └── unit_name 单位 pro_workorder (生产工单) ├── id 主键 ├── number 工单编号 ├── source_info JSON(包含saleOrderId) ├── material_name 产品名称 ├── current_process_name 【新增】当前工序名 ├── current_process_sort 【新增】当前工序序号 ├── total_process_count 【新增】总工序数 ├── overall_completion_rate 【新增】整体完成率 └── last_report_time 【新增】最后报工时间 pro_workorder_entry (工序) ├── id 主键 ├── workorder_id 工单ID(外键) ├── process_name 工序名称 ├── process_sort 工序序号 ├── reported_quantity 【新增】已报工数量 └── completion_rate 【新增】完成率 pro_report (报工单) ├── id 主键 ├── work_order_entry_id 工序ID(外键) ├── report_user_name 报工人 └── report_quantity 报工数量 ``` ### 2.2 关联关系 ```sql -- 销售订单 → 工单:通过source_info JSON字段 pro_workorder.source_info 包含: { "saleOrderId": 123, "saleOrderEntryId": 456 } -- 工单 → 工序 pro_workorder_entry.workorder_id = pro_workorder.id -- 工序 → 报工单 pro_report.work_order_entry_id = pro_workorder_entry.id ``` ### 2.2 新增字段说明 **pro_workorder 表新增字段:** ```sql current_process_name -- 当前执行工序(避免子查询) current_process_sort -- 当前工序序号(用于进度条) total_process_count -- 总工序数(避免COUNT) completed_process_count -- 已完成工序数(统计用) overall_completion_rate -- 整体完成率(避免AVG) last_report_time -- 最后报工时间(排序用) ``` **pro_workorder_entry 表新增字段:** ```sql reported_quantity -- 已报工数量(避免SUM) completion_rate -- 完成率(避免除法计算) last_report_time -- 最后报工时间(排序用) ``` ### 2.3 索引设计 ```sql -- 工单主表索引 CREATE INDEX idx_status_pro_status_create ON pro_workorder (status, pro_status, create_time); CREATE INDEX idx_material_name ON pro_workorder (material_name(50)); CREATE INDEX idx_batch_number ON pro_workorder (batch_number(50)); -- 工序子表索引 CREATE INDEX idx_workorder_type ON pro_workorder_entry (workorder_id, type); CREATE INDEX idx_workorder_sort ON pro_workorder_entry (workorder_id, process_sort); -- 报工单表索引 CREATE INDEX idx_entry_status_time ON pro_report (work_order_entry_id, status, report_time); ``` **SQL实现:** 详见 `2025-10-30_工序执行情况表重做.sql` --- ## 三、后端实现 ### 3.1 项目结构 ``` cn.sourceplan.statement ├── controller │ └── WorkOrderExecutionController.java 控制器 ├── domain │ ├── query │ │ └── WorkOrderExecutionQuery.java 查询参数 │ └── vo │ ├── WorkOrderExecutionVO.java 工单VO │ ├── ProcessExecutionVO.java 工序VO │ └── ReportVO.java 报工单VO ├── mapper │ ├── WorkOrderExecutionMapper.java Mapper接口 │ └── WorkOrderExecutionMapper.xml SQL映射 └── service ├── IWorkOrderExecutionService.java Service接口 └── impl └── WorkOrderExecutionServiceImpl.java Service实现 ``` ### 3.2 核心接口 #### 3.2.1 查询销售订单列表(主接口) ``` GET /reports/production/saleOrderExecution 参数: customerName, materialName, saleDate, pageNum, pageSize 返回: 分页销售订单列表(不含工单) ``` #### 3.2.2 查询订单的工单列表(含工序) ``` GET /reports/production/saleOrderWorkOrders/{saleOrderId} 参数: saleOrderId 返回: 该订单的所有工单(含工序进度) ``` #### 3.2.3 查询工单的报工单列表 ``` GET /reports/production/workOrderReports/{workOrderId} 参数: workOrderId 返回: 该工单所有工序的报工单 ``` ### 3.3 代码实现 #### 3.3.1 Query对象 ```java package cn.sourceplan.statement.domain.query; import lombok.Data; import java.util.Date; /** * 工单执行情况查询参数 * * @author ruoyi * @date 2025-10-30 */ @Data public class WorkOrderExecutionQuery { /** 产品名称(模糊查询) */ private String materialName; /** 批次号(模糊查询) */ private String batchNumber; /** 生产状态(A-待排产 B-生产中 D-已完成) */ private String proStatus; /** 开始日期 */ private Date dateFrom; /** 结束日期 */ private Date dateTo; /** 分页参数-当前页 */ private Integer pageNum; /** 分页参数-每页大小 */ private Integer pageSize; } ``` #### 3.3.2 VO对象 ```java package cn.sourceplan.statement.domain.vo; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import java.math.BigDecimal; import java.util.Date; /** * 工单执行情况VO * * @author ruoyi * @date 2025-10-30 */ @Data public class WorkOrderExecutionVO { /** 工单ID */ private Long id; /** 工单编号 */ private String number; /** 产品名称 */ private String materialName; /** 规格型号 */ private String specification; /** 生产数量 */ private BigDecimal quantity; /** 批次号 */ private String batchNumber; /** 生产状态 */ private String proStatus; /** 生产状态文本 */ private String proStatusText; /** 开始生产日期 */ @JsonFormat(pattern = "yyyy-MM-dd") private Date beginProDate; /** 计划完成日期 */ @JsonFormat(pattern = "yyyy-MM-dd") private Date planFinishDate; /** 创建时间 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date createTime; // ========== 统计字段 ========== /** 当前工序名称 */ private String currentProcess; /** 当前工序序号 */ private Integer currentProcessSort; /** 总工序数 */ private Integer totalProcessCount; /** 已完成工序数 */ private Integer completedProcessCount; /** 整体完成率 */ private String completionRate; /** 最后报工时间 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date lastReportTime; } ``` ```java package cn.sourceplan.statement.domain.vo; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import java.math.BigDecimal; import java.util.Date; /** * 工序执行情况VO * * @author ruoyi * @date 2025-10-30 */ @Data public class ProcessExecutionVO { /** 工序ID */ private Long id; /** 工序名称 */ private String processName; /** 工序序号 */ private Integer processSort; /** 计划数量 */ private BigDecimal planQuantity; /** 已报工数量 */ private BigDecimal reportedQuantity; /** 完成率 */ private BigDecimal completionRate; /** 状态(已完成/进行中/未开始) */ private String status; /** 最后报工时间 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date lastReportTime; /** 报工单数量 */ private Integer reportCount; } ``` ```java package cn.sourceplan.statement.domain.vo; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.Data; import java.math.BigDecimal; import java.util.Date; /** * 报工单VO * * @author ruoyi * @date 2025-10-30 */ @Data public class ReportVO { /** 报工单ID */ private Long id; /** 报工单号 */ private String number; /** 报工人 */ private String reportUserName; /** 报工时间 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date reportTime; /** 报工数量 */ private BigDecimal reportQuantity; /** 合格数量 */ private BigDecimal qualifiedQuantity; /** 不合格数量 */ private BigDecimal unqualifiedQuantity; /** 车间名称 */ private String workshopName; /** 工位名称 */ private String stationName; /** 状态 */ private String status; /** 状态文本 */ private String statusText; /** 备注 */ private String remark; /** 创建时间 */ @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") private Date createTime; } ``` #### 3.3.3 Mapper接口 ```java package cn.sourceplan.statement.mapper; import cn.sourceplan.statement.domain.query.WorkOrderExecutionQuery; import cn.sourceplan.statement.domain.vo.ProcessExecutionVO; import cn.sourceplan.statement.domain.vo.ReportVO; import cn.sourceplan.statement.domain.vo.WorkOrderExecutionVO; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import java.util.List; /** * 工单执行情况 Mapper接口 * * @author ruoyi * @date 2025-10-30 */ @Mapper public interface WorkOrderExecutionMapper { /** * 查询工单执行情况列表 * * @param query 查询参数 * @return 工单执行情况列表 */ List selectWorkOrderExecutionList(WorkOrderExecutionQuery query); /** * 根据工单ID查询工序列表 * * @param workOrderId 工单ID * @return 工序列表 */ List selectProcessListByWorkOrderId(@Param("workOrderId") Long workOrderId); /** * 根据工序ID查询报工单列表 * * @param workOrderEntryId 工序ID * @return 报工单列表 */ List selectReportListByProcessId(@Param("workOrderEntryId") Long workOrderEntryId); } ``` #### 3.3.4 Mapper XML ```xml ``` #### 3.3.5 Service接口 ```java package cn.sourceplan.statement.service; import cn.sourceplan.statement.domain.query.WorkOrderExecutionQuery; import cn.sourceplan.statement.domain.vo.ProcessExecutionVO; import cn.sourceplan.statement.domain.vo.ReportVO; import cn.sourceplan.statement.domain.vo.WorkOrderExecutionVO; import java.util.List; /** * 工单执行情况 Service接口 * * @author ruoyi * @date 2025-10-30 */ public interface IWorkOrderExecutionService { /** * 查询工单执行情况列表 * * @param query 查询参数 * @return 工单执行情况列表 */ List selectWorkOrderExecutionList(WorkOrderExecutionQuery query); /** * 根据工单ID查询工序列表 * * @param workOrderId 工单ID * @return 工序列表 */ List selectProcessListByWorkOrderId(Long workOrderId); /** * 根据工序ID查询报工单列表 * * @param workOrderEntryId 工序ID * @return 报工单列表 */ List selectReportListByProcessId(Long workOrderEntryId); } ``` #### 3.3.6 Service实现 ```java package cn.sourceplan.statement.service.impl; import cn.sourceplan.statement.domain.query.WorkOrderExecutionQuery; import cn.sourceplan.statement.domain.vo.ProcessExecutionVO; import cn.sourceplan.statement.domain.vo.ReportVO; import cn.sourceplan.statement.domain.vo.WorkOrderExecutionVO; import cn.sourceplan.statement.mapper.WorkOrderExecutionMapper; import cn.sourceplan.statement.service.IWorkOrderExecutionService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; /** * 工单执行情况 Service实现 * * @author ruoyi * @date 2025-10-30 */ @Service public class WorkOrderExecutionServiceImpl implements IWorkOrderExecutionService { @Autowired private WorkOrderExecutionMapper workOrderExecutionMapper; /** * 查询工单执行情况列表 * * @param query 查询参数 * @return 工单执行情况列表 */ @Override public List selectWorkOrderExecutionList(WorkOrderExecutionQuery query) { return workOrderExecutionMapper.selectWorkOrderExecutionList(query); } /** * 根据工单ID查询工序列表 * * @param workOrderId 工单ID * @return 工序列表 */ @Override public List selectProcessListByWorkOrderId(Long workOrderId) { return workOrderExecutionMapper.selectProcessListByWorkOrderId(workOrderId); } /** * 根据工序ID查询报工单列表 * * @param workOrderEntryId 工序ID * @return 报工单列表 */ @Override public List selectReportListByProcessId(Long workOrderEntryId) { return workOrderExecutionMapper.selectReportListByProcessId(workOrderEntryId); } } ``` #### 3.3.7 Controller ```java package cn.sourceplan.statement.controller; import cn.sourceplan.common.annotation.Log; import cn.sourceplan.common.core.controller.BaseController; import cn.sourceplan.common.core.domain.AjaxResult; import cn.sourceplan.common.core.page.TableDataInfo; import cn.sourceplan.common.enums.BusinessType; import cn.sourceplan.statement.domain.query.WorkOrderExecutionQuery; import cn.sourceplan.statement.domain.vo.ProcessExecutionVO; import cn.sourceplan.statement.domain.vo.ReportVO; import cn.sourceplan.statement.domain.vo.WorkOrderExecutionVO; import cn.sourceplan.statement.service.IWorkOrderExecutionService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; /** * 工单执行情况 Controller * * @author ruoyi * @date 2025-10-30 */ @RestController @RequestMapping("/reports/production") public class WorkOrderExecutionController extends BaseController { @Autowired private IWorkOrderExecutionService workOrderExecutionService; /** * 查询工单执行情况列表 */ @Log(title = "工单执行情况", businessType = BusinessType.QUERY) @GetMapping("/workOrderExecution") public TableDataInfo list(WorkOrderExecutionQuery query) { startPage(); List list = workOrderExecutionService.selectWorkOrderExecutionList(query); return getDataTable(list); } /** * 根据工单ID查询工序列表 */ @Log(title = "工序执行情况", businessType = BusinessType.QUERY) @GetMapping("/workOrderProcesses/{workOrderId}") public AjaxResult getProcesses(@PathVariable("workOrderId") Long workOrderId) { List list = workOrderExecutionService.selectProcessListByWorkOrderId(workOrderId); return AjaxResult.success(list); } /** * 根据工序ID查询报工单列表 */ @Log(title = "工序报工单", businessType = BusinessType.QUERY) @GetMapping("/processReports/{workOrderEntryId}") public AjaxResult getReports(@PathVariable("workOrderEntryId") Long workOrderEntryId) { List list = workOrderExecutionService.selectReportListByProcessId(workOrderEntryId); return AjaxResult.success(list); } } ``` --- ## 四、前端实现 ### 4.1 API接口 创建文件:`mes-ui/src/api/mes/statement/workOrderExecution.js` ```javascript import request from '@/utils/request' /** * 查询工单执行情况列表 */ export function listWorkOrderExecution(query) { return request({ url: '/reports/production/workOrderExecution', method: 'get', params: query }) } /** * 根据工单ID查询工序列表 */ export function listProcessesByWorkOrder(workOrderId) { return request({ url: `/reports/production/workOrderProcesses/${workOrderId}`, method: 'get' }) } /** * 根据工序ID查询报工单列表 */ export function listReportsByProcess(workOrderEntryId) { return request({ url: `/reports/production/processReports/${workOrderEntryId}`, method: 'get' }) } ``` ### 4.2 核心功能说明 #### 4.2.1 整体工序进度条 在订单头部显示所有工单的工序进度汇总,实现逻辑: 1. **数据加载时机** - 页面初始加载时,自动为前5个订单加载工序数据(优化首屏体验) - 分页切换时,自动为新页面的前5个订单加载工序数据 - 用户展开订单时,如果未加载则加载工序数据 2. **工序进度计算** ```javascript // 收集所有工单的工序,按工序名称分组 calculateOverallProcess(order) { const processMap = new Map() order.workOrderList.forEach(workOrder => { workOrder.processList.forEach(process => { if (!processMap.has(process.processName)) { processMap.set(process.processName, { processName: process.processName, sort: process.sort, completionRates: [] }) } processMap.get(process.processName).completionRates.push( parseFloat(process.completionRate) || 0 ) }) }) // 计算每个工序的平均完成率 order.overallProcessList = Array.from(processMap.values()) .sort((a, b) => a.sort - b.sort) .map(item => ({ processName: item.processName, sort: item.sort, avgCompletionRate: Math.round( item.completionRates.reduce((sum, rate) => sum + rate, 0) / item.completionRates.length ) })) } ``` 3. **进度条状态** - ✅ **绿色 success**:`avgCompletionRate >= 100` - 🔵 **蓝色 process**:`0 < avgCompletionRate < 100` - ⚪ **灰色 wait**:`avgCompletionRate == 0` 4. **样式设计** ```css .overall-process-bar { margin-top: 15px; padding: 15px 20px; background: linear-gradient(135deg, #f5f7fa 0%, #ffffff 100%); border-radius: 8px; border: 1px solid #EBEEF5; } ``` #### 4.2.2 展开/收起交互 - 小三角图标:`el-icon-caret-right`(收起)/ `el-icon-caret-bottom`(展开) - 点击整个订单头部区域即可展开/收起 - 鼠标悬停时背景色变化,提供视觉反馈 #### 4.2.3 工单表格展示 - 使用 `el-table` 组件展示工单列表 - 工序进度列:使用 `el-steps` 横向步骤条 - 完成率列:使用 `el-progress` 进度条,颜色动态变化 ### 4.3 页面组件 创建文件:`mes-ui/src/views/mes/statement/saleOrderExecution/index.vue` ```vue ``` --- ## 五、性能优化 ### 5.1 数据库优化 - ✅ 新增缓存字段(工单主表6个字段,工序子表3个字段) - ✅ 创建完善索引(主表3个,子表2个,报工单2个) - ✅ 零子查询,纯字段查询 - ✅ 触发器实时更新统计字段 ### 5.2 接口优化 - ✅ 懒加载:展开时才加载工序 - ✅ 按需加载:点击才加载报工单 - ✅ 分页查询:每次最多20条 - ✅ 前端缓存:已加载的数据不重复请求 ### 5.3 性能指标 | 查询类型 | 响应时间 | 备注 | |---------|---------|------| | 工单列表(20条) | < 100ms | 零子查询 | | 工序列表(8个) | < 50ms | 直接字段 | | 报工单(20条) | < 50ms | 简单查询 | | 总响应时间 | < 200ms | 用户无感知 | --- ## 六、实施步骤 ### 第一步:数据库修改 ✓ 1. 执行SQL脚本新增字段 2. 创建索引 3. 初始化缓存数据 4. 创建触发器 ### 第二步:后端开发 1. 创建Query、VO类(3个文件) 2. 创建Mapper接口和XML 3. 创建Service接口和实现 4. 创建Controller 5. 测试接口 ### 第三步:前端开发 1. 创建API文件 2. 创建页面组件 3. 配置路由和菜单 4. 测试功能 ### 第四步:验证测试 1. 功能测试:所有接口正常 2. 性能测试:响应时间达标 3. 数据准确性:统计正确 4. 压力测试:并发稳定 --- ## 七、注意事项 1. **数据一致性**:触发器保证实时更新,定时任务兜底 2. **性能保证**:所有查询走索引,无全表扫描 3. **错误处理**:接口异常返回友好提示 4. **权限控制**:根据角色控制数据范围 5. **日志记录**:关键操作记录日志 --- ## 八、维护说明 ### 8.1 定时任务 每天凌晨执行一次全量更新,修正可能的数据不一致 ### 8.2 数据验证 提供验证SQL,定期检查缓存字段准确性 ### 8.3 性能监控 监控查询响应时间,发现问题及时优化 --- ## 九、快速部署清单 ### 9.1 已完成项 ✅ #### 数据库层 - ✅ 执行 `.tasks/2025-10-30_工序执行情况表重做.sql` - 新增字段(6个主表字段 + 3个子表字段) - 创建索引(7个索引) - 初始化数据 - 创建触发器和存储过程 #### 后端代码 ✅ - ✅ `yjh-mes/src/main/java/cn/sourceplan/statement/domain/query/WorkOrderExecutionQuery.java` - ✅ `yjh-mes/src/main/java/cn/sourceplan/statement/domain/vo/WorkOrderExecutionVO.java` - ✅ `yjh-mes/src/main/java/cn/sourceplan/statement/domain/vo/ProcessExecutionVO.java` - ✅ `yjh-mes/src/main/java/cn/sourceplan/statement/domain/vo/ReportVO.java` - ✅ `yjh-mes/src/main/java/cn/sourceplan/statement/mapper/WorkOrderExecutionMapper.java` - ✅ `yjh-mes/src/main/resources/mapper/statement/WorkOrderExecutionMapper.xml` - ✅ `yjh-mes/src/main/java/cn/sourceplan/statement/service/IWorkOrderExecutionService.java` - ✅ `yjh-mes/src/main/java/cn/sourceplan/statement/service/impl/WorkOrderExecutionServiceImpl.java` - ✅ `yjh-mes/src/main/java/cn/sourceplan/statement/controller/WorkOrderExecutionController.java` #### 前端代码 ✅ - ✅ `mes-ui/src/api/mes/statement/workOrderExecution.js` - ✅ `mes-ui/src/views/mes/statement/workOrderExecution/index.vue` --- ### 9.2 待配置项 🔧 #### 1. 配置前端路由 编辑文件:`mes-ui/src/router/index.js` 或对应的路由文件 ```javascript { path: '/statement/workOrderExecution', component: () => import('@/views/mes/statement/workOrderExecution/index'), name: 'WorkOrderExecution', meta: { title: '工序执行情况表', icon: 'chart', noCache: true } } ``` #### 2. 配置菜单权限 在系统管理 → 菜单管理中添加菜单: ``` 菜单名称:工序执行情况表 菜单路径:/statement/workOrderExecution 组件路径:mes/statement/workOrderExecution/index 权限标识:statement:workOrderExecution:list 菜单图标:chart ``` 或直接执行SQL: ```sql -- 在sys_menu表中插入菜单(根据实际parent_id调整) INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, menu_type, visible, status, perms, icon, create_by, create_time, remark) VALUES ('工序执行情况表', 2000, 10, 'workOrderExecution', 'mes/statement/workOrderExecution/index', 1, 'C', '0', '0', 'statement:workOrderExecution:list', 'chart', 'admin', NOW(), '工序执行情况表'); ``` --- ### 9.3 测试步骤 🧪 #### 第1步:启动后端 ```bash cd mes-admin mvn clean package java -jar target/ruoyi-admin.jar ``` #### 第2步:测试接口 使用Postman或浏览器测试: **1) 查询工单列表** ``` GET http://localhost:8080/reports/production/workOrderExecution?pageNum=1&pageSize=10 ``` **2) 查询工序列表** ``` GET http://localhost:8080/reports/production/workOrderProcesses/1 ``` **3) 查询报工单** ``` GET http://localhost:8080/reports/production/processReports/1 ``` #### 第3步:启动前端 ```bash cd mes-ui npm install npm run dev ``` #### 第4步:访问页面 ``` http://localhost:80 登录后访问:生产管理 → 工序执行情况表 ``` --- ### 9.4 验证清单 ✓ #### 功能验证 - [ ] 工单列表正常显示 - [ ] 进度条正确显示当前工序和完成率 - [ ] 点击展开/收起按钮正常 - [ ] 工序列表懒加载成功 - [ ] 工序状态(未开始/进行中/已完成)正确 - [ ] 点击报工单按钮弹窗正常 - [ ] 报工单列表显示正确 - [ ] 未报工工序显示"未完成"状态 - [ ] 搜索条件(产品名称、批次号、状态)有效 - [ ] 分页功能正常 #### 性能验证 - [ ] 工单列表查询 < 100ms - [ ] 工序列表查询 < 50ms - [ ] 报工单查询 < 50ms - [ ] 10万级数据不卡顿 #### 数据验证 ```sql -- 验证工单统计字段是否正确 SELECT w.id, w.number, w.current_process_name, w.total_process_count, w.completed_process_count, w.overall_completion_rate, (SELECT COUNT(*) FROM pro_workorder_entry e WHERE e.workorder_id = w.id AND e.type = 'report') AS actual_total_count FROM pro_workorder w WHERE w.id = 1; -- 验证工序统计字段是否正确 SELECT e.id, e.process_name, e.reported_quantity, e.completion_rate, (SELECT SUM(r.report_quantity) FROM pro_report r WHERE r.work_order_entry_id = e.id AND r.status = 'A') AS actual_reported FROM pro_workorder_entry e WHERE e.workorder_id = 1 AND e.type = 'report'; ``` --- ### 9.5 常见问题 ❓ #### Q1: 接口404错误 **A:** 检查Controller的 `@RequestMapping` 路径是否正确,确认为 `/reports/production` #### Q2: 工序列表为空 **A:** 检查 `pro_workorder_entry` 表的 `type` 字段是否为 `'report'` #### Q3: 统计字段都是0 **A:** 执行SQL中的"初始化数据"部分,运行UPDATE语句 #### Q4: 前端报CORS错误 **A:** 检查 `vue.config.js` 的proxy配置,确保指向正确的后端地址 #### Q5: 触发器不生效 **A:** 检查触发器是否创建成功: ```sql SHOW TRIGGERS LIKE 'pro_report'; ``` --- ### 9.6 上线检查清单 🚀 #### 部署前 - [ ] 备份数据库 - [ ] 在测试环境验证所有功能 - [ ] 压力测试通过 - [ ] 代码已提交到Git #### 部署时 - [ ] 停止应用服务 - [ ] 执行SQL脚本 - [ ] 部署后端代码 - [ ] 部署前端代码 - [ ] 清除缓存 #### 部署后 - [ ] 验证接口正常 - [ ] 验证页面访问正常 - [ ] 检查日志无异常 - [ ] 性能监控正常 --- **项目状态:** 🔄 **需求已更新为销售订单版,代码待重新生成** --- ## 十、完整实现代码(销售订单版) ### 10.1 SQL查询语句 #### 查询销售订单列表 ```sql SELECT so.id, so.number AS orderNumber, so.customer_name AS customerName, so.sale_date AS saleDate, so.status, soe.material_name AS materialName, soe.quantity, soe.unit_name AS unitName, -- 统计该订单的工单数 ( SELECT COUNT(*) FROM pro_workorder pw WHERE JSON_EXTRACT(pw.source_info, '$.saleOrderId') = so.id AND pw.status = 'A' ) AS workOrderCount, -- 平均完成率 ( SELECT AVG(IFNULL(pw.overall_completion_rate, 0)) FROM pro_workorder pw WHERE JSON_EXTRACT(pw.source_info, '$.saleOrderId') = so.id AND pw.status = 'A' ) AS avgCompletionRate FROM sal_order so LEFT JOIN sal_order_entry soe ON soe.main_id = so.id WHERE so.status = 'A' AND (customerName IS NULL OR so.customer_name LIKE CONCAT('%', customerName, '%')) AND (materialName IS NULL OR soe.material_name LIKE CONCAT('%', materialName, '%')) ORDER BY so.sale_date DESC, so.create_time DESC ``` #### 查询订单的工单列表(含工序) ```sql SELECT pw.id, pw.number AS workOrderNumber, pw.material_name AS materialName, pw.batch_number AS batchNumber, pw.quantity, pw.pro_status AS proStatus, pw.current_process_name AS currentProcess, pw.current_process_sort AS currentProcessSort, pw.total_process_count AS totalProcessCount, IFNULL(pw.overall_completion_rate, 0) AS completionRate FROM pro_workorder pw WHERE JSON_EXTRACT(pw.source_info, '$.saleOrderId') = #{saleOrderId} AND pw.status = 'A' ORDER BY pw.create_time ASC ``` #### 查询工序列表 ```sql SELECT pwe.id, pwe.process_name AS processName, pwe.process_sort AS processSort, pwe.report_quantity AS planQuantity, IFNULL(pwe.reported_quantity, 0) AS reportedQuantity, IFNULL(pwe.completion_rate, 0) AS completionRate, CASE WHEN IFNULL(pwe.completion_rate, 0) >= 100 THEN '已完成' WHEN IFNULL(pwe.completion_rate, 0) > 0 THEN '进行中' ELSE '未开始' END AS status FROM pro_workorder_entry pwe WHERE pwe.workorder_id = #{workOrderId} AND pwe.type = 'report' ORDER BY pwe.process_sort ASC ``` --- **文档结束**