18 小时以前 7a681887e5a8e1aab49acc886974d354c69a280e
feat(mes): 新增质检指标分组树和平行测量功能

- 在 mes_qc_indicator 表新增 parent_id、item_type、formula_text、unit_text、sort_order 字段
- 在 mes_qc_indicator_result_detail 表新增 measure_no 字段支持平行测量
- 在质检相关表添加采样地点、证书编号、执行标准、复核审核人字段
- 实现质检指标分组树形结构和排序功能
- 支持平行测量多批次数据录入和展示
- 新增检验报告打印功能,包含签字区和表头信息
- 更新流程文档和错误码配置以支持新功能
- 优化控制器返回完整的指标层级关系数据
已删除100个文件
已添加7个文件
已修改42个文件
1445 ■■■■■ 文件已修改
.image/Java监控.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/MySQL.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/OA请假-列表.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/OA请假-发起.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/OA请假-详情.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/Redis.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/admin-uniapp/01.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/admin-uniapp/02.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/admin-uniapp/03.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/admin-uniapp/04.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/admin-uniapp/05.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/admin-uniapp/06.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/admin-uniapp/07.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/admin-uniapp/08.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/admin-uniapp/09.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/b79dd6c73651769086724a3a8b7ead8a.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/common/ai-feature.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/common/ai-preview.gif 补丁 | 查看 | 原始文档 | blame | 历史
.image/common/bpm-feature.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/common/crm-feature.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/common/erp-feature.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/common/im-feature.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/common/im-preview-home.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/common/im-preview-manager.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/common/infra-feature.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/common/iot-feature.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/common/iot-preview.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/common/mall-feature.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/common/mall-preview.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/common/mes-feature.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/common/mes-preview.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/common/project-vs.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/common/ruoyi-vue-pro-architecture.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/common/ruoyi-vue-pro-biz.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/common/system-feature.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/common/wms-feature.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/common/wms-preview.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/common/yudao-cloud-architecture.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/common/yudao-roadmap.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/个人中心.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/代码生成.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/令牌管理.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/任务列表-审批.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/任务列表-已办.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/任务列表-待办.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/任务日志.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/商户信息.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/在线用户.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/大屏设计器-列表.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/大屏设计器-编辑.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/大屏设计器-预览.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/字典数据.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/字典类型.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/定时任务.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/岗位管理.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/工作流设计器-bpmn.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/工作流设计器-simple.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/应用信息-列表.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/应用信息-编辑.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/应用管理.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/我的流程-列表.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/我的流程-发起.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/我的流程-详情.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/报表设计器-图形报表.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/报表设计器-打印设计.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/报表设计器-数据报表.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/操作日志.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/支付订单.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/敏感词.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/数据库文档.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/文件管理.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/文件管理2.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/文件配置.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/日志中心.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/流程模型-列表.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/流程模型-定义.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/流程模型-设计.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/流程表单.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/生成效果.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/用户分组.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/用户管理.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/登录.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/登录日志.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/短信日志.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/短信模板.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/短信渠道.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/租户套餐.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/租户管理.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/系统接口.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/菜单管理.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/表单构建.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/角色管理.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/访问日志.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/退款订单.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/通知公告.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/部门管理.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/配置管理.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/链路追踪.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/错误日志.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/错误码管理.jpg 补丁 | 查看 | 原始文档 | blame | 历史
.image/首页.jpg 补丁 | 查看 | 原始文档 | blame | 历史
docs/project-business/data/flows.json 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/project-business/data/objects.json 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/project-business/index.html 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/project-business/mermaid/05-quality-flow.mmd 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/sql/config_export_all_20260904.sql 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/mysql/mes_qc_indicator_report_upgrade.sql 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/mysql/mes_qc_indicator_seed.sql 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/indicator/MesQcIndicatorController.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/indicator/vo/MesQcIndicatorRespVO.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/indicator/vo/MesQcIndicatorSaveReqVO.java 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/indicatorresult/MesQcIndicatorResultController.java 110 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/indicatorresult/vo/MesQcIndicatorResultRespVO.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/indicatorresult/vo/MesQcIndicatorResultSaveReqVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/indicatorresult/vo/MesQcQcReportRespVO.java 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ipqc/MesQcIpqcController.java 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ipqc/vo/MesQcIpqcRespVO.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ipqc/vo/MesQcIpqcSaveReqVO.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/MesQcIqcController.java 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/vo/MesQcIqcRespVO.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/vo/MesQcIqcSaveReqVO.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/oqc/MesQcOqcController.java 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/oqc/vo/MesQcOqcRespVO.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/oqc/vo/MesQcOqcSaveReqVO.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/rqc/MesQcRqcController.java 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/rqc/vo/MesQcRqcRespVO.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/rqc/vo/MesQcRqcSaveReqVO.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/template/vo/MesQcTemplateRespVO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/template/vo/MesQcTemplateSaveReqVO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/template/vo/indicator/MesQcTemplateIndicatorRespVO.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/template/vo/indicator/MesQcTemplateIndicatorSaveReqVO.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/indicator/MesQcIndicatorDO.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/indicatorresult/MesQcIndicatorResultDetailDO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/ipqc/MesQcIpqcDO.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/iqc/MesQcIqcDO.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/oqc/MesQcOqcDO.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/rqc/MesQcRqcDO.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/template/MesQcTemplateDO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/template/MesQcTemplateIndicatorDO.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/qc/MesQcIndicatorItemTypeEnum.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/indicator/MesQcIndicatorServiceImpl.java 76 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/indicatorresult/MesQcIndicatorResultServiceImpl.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/indicatorresult/MesQcReportService.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/indicatorresult/MesQcReportServiceImpl.java 314 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ipqc/MesQcIpqcServiceImpl.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImpl.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/oqc/MesQcOqcServiceImpl.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/rqc/MesQcRqcServiceImpl.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.image/Java¼à¿Ø.jpg
Binary files differ
.image/MySQL.jpg
Binary files differ
.image/OAÇë¼Ù-Áбí.jpg
Binary files differ
.image/OAÇë¼Ù-·¢Æð.jpg
Binary files differ
.image/OAÇë¼Ù-ÏêÇé.jpg
Binary files differ
.image/Redis.jpg
Binary files differ
.image/admin-uniapp/01.png
Binary files differ
.image/admin-uniapp/02.png
Binary files differ
.image/admin-uniapp/03.png
Binary files differ
.image/admin-uniapp/04.png
Binary files differ
.image/admin-uniapp/05.png
Binary files differ
.image/admin-uniapp/06.png
Binary files differ
.image/admin-uniapp/07.png
Binary files differ
.image/admin-uniapp/08.png
Binary files differ
.image/admin-uniapp/09.png
Binary files differ
.image/b79dd6c73651769086724a3a8b7ead8a.jpg
.image/common/ai-feature.png
Binary files differ
.image/common/ai-preview.gif
Binary files differ
.image/common/bpm-feature.png
Binary files differ
.image/common/crm-feature.png
Binary files differ
.image/common/erp-feature.png
Binary files differ
.image/common/im-feature.png
Binary files differ
.image/common/im-preview-home.png
Binary files differ
.image/common/im-preview-manager.png
Binary files differ
.image/common/infra-feature.png
Binary files differ
.image/common/iot-feature.png
Binary files differ
.image/common/iot-preview.png
Binary files differ
.image/common/mall-feature.png
Binary files differ
.image/common/mall-preview.png
Binary files differ
.image/common/mes-feature.png
Binary files differ
.image/common/mes-preview.png
Binary files differ
.image/common/project-vs.png
Binary files differ
.image/common/ruoyi-vue-pro-architecture.png
Binary files differ
.image/common/ruoyi-vue-pro-biz.png
Binary files differ
.image/common/system-feature.png
Binary files differ
.image/common/wms-feature.png
Binary files differ
.image/common/wms-preview.png
Binary files differ
.image/common/yudao-cloud-architecture.png
Binary files differ
.image/common/yudao-roadmap.png
Binary files differ
.image/¸öÈËÖÐÐÄ.jpg
Binary files differ
.image/´úÂëÉú³É.jpg
Binary files differ
.image/ÁîÅÆ¹ÜÀí.jpg
Binary files differ
.image/ÈÎÎñÁбí-ÉóÅú.jpg
Binary files differ
.image/ÈÎÎñÁбí-ÒѰì.jpg
Binary files differ
.image/ÈÎÎñÁбí-´ý°ì.jpg
Binary files differ
.image/ÈÎÎñÈÕÖ¾.jpg
Binary files differ
.image/ÉÌ»§ÐÅÏ¢.jpg
Binary files differ
.image/ÔÚÏßÓû§.jpg
Binary files differ
.image/´óÆÁÉè¼ÆÆ÷-Áбí.jpg
Binary files differ
.image/´óÆÁÉè¼ÆÆ÷-±à¼­.jpg
Binary files differ
.image/´óÆÁÉè¼ÆÆ÷-Ô¤ÀÀ.jpg
Binary files differ
.image/×ÖµäÊý¾Ý.jpg
Binary files differ
.image/×ÖµäÀàÐÍ.jpg
Binary files differ
.image/¶¨Ê±ÈÎÎñ.jpg
Binary files differ
.image/¸Úλ¹ÜÀí.jpg
Binary files differ
.image/¹¤×÷Á÷Éè¼ÆÆ÷-bpmn.jpg
Binary files differ
.image/¹¤×÷Á÷Éè¼ÆÆ÷-simple.jpg
Binary files differ
.image/Ó¦ÓÃÐÅÏ¢-Áбí.jpg
Binary files differ
.image/Ó¦ÓÃÐÅÏ¢-±à¼­.jpg
Binary files differ
.image/Ó¦ÓùÜÀí.jpg
Binary files differ
.image/ÎÒµÄÁ÷³Ì-Áбí.jpg
Binary files differ
.image/ÎÒµÄÁ÷³Ì-·¢Æð.jpg
Binary files differ
.image/ÎÒµÄÁ÷³Ì-ÏêÇé.jpg
Binary files differ
.image/±¨±íÉè¼ÆÆ÷-ͼÐᨱí.jpg
Binary files differ
.image/±¨±íÉè¼ÆÆ÷-´òÓ¡Éè¼Æ.jpg
Binary files differ
.image/±¨±íÉè¼ÆÆ÷-Êý¾Ý±¨±í.jpg
Binary files differ
.image/²Ù×÷ÈÕÖ¾.jpg
Binary files differ
.image/Ö§¸¶¶©µ¥.jpg
Binary files differ
.image/Ãô¸Ð´Ê.jpg
Binary files differ
.image/Êý¾Ý¿âÎĵµ.jpg
Binary files differ
.image/Îļþ¹ÜÀí.jpg
Binary files differ
.image/Îļþ¹ÜÀí2.jpg
Binary files differ
.image/ÎļþÅäÖÃ.jpg
Binary files differ
.image/ÈÕÖ¾ÖÐÐÄ.jpg
Binary files differ
.image/Á÷³ÌÄ£ÐÍ-Áбí.jpg
Binary files differ
.image/Á÷³ÌÄ£ÐÍ-¶¨Òå.jpg
Binary files differ
.image/Á÷³ÌÄ£ÐÍ-Éè¼Æ.jpg
Binary files differ
.image/Á÷³Ì±íµ¥.jpg
Binary files differ
.image/Éú³ÉЧ¹û.jpg
Binary files differ
.image/Óû§·Ö×é.jpg
Binary files differ
.image/Óû§¹ÜÀí.jpg
Binary files differ
.image/怬.jpg
Binary files differ
.image/µÇ¼ÈÕÖ¾.jpg
Binary files differ
.image/¶ÌÐÅÈÕÖ¾.jpg
Binary files differ
.image/¶ÌÐÅÄ£°å.jpg
Binary files differ
.image/¶ÌÐÅÇþµÀ.jpg
Binary files differ
.image/×â»§ÌײÍ.png
Binary files differ
.image/×â»§¹ÜÀí.jpg
Binary files differ
.image/ϵͳ½Ó¿Ú.jpg
Binary files differ
.image/²Ëµ¥¹ÜÀí.jpg
Binary files differ
.image/±íµ¥¹¹½¨.jpg
Binary files differ
.image/½ÇÉ«¹ÜÀí.jpg
Binary files differ
.image/·ÃÎÊÈÕÖ¾.jpg
Binary files differ
.image/Í˿µ¥.jpg
Binary files differ
.image/֪ͨ¹«¸æ.jpg
Binary files differ
.image/²¿ÃŹÜÀí.jpg
Binary files differ
.image/ÅäÖùÜÀí.jpg
Binary files differ
.image/Á´Â·×·×Ù.jpg
Binary files differ
.image/´íÎóÈÕÖ¾.jpg
Binary files differ
.image/´íÎóÂë¹ÜÀí.jpg
Binary files differ
.image/Ê×Ò³.jpg
Binary files differ
docs/project-business/data/flows.json
@@ -29,12 +29,13 @@
       {"stage": "联动", "action": "审批通过按工序标记: å€’冲→自动物料消耗扣线边库(允许负库存);质检→产出待检行等 IPQC;关键非检→直接产出入库", "roles": ["系统自动"]},
       {"stage": "完工", "action": "仅末道工序回写工单已生产数量,达标自动完成;MPS ç´¯è®¡åˆ¤æ–­å®Œæˆå¹¶å‘事件给销售订单", "roles": ["系统自动"]}]},
    {"id": "quality", "name": "质量流程", "mermaid": "mermaid/05-quality-flow.mmd", "color": "#e06c6c",
     "summary": "四道检验闸口(IQC/IPQC/OQC/RQC,二态 0→4) + åˆ¤å®šå››æžœ(合格/特采/退货/报废) + NCR è¯„审处置关闭;AI æä¾›åˆ¤å®šå»ºè®®ï¼›NCR å½“前仅台账不触发返工单。",
     "summary": "四道检验闸口(IQC/IPQC/OQC/RQC,二态 0→4) + åˆ¤å®šå››æžœ(合格/特采/退货/报废) + NCR è¯„审处置关闭;AI æä¾›åˆ¤å®šå»ºè®®ï¼›NCR å½“前仅台账不触发返工单。支持化工报告范式:分组指标树+公式文本+平行测量批次+采样表头+签字区,浏览器打印检验报告。",
     "steps": [
       {"stage": "来料", "action": "到货通知/外协入库→IQC,结论回写推进待入库或触发退货", "roles": ["IQC è´¨æ£€å‘˜"]},
       {"stage": "过程", "action": "报工质检工序产出行→IPQC,合格拆行入库、不合格生成 NCR", "roles": ["IPQC è´¨æ£€å‘˜"]},
       {"stage": "出货", "action": "销售出库提交→OQC,完成后出库单进入待拣货", "roles": ["OQC è´¨æ£€å‘˜"]},
       {"stage": "退货", "action": "退料/销售退货/售后退货→RQC,结论回调单据与售后", "roles": ["RQC è´¨æ£€å‘˜"]},
       {"stage": "报告", "action": "检验结果按分组树+测量批次录入,/indicator-result/report èšåˆåŽå‰ç«¯æ‰“印检验报告(含采样地点/证书编号/执行标准/复核·审核签字区)", "roles": ["质检员", "复核人", "审核人"]},
       {"stage": "不合格", "action": "NCR: å¾…评审→评审→处置(退货/报废/返工/让步接收/降级)→关闭", "roles": ["质量工程师"]}]},
    {"id": "warehouse", "name": "仓储流程", "mermaid": "mermaid/06-warehouse-flow.mmd", "color": "#7e6bd6",
     "summary": "15 ç±»å•据驱动库存台账(六元组、四量)变动,全部经事务流水留痕;支持调拨在途、盘点调整、批次/SN è¿½æº¯ã€åº“存占用与四级冻结、线边虚拟库负库存;对外 WmsStockApi ä¾› ERP æ”¶å‘。",
docs/project-business/data/objects.json
@@ -38,7 +38,9 @@
    {"name": "IQC来料检验", "module": "质量", "table": "mes_qc_iqc", "doc": "到货通知/外协入库触发,草稿0→完成4,结论回写来源单据", "upstream": ["到货通知", "外协入库"], "downstream": ["采购入库", "供应商退货", "NCR"], "keywords": "IQC æ¥æ–™æ£€éªŒ"},
    {"name": "IPQC过程检验", "module": "质量", "table": "mes_qc_ipqc", "doc": "报工质检工序产出行触发,完成回调推进报工与产出入库", "upstream": ["报工"], "downstream": ["产出入库", "NCR"], "keywords": "IPQC è¿‡ç¨‹æ£€éªŒ å·¡æ£€"},
    {"name": "OQC出货检验", "module": "质量", "table": "mes_qc_oqc", "doc": "销售出库提交触发,完成推进出库单待拣货", "upstream": ["销售出库单"], "downstream": ["销售出库单(推进)"], "keywords": "OQC å‡ºè´§æ£€éªŒ å‡ºåŽ‚æ£€éªŒ"},
    {"name": "RQC退货检验", "module": "质量", "table": "mes_qc_rqc", "doc": "生产退料/销售退货/售后退货触发,结论回调售后", "upstream": ["退料", "退货"], "downstream": ["退货入库", "售后回调"], "keywords": "RQC é€€è´§æ£€éªŒ"},
    {"name": "RQC退货检验", "module": "质量", "table": "mes_qc_rqc", "doc": "生产退料/销售退货/售后退货触发,结论回调售后;四类检验单均支持采样地点/证书编号/执行标准表头与复核·审核人,并可打印检验报告", "upstream": ["退料", "退货"], "downstream": ["退货入库", "售后回调", "检验报告打印"], "keywords": "RQC é€€è´§æ£€éªŒ"},
    {"name": "检验指标", "module": "质量", "table": "mes_qc_indicator", "doc": "支持两级分组树(itemType录入/分组+parentId)、公式文本展示、单位文本、同级排序;分组项不录值;有子项禁删;同层级名称可重复", "upstream": [], "downstream": ["质检方案", "检验报告打印"], "keywords": "检验指标 indicator åˆ†ç»„ å…¬å¼ åŒ–å·¥"},
    {"name": "检验报告打印", "module": "质量", "table": "mes_qc_indicator_result_detail", "doc": "化工报告范式:平行测量批次(measure_no)+分组矩阵+公式列+签字区;/mes/qc/indicator-result/report èšåˆï¼Œå‰ç«¯æµè§ˆå™¨æ‰“印", "upstream": ["IQC来料检验", "IPQC过程检验", "OQC出货检验", "RQC退货检验", "检验指标"], "downstream": [], "keywords": "打印 æŠ¥å‘Š æµ‹é‡æ‰¹æ¬¡ ç­¾å­— æ­£ä¸é†‡ GB/T6027"},
    {"name": "NCR不合格品", "module": "质量", "table": "mes_qc_ncr", "doc": "待评审0→已评审6→已处置7→已关闭8,处置:退货/报废/返工/让步接收/降级", "upstream": ["IQC/IPQC/OQC/RQC"], "downstream": ["退货/报废/返工(台账)"], "keywords": "NCR ä¸åˆæ ¼ å¤„ç½® ç‰¹é‡‡ æŠ¥åºŸ"},
    {"name": "设备", "module": "设备", "table": "mes_dv_machinery", "doc": "台账状态:停机/生产中/保养中;点检·保养·维修工单;AI é£Žé™©é¢„测消费其数据", "upstream": [], "downstream": ["点检", "ç»´ä¿®", "生产风险预测"], "keywords": "设备 ç‚¹æ£€ ä¿å…» ç»´ä¿® machinery"},
    {"name": "售后工单", "module": "售后", "table": "after_sale_ticket", "doc": "草稿0→处理中10→待入库30→待退款40→完结50→关闭60/取消99,判定:一般/ç»´ä¿®/退货", "upstream": ["客户反馈(人工录入)"], "downstream": ["维修记录", "退货申请"], "keywords": "售后 å·¥å• ticket ç»´ä¿®"},
docs/project-business/index.html
@@ -473,7 +473,9 @@
    {"name": "IQC来料检验", "module": "质量", "table": "mes_qc_iqc", "doc": "到货通知/外协入库触发,草稿0→完成4,结论回写来源单据", "upstream": ["到货通知", "外协入库"], "downstream": ["采购入库", "供应商退货", "NCR"], "keywords": "IQC æ¥æ–™æ£€éªŒ"},
    {"name": "IPQC过程检验", "module": "质量", "table": "mes_qc_ipqc", "doc": "报工质检工序产出行触发,完成回调推进报工与产出入库", "upstream": ["报工"], "downstream": ["产出入库", "NCR"], "keywords": "IPQC è¿‡ç¨‹æ£€éªŒ å·¡æ£€"},
    {"name": "OQC出货检验", "module": "质量", "table": "mes_qc_oqc", "doc": "销售出库提交触发,完成推进出库单待拣货", "upstream": ["销售出库单"], "downstream": ["销售出库单(推进)"], "keywords": "OQC å‡ºè´§æ£€éªŒ å‡ºåŽ‚æ£€éªŒ"},
    {"name": "RQC退货检验", "module": "质量", "table": "mes_qc_rqc", "doc": "生产退料/销售退货/售后退货触发,结论回调售后", "upstream": ["退料", "退货"], "downstream": ["退货入库", "售后回调"], "keywords": "RQC é€€è´§æ£€éªŒ"},
    {"name": "RQC退货检验", "module": "质量", "table": "mes_qc_rqc", "doc": "生产退料/销售退货/售后退货触发,结论回调售后;四类检验单均支持采样地点/证书编号/执行标准表头与复核·审核人,并可打印检验报告", "upstream": ["退料", "退货"], "downstream": ["退货入库", "售后回调", "检验报告打印"], "keywords": "RQC é€€è´§æ£€éªŒ"},
    {"name": "检验指标", "module": "质量", "table": "mes_qc_indicator", "doc": "支持两级分组树(itemType录入/分组+parentId)、公式文本展示、单位文本、同级排序;分组项不录值;有子项禁删;同层级名称可重复", "upstream": [], "downstream": ["质检方案", "检验报告打印"], "keywords": "检验指标 indicator åˆ†ç»„ å…¬å¼ åŒ–å·¥"},
    {"name": "检验报告打印", "module": "质量", "table": "mes_qc_indicator_result_detail", "doc": "化工报告范式:平行测量批次(measure_no)+分组矩阵+公式列+签字区;/mes/qc/indicator-result/report èšåˆï¼Œå‰ç«¯æµè§ˆå™¨æ‰“印", "upstream": ["IQC来料检验", "IPQC过程检验", "OQC出货检验", "RQC退货检验", "检验指标"], "downstream": [], "keywords": "打印 æŠ¥å‘Š æµ‹é‡æ‰¹æ¬¡ ç­¾å­— æ­£ä¸é†‡ GB/T6027"},
    {"name": "NCR不合格品", "module": "质量", "table": "mes_qc_ncr", "doc": "待评审0→已评审6→已处置7→已关闭8,处置:退货/报废/返工/让步接收/降级", "upstream": ["IQC/IPQC/OQC/RQC"], "downstream": ["退货/报废/返工(台账)"], "keywords": "NCR ä¸åˆæ ¼ å¤„ç½® ç‰¹é‡‡ æŠ¥åºŸ"},
    {"name": "设备", "module": "设备", "table": "mes_dv_machinery", "doc": "台账状态:停机/生产中/保养中;点检·保养·维修工单;AI é£Žé™©é¢„测消费其数据", "upstream": [], "downstream": ["点检", "ç»´ä¿®", "生产风险预测"], "keywords": "设备 ç‚¹æ£€ ä¿å…» ç»´ä¿® machinery"},
    {"name": "售后工单", "module": "售后", "table": "after_sale_ticket", "doc": "草稿0→处理中10→待入库30→待退款40→完结50→关闭60/取消99,判定:一般/ç»´ä¿®/退货", "upstream": ["客户反馈(人工录入)"], "downstream": ["维修记录", "退货申请"], "keywords": "售后 å·¥å• ticket ç»´ä¿®"},
@@ -514,12 +516,13 @@
       {"stage": "联动", "action": "审批通过按工序标记: å€’冲→自动物料消耗扣线边库(允许负库存);质检→产出待检行等 IPQC;关键非检→直接产出入库", "roles": ["系统自动"]},
       {"stage": "完工", "action": "仅末道工序回写工单已生产数量,达标自动完成;MPS ç´¯è®¡åˆ¤æ–­å®Œæˆå¹¶å‘事件给销售订单", "roles": ["系统自动"]}]},
    {"id": "quality", "name": "质量流程", "mermaid": "mermaid/05-quality-flow.mmd", "color": "#e06c6c",
     "summary": "四道检验闸口(IQC/IPQC/OQC/RQC,二态 0→4) + åˆ¤å®šå››æžœ(合格/特采/退货/报废) + NCR è¯„审处置关闭;AI æä¾›åˆ¤å®šå»ºè®®ï¼›NCR å½“前仅台账不触发返工单。",
     "summary": "四道检验闸口(IQC/IPQC/OQC/RQC,二态 0→4) + åˆ¤å®šå››æžœ(合格/特采/退货/报废) + NCR è¯„审处置关闭;AI æä¾›åˆ¤å®šå»ºè®®ï¼›NCR å½“前仅台账不触发返工单。支持化工报告范式:分组指标树+公式文本+平行测量批次+采样表头+签字区,浏览器打印检验报告。",
     "steps": [
       {"stage": "来料", "action": "到货通知/外协入库→IQC,结论回写推进待入库或触发退货", "roles": ["IQC è´¨æ£€å‘˜"]},
       {"stage": "过程", "action": "报工质检工序产出行→IPQC,合格拆行入库、不合格生成 NCR", "roles": ["IPQC è´¨æ£€å‘˜"]},
       {"stage": "出货", "action": "销售出库提交→OQC,完成后出库单进入待拣货", "roles": ["OQC è´¨æ£€å‘˜"]},
       {"stage": "退货", "action": "退料/销售退货/售后退货→RQC,结论回调单据与售后", "roles": ["RQC è´¨æ£€å‘˜"]},
       {"stage": "报告", "action": "检验结果按分组树+测量批次录入,/indicator-result/report èšåˆåŽå‰ç«¯æ‰“印检验报告(含采样地点/证书编号/执行标准/复核·审核签字区)", "roles": ["质检员", "复核人", "审核人"]},
       {"stage": "不合格", "action": "NCR: å¾…评审→评审→处置(退货/报废/返工/让步接收/降级)→关闭", "roles": ["质量工程师"]}]},
    {"id": "warehouse", "name": "仓储流程", "mermaid": "mermaid/06-warehouse-flow.mmd", "color": "#7e6bd6",
     "summary": "15 ç±»å•据驱动库存台账(六元组、四量)变动,全部经事务流水留痕;支持调拨在途、盘点调整、批次/SN è¿½æº¯ã€åº“存占用与四级冻结、线边虚拟库负库存;对外 WmsStockApi ä¾› ERP æ”¶å‘。",
docs/project-business/mermaid/05-quality-flow.mmd
@@ -18,3 +18,5 @@
  N2 -->|处置方式:退货1报废2返工3让步接收4降级使用5| N3[已处置7]
  N3 -->|关闭| N4[已关闭8]
  Q8[支撑数据:质检方案·检验指标·缺陷库·指标结果] --- A1
  Q9[化工报告增强:指标分组树+公式文本·平行测量批次·采样地点/证书编号/执行标准表头·复核人/审核人] --- Q8
  A1 & A2 & A3 & A4 --> P1[检验报告单打印·前端浏览器打印·含签字区]
docs/sql/config_export_all_20260904.sql
@@ -7928,6 +7928,11 @@
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
  `code` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '检测项编码',
  `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '检测项名称',
  `parent_id` bigint NOT NULL DEFAULT '0' COMMENT '父指标编号(0=顶级)',
  `item_type` tinyint NOT NULL DEFAULT '1' COMMENT '条目类型:1=录入项,2=分组项',
  `formula_text` varchar(500) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '公式展示文本(打印报告用,不参与计算)',
  `unit_text` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '单位文本(如 kg/m³、/%,打印展示用)',
  `sort_order` int NOT NULL DEFAULT '0' COMMENT '同级排序号',
  `type` int DEFAULT NULL COMMENT '检测项类型',
  `tool` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '检测工具',
  `result_type` int DEFAULT NULL COMMENT '结果值类型',
@@ -7938,8 +7943,9 @@
  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '更新者',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='MES è´¨æ£€æŒ‡æ ‡';
  PRIMARY KEY (`id`) USING BTREE,
  KEY `idx_qc_indicator_parent_id` (`parent_id`)
) ENGINE=InnoDB AUTO_INCREMENT=35 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='MES è´¨æ£€æŒ‡æ ‡';
-- ============ è¡¨ï¼šmes_qc_indicator_result(仅 DDL,无数据) ============
DROP TABLE IF EXISTS `mes_qc_indicator_result`;
@@ -7965,6 +7971,7 @@
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
  `result_id` bigint DEFAULT NULL COMMENT '关联检验结果编号',
  `indicator_id` bigint DEFAULT NULL COMMENT '检测指标编号',
  `measure_no` int NOT NULL DEFAULT '1' COMMENT '测量批次号(从 1 Ã¥Â¼â‚¬Ã¥Â§â€¹Ã¯Â¼Å’默认 1)',
  `value` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '检测值',
  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建者',
@@ -7972,7 +7979,8 @@
  `updater` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '更新者',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
  PRIMARY KEY (`id`) USING BTREE
  PRIMARY KEY (`id`) USING BTREE,
  KEY `idx_qc_result_detail_measure` (`result_id`,`indicator_id`,`measure_no`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='MES æ£€éªŒç»“果明细记录';
-- ============ è¡¨ï¼šmes_qc_ipqc(仅 DDL,无数据) ============
@@ -8008,6 +8016,11 @@
  `inspect_date` datetime DEFAULT NULL COMMENT '检测日期',
  `inspector_user_id` bigint DEFAULT NULL COMMENT '检测人员用户编号',
  `status` int DEFAULT NULL COMMENT '状态',
  `sampling_location` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '采样地点',
  `certificate_no` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '证书编号/合格证号',
  `execute_standard` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '执行标准',
  `audit_user_id` bigint DEFAULT NULL COMMENT '复核人用户编号',
  `approve_user_id` bigint DEFAULT NULL COMMENT '审核人用户编号',
  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建者',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
@@ -8070,6 +8083,11 @@
  `inspect_date` datetime DEFAULT NULL COMMENT '检测日期',
  `inspector_user_id` bigint DEFAULT NULL COMMENT '检测人员用户编号',
  `status` int DEFAULT NULL COMMENT '状态',
  `sampling_location` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '采样地点',
  `certificate_no` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '证书编号/合格证号',
  `execute_standard` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '执行标准',
  `audit_user_id` bigint DEFAULT NULL COMMENT '复核人用户编号',
  `approve_user_id` bigint DEFAULT NULL COMMENT '审核人用户编号',
  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建者',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
@@ -8175,6 +8193,11 @@
  `inspect_date` datetime DEFAULT NULL COMMENT '检测日期',
  `inspector_user_id` bigint DEFAULT NULL COMMENT '检测人员用户编号',
  `status` int DEFAULT NULL COMMENT '状态',
  `sampling_location` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '采样地点',
  `certificate_no` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '证书编号/合格证号',
  `execute_standard` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '执行标准',
  `audit_user_id` bigint DEFAULT NULL COMMENT '复核人用户编号',
  `approve_user_id` bigint DEFAULT NULL COMMENT '审核人用户编号',
  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建者',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
@@ -8235,6 +8258,11 @@
  `inspect_date` datetime DEFAULT NULL COMMENT '检测日期',
  `inspector_user_id` bigint DEFAULT NULL COMMENT '检测人员用户编号',
  `status` int DEFAULT NULL COMMENT '状态',
  `sampling_location` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '采样地点',
  `certificate_no` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '证书编号/合格证号',
  `execute_standard` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '执行标准',
  `audit_user_id` bigint DEFAULT NULL COMMENT '复核人用户编号',
  `approve_user_id` bigint DEFAULT NULL COMMENT '审核人用户编号',
  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建者',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
@@ -8275,6 +8303,7 @@
  `code` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '方案编号',
  `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '方案名称',
  `types` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '检测种类',
  `default_execute_standard` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '默认执行标准(如 GB/T6027)',
  `status` int DEFAULT NULL COMMENT '状态',
  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建者',
@@ -8291,6 +8320,8 @@
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
  `template_id` bigint DEFAULT NULL COMMENT '质检方案编号',
  `indicator_id` bigint DEFAULT NULL COMMENT '质检指标编号',
  `sort_order` int NOT NULL DEFAULT '0' COMMENT '展示排序号',
  `judge_flag` tinyint NOT NULL DEFAULT '1' COMMENT '是否参与合格判定:1=是,0=否',
  `check_method` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '检测方法',
  `standard_value` decimal(24,6) DEFAULT NULL COMMENT '标准值',
  `unit_measure_id` bigint DEFAULT NULL COMMENT '计量单位编号',
sql/mysql/mes_qc_indicator_report_upgrade.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
-- MES è´¨æ£€å‡çº§ï¼šåŒ–工检验报告能力(指标分组树 + å…¬å¼æ–‡æœ¬ + å¹³è¡Œæµ‹é‡ + æŠ¥å‘Šè¡¨å¤´å­—段)
-- é€‚用:MySQL 8.x;不含 tenant_id;重复执行会报"字段已存在",需先核对 information_schema
-- 1. æŒ‡æ ‡å­—典:层级分组 + æ¡ç›®ç±»åž‹ + å…¬å¼/单位文本 + æŽ’序
ALTER TABLE mes_qc_indicator
    ADD COLUMN parent_id BIGINT NOT NULL DEFAULT 0 COMMENT '父指标编号(0=顶级)' AFTER name,
    ADD COLUMN item_type TINYINT NOT NULL DEFAULT 1 COMMENT '条目类型:1=录入项,2=分组项' AFTER parent_id,
    ADD COLUMN formula_text VARCHAR(500) NULL COMMENT '公式展示文本(打印报告用,不参与计算)' AFTER item_type,
    ADD COLUMN unit_text VARCHAR(50) NULL COMMENT '单位文本(如 kg/m³、/%,打印展示用)' AFTER formula_text,
    ADD COLUMN sort_order INT NOT NULL DEFAULT 0 COMMENT '同级排序号' AFTER unit_text;
CREATE INDEX idx_qc_indicator_parent_id ON mes_qc_indicator (parent_id);
-- 2. æ–¹æ¡ˆæŒ‡æ ‡ï¼šæŽ’序 + æ˜¯å¦å‚与判定(分组/中间参数行=0)
ALTER TABLE mes_qc_template_indicator
    ADD COLUMN sort_order INT NOT NULL DEFAULT 0 COMMENT '展示排序号' AFTER indicator_id,
    ADD COLUMN judge_flag TINYINT NOT NULL DEFAULT 1 COMMENT '是否参与合格判定:1=是,0=否' AFTER sort_order;
-- 3. æ£€éªŒç»“果明细:平行测量批次号
ALTER TABLE mes_qc_indicator_result_detail
    ADD COLUMN measure_no INT NOT NULL DEFAULT 1 COMMENT '测量批次号(从 1 å¼€å§‹ï¼Œé»˜è®¤ 1)' AFTER indicator_id;
CREATE INDEX idx_qc_result_detail_measure ON mes_qc_indicator_result_detail (result_id, indicator_id, measure_no);
-- 4. æ£€éªŒæ–¹æ¡ˆï¼šé»˜è®¤æ‰§è¡Œæ ‡å‡†ï¼ˆå»ºå•时带入主表)
ALTER TABLE mes_qc_template
    ADD COLUMN default_execute_standard VARCHAR(50) NULL COMMENT '默认执行标准(如 GB/T6027)' AFTER types;
-- 5. å››ç±»æ£€éªŒå•主表:报告表头 + å¤æ ¸/审核签字字段
ALTER TABLE mes_qc_iqc
    ADD COLUMN sampling_location VARCHAR(100) NULL COMMENT '采样地点' AFTER status,
    ADD COLUMN certificate_no VARCHAR(50) NULL COMMENT '证书编号/合格证号' AFTER sampling_location,
    ADD COLUMN execute_standard VARCHAR(50) NULL COMMENT '执行标准' AFTER certificate_no,
    ADD COLUMN audit_user_id BIGINT NULL COMMENT '复核人用户编号' AFTER execute_standard,
    ADD COLUMN approve_user_id BIGINT NULL COMMENT '审核人用户编号' AFTER audit_user_id;
ALTER TABLE mes_qc_ipqc
    ADD COLUMN sampling_location VARCHAR(100) NULL COMMENT '采样地点' AFTER status,
    ADD COLUMN certificate_no VARCHAR(50) NULL COMMENT '证书编号/合格证号' AFTER sampling_location,
    ADD COLUMN execute_standard VARCHAR(50) NULL COMMENT '执行标准' AFTER certificate_no,
    ADD COLUMN audit_user_id BIGINT NULL COMMENT '复核人用户编号' AFTER execute_standard,
    ADD COLUMN approve_user_id BIGINT NULL COMMENT '审核人用户编号' AFTER audit_user_id;
ALTER TABLE mes_qc_oqc
    ADD COLUMN sampling_location VARCHAR(100) NULL COMMENT '采样地点' AFTER status,
    ADD COLUMN certificate_no VARCHAR(50) NULL COMMENT '证书编号/合格证号' AFTER sampling_location,
    ADD COLUMN execute_standard VARCHAR(50) NULL COMMENT '执行标准' AFTER certificate_no,
    ADD COLUMN audit_user_id BIGINT NULL COMMENT '复核人用户编号' AFTER execute_standard,
    ADD COLUMN approve_user_id BIGINT NULL COMMENT '审核人用户编号' AFTER audit_user_id;
ALTER TABLE mes_qc_rqc
    ADD COLUMN sampling_location VARCHAR(100) NULL COMMENT '采样地点' AFTER status,
    ADD COLUMN certificate_no VARCHAR(50) NULL COMMENT '证书编号/合格证号' AFTER sampling_location,
    ADD COLUMN execute_standard VARCHAR(50) NULL COMMENT '执行标准' AFTER certificate_no,
    ADD COLUMN audit_user_id BIGINT NULL COMMENT '复核人用户编号' AFTER execute_standard,
    ADD COLUMN approve_user_id BIGINT NULL COMMENT '审核人用户编号' AFTER audit_user_id;
sql/mysql/mes_qc_indicator_seed.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,50 @@
-- ========================================================================
-- MES è´¨æ£€æŒ‡æ ‡ç§å­æ•°æ®ï¼šæ­£ä¸é†‡æ£€éªŒè®°å½•(GB/T 6027)样例树
-- ç”¨é€”:演示分组检验项 + å…¬å¼æ–‡æœ¬ + å¹³è¡Œæµ‹é‡ï¼Œä¾›å‰ç«¯å½•å…¥/打印直接试用
-- å–值约定:item_type 1=录入项 2=分组项;result_type 1=浮点 2=整数 3=文本 4=字典 5=文件
-- ä¸å« tenant_id(项目不使用多租户)
-- æ³¨æ„ï¼šæ‰§è¡Œå‰ç¡®è®¤ mes_qc_indicator å·²å« parent_id/item_type/formula_text/unit_text/sort_order åˆ—
-- ========================================================================
-- å¹‚等:重复执行前清理旧种子(按 code å‰ç¼€ï¼Œå«åˆ†ç»„与子项)
DELETE FROM mes_qc_indicator WHERE code LIKE 'SEED-BT-%';
-- é¡¶çº§ï¼šå¤–观(定性文本结论)
INSERT INTO mes_qc_indicator (code, name, parent_id, item_type, formula_text, unit_text, sort_order, type, result_type, result_specification, remark, creator, updater)
VALUES ('SEED-BT-APPEAR', '外观', 0, 1, NULL, NULL, 10, 4, 3, NULL, '澄清透明液体,无机械杂质', '1', '1');
-- é¡¶çº§ï¼šè‰²åº¦
INSERT INTO mes_qc_indicator (code, name, parent_id, item_type, formula_text, unit_text, sort_order, type, result_type, result_specification, remark, creator, updater)
VALUES ('SEED-BT-COLOR', '色度', 0, 1, NULL, 'Hazen', 20, 4, 1, NULL, NULL, '1', '1');
-- åˆ†ç»„:水分测定(含公式文本,下挂参数行 + ç»“果行)
INSERT INTO mes_qc_indicator (code, name, parent_id, item_type, formula_text, unit_text, sort_order, type, result_type, result_specification, remark, creator, updater)
VALUES ('SEED-BT-WATER', '水分', 0, 2, 'w = (m1 - m0) / m Ã— 100%', NULL, 30, 4, NULL, NULL, '卡尔费休/干燥失重', '1', '1');
SET @g_water = LAST_INSERT_ID();
INSERT INTO mes_qc_indicator (code, name, parent_id, item_type, formula_text, unit_text, sort_order, type, result_type, result_specification, remark, creator, updater) VALUES
('SEED-BT-WATER-M',  '试样质量 m',   @g_water, 1, NULL, 'g',  1, 4, 1, NULL, NULL, '1', '1'),
('SEED-BT-WATER-M1', '试样+称量瓶 m1', @g_water, 1, NULL, 'g', 2, 4, 1, NULL, NULL, '1', '1'),
('SEED-BT-WATER-M0', '称量瓶 m0',    @g_water, 1, NULL, 'g',  3, 4, 1, NULL, NULL, '1', '1'),
('SEED-BT-WATER-W',  '水分结果 w',   @g_water, 1, NULL, '%',  4, 4, 1, NULL, NULL, '1', '1');
-- åˆ†ç»„:酸含量(左侧公式 + å¤šå‚数行 + ç»“果行)
INSERT INTO mes_qc_indicator (code, name, parent_id, item_type, formula_text, unit_text, sort_order, type, result_type, result_specification, remark, creator, updater)
VALUES ('SEED-BT-ACID', '酸含量', 0, 2, 'w3 = c Ã— V Ã— 0.06005 / m Ã— 100%', NULL, 40, 4, NULL, NULL, '酸碱滴定', '1', '1');
SET @g_acid = LAST_INSERT_ID();
INSERT INTO mes_qc_indicator (code, name, parent_id, item_type, formula_text, unit_text, sort_order, type, result_type, result_specification, remark, creator, updater) VALUES
('SEED-BT-ACID-C',   '标液浓度 c', @g_acid, 1, NULL, 'mol/L', 1, 4, 1, NULL, NULL, '1', '1'),
('SEED-BT-ACID-V',   '消耗滴定液体积 V', @g_acid, 1, NULL, 'mL', 2, 4, 1, NULL, NULL, '1', '1'),
('SEED-BT-ACID-M',   '试样质量 m', @g_acid, 1, NULL, 'g',    3, 4, 1, NULL, NULL, '1', '1'),
('SEED-BT-ACID-W3',  '酸含量结果 w3', @g_acid, 1, NULL, '%', 4, 4, 1, NULL, NULL, '1', '1');
-- åˆ†ç»„:正丁醇含量(纯度差减法)
INSERT INTO mes_qc_indicator (code, name, parent_id, item_type, formula_text, unit_text, sort_order, type, result_type, result_specification, remark, creator, updater)
VALUES ('SEED-BT-PURITY', '正丁醇含量', 0, 2, '纯度 = 100% - æ°´åˆ† - é…¸å«é‡ - å…¶å®ƒæ‚è´¨', NULL, 50, 4, NULL, NULL, '气相色谱/差减', '1', '1');
SET @g_purity = LAST_INSERT_ID();
INSERT INTO mes_qc_indicator (code, name, parent_id, item_type, formula_text, unit_text, sort_order, type, result_type, result_specification, remark, creator, updater) VALUES
('SEED-BT-PURITY-V', '正丁醇含量 /%', @g_purity, 1, NULL, '%', 1, 4, 1, NULL, NULL, '1', '1');
-- é¡¶çº§ï¼šå¯†åº¦ã€æŠ˜å…‰çއ
INSERT INTO mes_qc_indicator (code, name, parent_id, item_type, formula_text, unit_text, sort_order, type, result_type, result_specification, remark, creator, updater) VALUES
('SEED-BT-DENSITY', '密度',   0, 1, NULL, 'kg/m³', 60, 4, 1, NULL, NULL, '1', '1'),
('SEED-BT-REFRACT', '折光率', 0, 1, NULL, 'n²⁰D', 70, 4, 1, NULL, NULL, '1', '1');
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/indicator/MesQcIndicatorController.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.mes.controller.admin.qc.indicator;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
@@ -22,7 +23,11 @@
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@@ -66,7 +71,7 @@
    @PreAuthorize("@ss.hasPermission('mes:qc-indicator:query')")
    public CommonResult<MesQcIndicatorRespVO> getIndicator(@RequestParam("id") Long id) {
        MesQcIndicatorDO indicator = indicatorService.getIndicator(id);
        return success(BeanUtils.toBean(indicator, MesQcIndicatorRespVO.class));
        return success(buildRespVOList(Collections.singletonList(indicator)).get(0));
    }
    @GetMapping("/page")
@@ -74,7 +79,14 @@
    @PreAuthorize("@ss.hasPermission('mes:qc-indicator:query')")
    public CommonResult<PageResult<MesQcIndicatorRespVO>> getIndicatorPage(@Valid MesQcIndicatorPageReqVO pageReqVO) {
        PageResult<MesQcIndicatorDO> pageResult = indicatorService.getIndicatorPage(pageReqVO);
        return success(BeanUtils.toBean(pageResult, MesQcIndicatorRespVO.class));
        return success(new PageResult<>(buildRespVOList(pageResult.getList()), pageResult.getTotal()));
    }
    @GetMapping("/list")
    @Operation(summary = "获得质检指标全量列表(含分组层级,供树表/下拉使用)")
    @PreAuthorize("@ss.hasPermission('mes:qc-indicator:query')")
    public CommonResult<List<MesQcIndicatorRespVO>> getIndicatorList() {
        return success(buildRespVOList(indicatorService.getIndicatorList()));
    }
    @GetMapping("/export-excel")
@@ -90,4 +102,26 @@
                BeanUtils.toBean(list, MesQcIndicatorRespVO.class));
    }
    // ==================== æ‹¼æŽ¥ VO ====================
    private List<MesQcIndicatorRespVO> buildRespVOList(List<MesQcIndicatorDO> list) {
        List<MesQcIndicatorRespVO> voList = BeanUtils.toBean(list, MesQcIndicatorRespVO.class);
        if (CollUtil.isEmpty(list)) {
            return voList;
        }
        // å¡«å……父指标名称
        Set<Long> parentIds = list.stream().map(MesQcIndicatorDO::getParentId)
                .filter(pid -> pid != null && pid > 0).collect(Collectors.toSet());
        if (CollUtil.isNotEmpty(parentIds)) {
            Map<Long, String> parentNameMap = indicatorService.getIndicatorList(parentIds).stream()
                    .collect(Collectors.toMap(MesQcIndicatorDO::getId, MesQcIndicatorDO::getName));
            voList.forEach(vo -> {
                if (vo.getParentId() != null && vo.getParentId() > 0) {
                    vo.setParentName(parentNameMap.get(vo.getParentId()));
                }
            });
        }
        return voList;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/indicator/vo/MesQcIndicatorRespVO.java
@@ -27,6 +27,24 @@
    @ExcelProperty("检测项名称")
    private String name;
    @Schema(description = "父指标编号(0=顶级)", example = "0")
    private Long parentId;
    @Schema(description = "父指标名称", example = "水分")
    private String parentName;
    @Schema(description = "条目类型:1=录入项,2=分组项", example = "1")
    private Integer itemType;
    @Schema(description = "公式展示文本(打印报告用)", example = "w3=c1v1*0.06005/pv*100%")
    private String formulaText;
    @Schema(description = "单位文本(打印展示用)", example = "kg/m³")
    private String unitText;
    @Schema(description = "同级排序号", example = "10")
    private Integer sortOrder;
    @Schema(description = "检测项类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @ExcelProperty(value = "检测项类型", converter = DictConvert.class)
    @DictFormat(DictTypeConstants.MES_INDICATOR_TYPE)
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/indicator/vo/MesQcIndicatorSaveReqVO.java
@@ -20,15 +20,26 @@
    @NotEmpty(message = "检测项名称不能为空")
    private String name;
    @Schema(description = "父指标编号(0=顶级,不传视为 0)", example = "0")
    private Long parentId;
    @Schema(description = "条目类型:1=录入项,2=分组项(不传默认 1)", example = "1")
    private Integer itemType;
    @Schema(description = "公式展示文本(打印报告用,不参与计算)", example = "w3=c1v1*0.06005/pv*100%")
    private String formulaText;
    @Schema(description = "单位文本(打印展示用)", example = "kg/m³")
    private String unitText;
    @Schema(description = "同级排序号", example = "10")
    private Integer sortOrder;
    @Schema(description = "检测项类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "检测项类型不能为空")
    private Integer type;
    @Schema(description = "检测工具", example = "卡尺")
    private String tool;
    @Schema(description = "结果值类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "结果值类型不能为空")
    @Schema(description = "结果值类型(录入项必填,分组项不填;服务层校验)", example = "1")
    private Integer resultType;
    @Schema(description = "结果值属性", example = "IMG")
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/indicatorresult/MesQcIndicatorResultController.java
@@ -13,8 +13,10 @@
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.oqc.MesQcOqcLineDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.rqc.MesQcRqcLineDO;
import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum;
import cn.iocoder.yudao.module.mes.enums.qc.MesQcIndicatorItemTypeEnum;
import cn.iocoder.yudao.module.mes.service.qc.indicator.MesQcIndicatorService;
import cn.iocoder.yudao.module.mes.service.qc.indicatorresult.MesQcIndicatorResultService;
import cn.iocoder.yudao.module.mes.service.qc.indicatorresult.MesQcReportService;
import cn.iocoder.yudao.module.mes.service.qc.ipqc.MesQcIpqcLineService;
import cn.iocoder.yudao.module.mes.service.qc.iqc.MesQcIqcLineService;
import cn.iocoder.yudao.module.mes.service.qc.oqc.MesQcOqcLineService;
@@ -30,6 +32,7 @@
import org.springframework.web.bind.annotation.*;
import java.util.*;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
@@ -44,6 +47,8 @@
    @Resource
    private MesQcIndicatorResultService resultService;
    @Resource
    private MesQcReportService reportService;
    @Resource
    private MesQcIqcLineService iqcLineService;
    @Resource
@@ -117,12 +122,29 @@
        return success(voPageResult);
    }
    @GetMapping("/report")
    @Operation(summary = "获得检验报告单数据(打印用:表头 + æ£€éªŒé¡¹æ ‘ + å¹³è¡Œæµ‹é‡ + ç­¾å­—区)")
    @Parameters({
            @Parameter(name = "qcId", description = "质检单ID", required = true, example = "1"),
            @Parameter(name = "qcType", description = "质检类型", required = true, example = "1"),
            @Parameter(name = "resultId", description = "检验结果(样品)编号,可不传;多样品时用于选择", example = "1")
    })
    @PreAuthorize("@ss.hasPermission('mes:qc-iqc:query')")
    public CommonResult<MesQcQcReportRespVO> getQcReportData(
            @RequestParam("qcId") Long qcId,
            @RequestParam("qcType") Integer qcType,
            @RequestParam(value = "resultId", required = false) Long resultId) {
        return success(reportService.getQcReportData(qcId, qcType, resultId));
    }
    // ==================== æ‹¼æŽ¥ VO ====================
    /**
     * æž„建明细 Item åˆ—表
     * æž„建明细 Item æ ‘(支持分组层级 + å¹³è¡Œæµ‹é‡å¤šæ‰¹æ¬¡ï¼‰
     *
     * <p>前端表单仅需:indicatorId、indicatorName、valueType、valueSpecification + å·²æœ‰ç»“果(id、value、remark)</p>
     * <p>返回树形列表:顶级为分组项或独立录入项,分组项 children æŒ‚子项;
     * æ¯ä¸ªå½•入项的平行测量值放在 measures(按 measureNo å‡åºï¼‰ï¼›
     * å…¼å®¹å­—段 id/value/remark å–第一批值,供旧数据直接展示</p>
     *
     * @param details å·²æœ‰ç»“果明细(空列表表示获取空值模板)
     * @param qcId    è´¨æ£€å• ID
@@ -155,30 +177,78 @@
            return Collections.emptyList();
        }
        // 1.2 æ‰¹é‡æŸ¥è¯¢æ£€æµ‹æŒ‡æ ‡
        // 1.2 æ‰¹é‡æŸ¥è¯¢æ£€æµ‹æŒ‡æ ‡ï¼›å­é¡¹çš„父分组未挂在行上时自动补全,保证树完整
        Map<Long, MesQcIndicatorDO> indicatorMap = indicatorService.getIndicatorMap(new HashSet<>(indicatorIds));
        // 1.3 æž„建已有明细 Map(按 indicatorId ç´¢å¼•)
        Map<Long, MesQcIndicatorResultDetailDO> detailMap = CollUtil.isEmpty(details)
                ? Collections.emptyMap()
                : convertMap(details, MesQcIndicatorResultDetailDO::getIndicatorId);
        Set<Long> missingParentIds = indicatorMap.values().stream()
                .filter(ind -> ind.getParentId() != null && ind.getParentId() > 0
                        && !indicatorMap.containsKey(ind.getParentId()))
                .map(MesQcIndicatorDO::getParentId).collect(Collectors.toSet());
        if (CollUtil.isNotEmpty(missingParentIds)) {
            indicatorMap.putAll(indicatorService.getIndicatorMap(missingParentIds));
        }
        // 2. éåŽ†æŒ‡æ ‡ ID,组装 VO
        List<MesQcIndicatorResultRespVO.Item> voList = new ArrayList<>(indicatorIds.size());
        for (Long indicatorId : indicatorIds) {
        // 1.3 å·²æœ‰æ˜Žç»†åˆ†ç»„(indicatorId -> å¤šæ‰¹æ¬¡åˆ—表)
        Map<Long, List<MesQcIndicatorResultDetailDO>> detailMap = CollUtil.isEmpty(details)
                ? Collections.emptyMap()
                : details.stream().collect(Collectors.groupingBy(MesQcIndicatorResultDetailDO::getIndicatorId));
        // 2. æž„建节点
        Map<Long, MesQcIndicatorResultRespVO.Item> nodeMap = new LinkedHashMap<>();
        for (Map.Entry<Long, MesQcIndicatorDO> entry : indicatorMap.entrySet()) {
            Long indicatorId = entry.getKey();
            MesQcIndicatorDO indicator = entry.getValue();
            MesQcIndicatorResultRespVO.Item vo = new MesQcIndicatorResultRespVO.Item()
                    .setIndicatorId(indicatorId);
            // æ¥è‡ª indicator
            findAndThen(indicatorMap, indicatorId, indicator -> vo
                    .setIndicatorId(indicatorId)
                    .setIndicatorName(indicator.getName())
                    .setValueType(indicator.getResultType())
                    .setValueSpecification(indicator.getResultSpecification()));
            // æ¥è‡ªå·²æœ‰ç»“果明细(如有)
            findAndThen(detailMap, indicatorId, detail -> vo
                    .setId(detail.getId()).setResultId(detail.getResultId())
                    .setValue(detail.getValue()).setRemark(detail.getRemark()));
            voList.add(vo);
                    .setValueSpecification(indicator.getResultSpecification())
                    .setParentId(Objects.requireNonNullElse(indicator.getParentId(), 0L))
                    .setItemType(Objects.requireNonNullElse(indicator.getItemType(),
                            MesQcIndicatorItemTypeEnum.RECORD.getType()))
                    .setFormulaText(indicator.getFormulaText())
                    .setUnitText(indicator.getUnitText())
                    .setSortOrder(Objects.requireNonNullElse(indicator.getSortOrder(), 0));
            List<MesQcIndicatorResultDetailDO> itemDetails = detailMap.getOrDefault(indicatorId, Collections.emptyList());
            List<MesQcIndicatorResultRespVO.Measure> measures = new ArrayList<>(itemDetails.size());
            itemDetails.stream()
                    .sorted(Comparator.comparing(d -> Objects.requireNonNullElse(d.getMeasureNo(), 1)))
                    .forEach(detail -> measures.add(new MesQcIndicatorResultRespVO.Measure()
                            .setDetailId(detail.getId()).setResultId(detail.getResultId())
                            .setMeasureNo(Objects.requireNonNullElse(detail.getMeasureNo(), 1))
                            .setValue(detail.getValue()).setRemark(detail.getRemark())));
            vo.setMeasures(measures);
            // å…¼å®¹æ—§å­—段:取第一批测量值
            if (CollUtil.isNotEmpty(measures)) {
                MesQcIndicatorResultRespVO.Measure first = measures.get(0);
                vo.setId(first.getDetailId()).setResultId(first.getResultId())
                        .setValue(first.getValue()).setRemark(first.getRemark());
            }
            nodeMap.put(indicatorId, vo);
        }
        return voList;
        // 3. æŒ‚树:父不在集合内的子项提升为顶级;同级按排序号
        Comparator<MesQcIndicatorResultRespVO.Item> byOrder =
                Comparator.comparing(MesQcIndicatorResultRespVO.Item::getSortOrder)
                        .thenComparing(MesQcIndicatorResultRespVO.Item::getIndicatorId);
        List<MesQcIndicatorResultRespVO.Item> roots = new ArrayList<>();
        for (MesQcIndicatorResultRespVO.Item node : nodeMap.values()) {
            MesQcIndicatorResultRespVO.Item parent = node.getParentId() > 0 ? nodeMap.get(node.getParentId()) : null;
            if (parent == null) {
                roots.add(node);
            } else {
                if (parent.getChildren() == null) {
                    parent.setChildren(new ArrayList<>());
                }
                parent.getChildren().add(node);
            }
        }
        roots.sort(byOrder);
        roots.forEach(root -> {
            if (root.getChildren() != null) {
                root.getChildren().sort(byOrder);
            }
        });
        return roots;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/indicatorresult/vo/MesQcIndicatorResultRespVO.java
@@ -39,11 +39,11 @@
    @Schema(description = "检验结果明细列表")
    private List<Item> items;
    @Schema(description = "检验结果明细项")
    @Schema(description = "检验结果明细项(树形:分组项下挂子项;平行测量放入 measures)")
    @Data
    public static class Item {
        @Schema(description = "编号", example = "1024")
        @Schema(description = "编号(第一批明细的 ID,兼容旧字段)", example = "1024")
        private Long id;
        @Schema(description = "关联检验结果ID", example = "1")
@@ -52,10 +52,10 @@
        @Schema(description = "检测指标ID", example = "1")
        private Long indicatorId;
        @Schema(description = "检测值(统一存为字符串)", example = "3.14")
        @Schema(description = "检测值(第一批测量值,兼容旧字段)", example = "3.14")
        private String value;
        @Schema(description = "备注", example = "无")
        @Schema(description = "备注(第一批明细的备注,兼容旧字段)", example = "无")
        private String remark;
        // ========== å…³è”查询字段(来自 indicator) ==========
@@ -69,6 +69,52 @@
        @Schema(description = "值属性", example = "IMG")
        private String valueSpecification;
        @Schema(description = "父指标编号(0=顶级)", example = "0")
        private Long parentId;
        @Schema(description = "条目类型:1=录入项,2=分组项", example = "1")
        private Integer itemType;
        @Schema(description = "公式展示文本(打印报告用)", example = "w3=c1v1*0.06005/pv*100%")
        private String formulaText;
        @Schema(description = "单位文本(打印展示用)", example = "kg/m³")
        private String unitText;
        @Schema(description = "同级排序号", example = "10")
        private Integer sortOrder;
        // ========== å¹³è¡Œæµ‹é‡ ==========
        @Schema(description = "测量批次明细列表(按 measureNo å‡åºï¼‰")
        private List<Measure> measures;
        // ========== æ ‘形结构 ==========
        @Schema(description = "子项列表(分组项下挂)")
        private List<Item> children;
    }
    @Schema(description = "检验结果测量批次明细(同一指标的一次平行测量)")
    @Data
    public static class Measure {
        @Schema(description = "明细编号", example = "1024")
        private Long detailId;
        @Schema(description = "关联检验结果ID", example = "1")
        private Long resultId;
        @Schema(description = "测量批次号", example = "1")
        private Integer measureNo;
        @Schema(description = "检测值", example = "3.14")
        private String value;
        @Schema(description = "备注", example = "无")
        private String remark;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/indicatorresult/vo/MesQcIndicatorResultSaveReqVO.java
@@ -48,6 +48,9 @@
        @Schema(description = "检测指标ID", example = "1")
        private Long indicatorId;
        @Schema(description = "测量批次号(从 1 å¼€å§‹ï¼Œå¹³è¡Œæµ‹é‡å¤šæ‰¹æ¬¡ï¼›ä¸ä¼ é»˜è®¤ 1)", example = "1")
        private Integer measureNo;
        @Schema(description = "检测值(统一存为字符串)", example = "3.14")
        private String value;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/indicatorresult/vo/MesQcQcReportRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,149 @@
package cn.iocoder.yudao.module.mes.controller.admin.qc.indicatorresult.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - MES æ£€éªŒæŠ¥å‘Šå•(打印数据) Response VO")
@Data
public class MesQcQcReportRespVO {
    // ========== æŠ¥å‘Šè¡¨å¤´ ==========
    @Schema(description = "质检类型:1=IQC,2=IPQC,3=OQC,4=RQC", example = "1")
    private Integer qcType;
    @Schema(description = "检验单编号", example = "IQC20260830001")
    private String docCode;
    @Schema(description = "检验单名称(报告标题)", example = "正丁醇检验记录")
    private String docName;
    @Schema(description = "物料编码", example = "M001")
    private String itemCode;
    @Schema(description = "物料名称", example = "正丁醇")
    private String itemName;
    @Schema(description = "物料规格型号", example = "工业级")
    private String itemSpecification;
    @Schema(description = "批次号(来料供应商批次/其他单据批次号)", example = "V0028B1")
    private String batchNo;
    @Schema(description = "采样地点", example = "罐区")
    private String samplingLocation;
    @Schema(description = "证书编号/合格证号", example = "V0028")
    private String certificateNo;
    @Schema(description = "执行标准", example = "GB/T6027")
    private String executeStandard;
    @Schema(description = "采样/检验时间", example = "2026-08-30T01:00:00")
    private LocalDateTime samplingTime;
    @Schema(description = "批量(来料=接收数量,过程/退货=检测数量,出货=出货数量)", example = "30")
    private BigDecimal batchQuantity;
    @Schema(description = "检测结果(字典 mes_qc_check_result)", example = "1")
    private Integer checkResult;
    // ========== ç­¾å­—区 ==========
    @Schema(description = "分析者(检测人)用户编号", example = "1")
    private Long inspectorUserId;
    @Schema(description = "分析者姓名", example = "新艳")
    private String inspectorName;
    @Schema(description = "复核人用户编号", example = "2")
    private Long auditUserId;
    @Schema(description = "复核人姓名", example = "海丽")
    private String auditName;
    @Schema(description = "审核人用户编号", example = "3")
    private Long approveUserId;
    @Schema(description = "审核人姓名", example = "王五")
    private String approveName;
    @Schema(description = "备注", example = "无")
    private String remark;
    // ========== æ£€éªŒé¡¹çŸ©é˜µ ==========
    @Schema(description = "测量批次号列表(报告列头,如 [1,2];无平行测量时为 [1])")
    private List<Integer> measureNos;
    @Schema(description = "检验项树(顶级=分组项或独立录入项;分组项 children æŒ‚子项)")
    private List<ReportItem> items;
    @Schema(description = "报告检验项")
    @Data
    public static class ReportItem {
        @Schema(description = "检测指标ID", example = "1")
        private Long indicatorId;
        @Schema(description = "检测项编码", example = "ACID")
        private String indicatorCode;
        @Schema(description = "检测项名称", example = "酸含量")
        private String indicatorName;
        @Schema(description = "父指标编号(0=顶级)", example = "0")
        private Long parentId;
        @Schema(description = "条目类型:1=录入项,2=分组项", example = "1")
        private Integer itemType;
        @Schema(description = "公式展示文本(打印在分组行左侧,可为空)", example = "w3=c1v1*0.06005/pv*100%")
        private String formulaText;
        @Schema(description = "单位文本", example = "mol/L")
        private String unitText;
        @Schema(description = "同级排序号", example = "10")
        private Integer sortOrder;
        @Schema(description = "检测方法", example = "酸碱滴定")
        private String checkMethod;
        @Schema(description = "标准值", example = "0.0010")
        private BigDecimal standardValue;
        @Schema(description = "阈值下限", example = "0")
        private BigDecimal thresholdMin;
        @Schema(description = "阈值上限", example = "0.0050")
        private BigDecimal thresholdMax;
        @Schema(description = "测量值列表(按 measureNo å‡åºï¼›åˆ†ç»„项为空)")
        private List<ReportMeasure> measures;
        @Schema(description = "子项列表(分组项下挂)")
        private List<ReportItem> children;
    }
    @Schema(description = "报告测量值(某检验项某批次)")
    @Data
    public static class ReportMeasure {
        @Schema(description = "测量批次号", example = "1")
        private Integer measureNo;
        @Schema(description = "检测值", example = "0.001")
        private String value;
        @Schema(description = "备注", example = "无")
        private String remark;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ipqc/MesQcIpqcController.java
@@ -172,9 +172,12 @@
        // æ‰¹é‡æŸ¥è¯¢è®¡é‡å•位(
        Map<Long, MesMdUnitMeasureDO> unitMeasureMap = unitMeasureService.getUnitMeasureMap(
                convertSet(itemMap.values(), MesMdItemDO::getUnitMeasureId));
        // æ‰¹é‡æŸ¥è¯¢æ£€æµ‹äººå‘˜
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
                convertSet(list, MesQcIpqcDO::getInspectorUserId));
        // æ‰¹é‡æŸ¥è¯¢æ£€æµ‹/复核/审核人员
        Set<Long> qcUserIds = new HashSet<>();
        qcUserIds.addAll(convertSet(list, MesQcIpqcDO::getInspectorUserId));
        qcUserIds.addAll(convertSet(list, MesQcIpqcDO::getAuditUserId));
        qcUserIds.addAll(convertSet(list, MesQcIpqcDO::getApproveUserId));
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(qcUserIds);
        // æ‰¹é‡æŸ¥è¯¢æ£€éªŒé¡¹æŒ‡æ ‡
        Map<Long, String> indicatorSummaryMap = buildIndicatorSummaryMap(list);
        // æ‹¼è£… VO
@@ -192,6 +195,10 @@
            });
            findAndThen(userMap, vo.getInspectorUserId(),
                    user -> vo.setInspectorNickname(user.getNickname()));
            findAndThen(userMap, vo.getAuditUserId(),
                    user -> vo.setAuditNickname(user.getNickname()));
            findAndThen(userMap, vo.getApproveUserId(),
                    user -> vo.setApproveNickname(user.getNickname()));
            vo.setIndicatorResultSummary(indicatorSummaryMap.getOrDefault(vo.getId(), ""));
        });
    }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ipqc/vo/MesQcIpqcRespVO.java
@@ -179,6 +179,29 @@
    @ExcelProperty("备注")
    private String remark;
    // ========== æŠ¥å‘Šè¡¨å¤´ä¸Žç­¾å­— ==========
    @Schema(description = "采样地点", example = "罐区")
    private String samplingLocation;
    @Schema(description = "证书编号/合格证号", example = "V0028")
    private String certificateNo;
    @Schema(description = "执行标准", example = "GB/T6027")
    private String executeStandard;
    @Schema(description = "复核人用户编号", example = "1")
    private Long auditUserId;
    @Schema(description = "复核人昵称", example = "李四")
    private String auditNickname;
    @Schema(description = "审核人用户编号", example = "1")
    private Long approveUserId;
    @Schema(description = "审核人昵称", example = "王五")
    private String approveNickname;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ipqc/vo/MesQcIpqcSaveReqVO.java
@@ -112,6 +112,23 @@
    @Schema(description = "备注", example = "备注")
    private String remark;
    // ========== æŠ¥å‘Šè¡¨å¤´ä¸Žç­¾å­— ==========
    @Schema(description = "采样地点", example = "罐区")
    private String samplingLocation;
    @Schema(description = "证书编号/合格证号", example = "V0028")
    private String certificateNo;
    @Schema(description = "执行标准", example = "GB/T6027")
    private String executeStandard;
    @Schema(description = "复核人用户编号", example = "1")
    private Long auditUserId;
    @Schema(description = "审核人用户编号", example = "1")
    private Long approveUserId;
    @AssertTrue(message = "检测数量必须等于合格品数量与不合格品数量之和")
    @JsonIgnore
    public boolean isQuantityValid() {
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/MesQcIqcController.java
@@ -160,9 +160,12 @@
        // æ‰¹é‡æŸ¥è¯¢è®¡é‡å•位(通过物料的 unitMeasureId)
        Map<Long, MesMdUnitMeasureDO> unitMeasureMap = unitMeasureService.getUnitMeasureMap(
                convertSet(itemMap.values(), MesMdItemDO::getUnitMeasureId));
        // æ‰¹é‡æŸ¥è¯¢æ£€æµ‹äººå‘˜
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
                convertSet(list, MesQcIqcDO::getInspectorUserId));
        // æ‰¹é‡æŸ¥è¯¢æ£€æµ‹/复核/审核人员
        Set<Long> qcUserIds = new HashSet<>();
        qcUserIds.addAll(convertSet(list, MesQcIqcDO::getInspectorUserId));
        qcUserIds.addAll(convertSet(list, MesQcIqcDO::getAuditUserId));
        qcUserIds.addAll(convertSet(list, MesQcIqcDO::getApproveUserId));
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(qcUserIds);
        // æ‰¹é‡æŸ¥è¯¢æ£€éªŒé¡¹æŒ‡æ ‡
        Set<Long> qcIds = convertSet(list, MesQcIqcDO::getId);
        Map<Long, List<MesQcIndicatorResultDO>> indicatorResultMap = new HashMap<>();
@@ -233,6 +236,10 @@
            });
            findAndThen(userMap, vo.getInspectorUserId(),
                    user -> vo.setInspectorNickname(user.getNickname()));
            findAndThen(userMap, vo.getAuditUserId(),
                    user -> vo.setAuditNickname(user.getNickname()));
            findAndThen(userMap, vo.getApproveUserId(),
                    user -> vo.setApproveNickname(user.getNickname()));
            // è®¾ç½®æ£€éªŒé¡¹æŒ‡æ ‡æ±‡æ€»
            vo.setIndicatorResultSummary(indicatorSummaryMap.getOrDefault(vo.getId(), ""));
        });
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/vo/MesQcIqcRespVO.java
@@ -152,6 +152,29 @@
    @ExcelProperty("备注")
    private String remark;
    // ========== æŠ¥å‘Šè¡¨å¤´ä¸Žç­¾å­— ==========
    @Schema(description = "采样地点", example = "罐区")
    private String samplingLocation;
    @Schema(description = "证书编号/合格证号", example = "V0028")
    private String certificateNo;
    @Schema(description = "执行标准", example = "GB/T6027")
    private String executeStandard;
    @Schema(description = "复核人用户编号", example = "1")
    private Long auditUserId;
    @Schema(description = "复核人昵称", example = "李四")
    private String auditNickname;
    @Schema(description = "审核人用户编号", example = "1")
    private Long approveUserId;
    @Schema(description = "审核人昵称", example = "王五")
    private String approveNickname;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/vo/MesQcIqcSaveReqVO.java
@@ -91,6 +91,23 @@
    @Schema(description = "备注", example = "备注")
    private String remark;
    // ========== æŠ¥å‘Šè¡¨å¤´ä¸Žç­¾å­— ==========
    @Schema(description = "采样地点", example = "罐区")
    private String samplingLocation;
    @Schema(description = "证书编号/合格证号", example = "V0028")
    private String certificateNo;
    @Schema(description = "执行标准", example = "GB/T6027")
    private String executeStandard;
    @Schema(description = "复核人用户编号", example = "1")
    private Long auditUserId;
    @Schema(description = "审核人用户编号", example = "1")
    private Long approveUserId;
    @AssertTrue(message = "合格品数量与不合格品数量之和必须等于本次接收数量")
    @JsonIgnore
    public boolean isQuantityValid() {
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/oqc/MesQcOqcController.java
@@ -159,9 +159,12 @@
        // æ‰¹é‡æŸ¥è¯¢è®¡é‡å•位
        Map<Long, MesMdUnitMeasureDO> unitMeasureMap = unitMeasureService.getUnitMeasureMap(
                convertSet(itemMap.values(), MesMdItemDO::getUnitMeasureId));
        // æ‰¹é‡æŸ¥è¯¢æ£€æµ‹äººå‘˜
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
                convertSet(list, MesQcOqcDO::getInspectorUserId));
        // æ‰¹é‡æŸ¥è¯¢æ£€æµ‹/复核/审核人员
        Set<Long> qcUserIds = new HashSet<>();
        qcUserIds.addAll(convertSet(list, MesQcOqcDO::getInspectorUserId));
        qcUserIds.addAll(convertSet(list, MesQcOqcDO::getAuditUserId));
        qcUserIds.addAll(convertSet(list, MesQcOqcDO::getApproveUserId));
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(qcUserIds);
        // æ‰¹é‡æŸ¥è¯¢æ£€éªŒé¡¹æŒ‡æ ‡
        Map<Long, String> indicatorSummaryMap = buildIndicatorSummaryMap(list);
        // æ‹¼è£… VO
@@ -175,6 +178,10 @@
            });
            findAndThen(userMap, vo.getInspectorUserId(),
                    user -> vo.setInspectorNickname(user.getNickname()));
            findAndThen(userMap, vo.getAuditUserId(),
                    user -> vo.setAuditNickname(user.getNickname()));
            findAndThen(userMap, vo.getApproveUserId(),
                    user -> vo.setApproveNickname(user.getNickname()));
            vo.setIndicatorResultSummary(indicatorSummaryMap.getOrDefault(vo.getId(), ""));
        });
    }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/oqc/vo/MesQcOqcRespVO.java
@@ -160,6 +160,29 @@
    @ExcelProperty("备注")
    private String remark;
    // ========== æŠ¥å‘Šè¡¨å¤´ä¸Žç­¾å­— ==========
    @Schema(description = "采样地点", example = "罐区")
    private String samplingLocation;
    @Schema(description = "证书编号/合格证号", example = "V0028")
    private String certificateNo;
    @Schema(description = "执行标准", example = "GB/T6027")
    private String executeStandard;
    @Schema(description = "复核人用户编号", example = "1")
    private Long auditUserId;
    @Schema(description = "复核人昵称", example = "李四")
    private String auditNickname;
    @Schema(description = "审核人用户编号", example = "1")
    private Long approveUserId;
    @Schema(description = "审核人昵称", example = "王五")
    private String approveNickname;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/oqc/vo/MesQcOqcSaveReqVO.java
@@ -95,6 +95,23 @@
    @Schema(description = "备注", example = "备注")
    private String remark;
    // ========== æŠ¥å‘Šè¡¨å¤´ä¸Žç­¾å­— ==========
    @Schema(description = "采样地点", example = "罐区")
    private String samplingLocation;
    @Schema(description = "证书编号/合格证号", example = "V0028")
    private String certificateNo;
    @Schema(description = "执行标准", example = "GB/T6027")
    private String executeStandard;
    @Schema(description = "复核人用户编号", example = "1")
    private Long auditUserId;
    @Schema(description = "审核人用户编号", example = "1")
    private Long approveUserId;
    @AssertTrue(message = "检测数量必须等于合格品数量与不合格品数量之和")
    @JsonIgnore
    public boolean isQuantityValid() {
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/rqc/MesQcRqcController.java
@@ -151,9 +151,12 @@
        // æ‰¹é‡æŸ¥è¯¢è®¡é‡å•位
        Map<Long, MesMdUnitMeasureDO> unitMeasureMap = unitMeasureService.getUnitMeasureMap(
                convertSet(itemMap.values(), MesMdItemDO::getUnitMeasureId));
        // æ‰¹é‡æŸ¥è¯¢æ£€æµ‹äººå‘˜
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
                convertSet(list, MesQcRqcDO::getInspectorUserId));
        // æ‰¹é‡æŸ¥è¯¢æ£€æµ‹/复核/审核人员
        Set<Long> qcUserIds = new HashSet<>();
        qcUserIds.addAll(convertSet(list, MesQcRqcDO::getInspectorUserId));
        qcUserIds.addAll(convertSet(list, MesQcRqcDO::getAuditUserId));
        qcUserIds.addAll(convertSet(list, MesQcRqcDO::getApproveUserId));
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(qcUserIds);
        // æ‰¹é‡æŸ¥è¯¢æ£€éªŒé¡¹æŒ‡æ ‡
        Map<Long, String> indicatorSummaryMap = buildIndicatorSummaryMap(list);
        // æ‹¼è£… VO
@@ -165,6 +168,10 @@
            });
            findAndThen(userMap, vo.getInspectorUserId(),
                    user -> vo.setInspectorNickname(user.getNickname()));
            findAndThen(userMap, vo.getAuditUserId(),
                    user -> vo.setAuditNickname(user.getNickname()));
            findAndThen(userMap, vo.getApproveUserId(),
                    user -> vo.setApproveNickname(user.getNickname()));
            vo.setIndicatorResultSummary(indicatorSummaryMap.getOrDefault(vo.getId(), ""));
        });
    }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/rqc/vo/MesQcRqcRespVO.java
@@ -147,6 +147,29 @@
    @ExcelProperty("备注")
    private String remark;
    // ========== æŠ¥å‘Šè¡¨å¤´ä¸Žç­¾å­— ==========
    @Schema(description = "采样地点", example = "罐区")
    private String samplingLocation;
    @Schema(description = "证书编号/合格证号", example = "V0028")
    private String certificateNo;
    @Schema(description = "执行标准", example = "GB/T6027")
    private String executeStandard;
    @Schema(description = "复核人用户编号", example = "1")
    private Long auditUserId;
    @Schema(description = "复核人昵称", example = "李四")
    private String auditNickname;
    @Schema(description = "审核人用户编号", example = "1")
    private Long approveUserId;
    @Schema(description = "审核人昵称", example = "王五")
    private String approveNickname;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/rqc/vo/MesQcRqcSaveReqVO.java
@@ -89,6 +89,23 @@
    @Schema(description = "备注", example = "备注")
    private String remark;
    // ========== æŠ¥å‘Šè¡¨å¤´ä¸Žç­¾å­— ==========
    @Schema(description = "采样地点", example = "罐区")
    private String samplingLocation;
    @Schema(description = "证书编号/合格证号", example = "V0028")
    private String certificateNo;
    @Schema(description = "执行标准", example = "GB/T6027")
    private String executeStandard;
    @Schema(description = "复核人用户编号", example = "1")
    private Long auditUserId;
    @Schema(description = "审核人用户编号", example = "1")
    private Long approveUserId;
    @AssertTrue(message = "检测数量必须等于合格品数量与不合格品数量之和")
    @JsonIgnore
    public boolean isQuantityValid() {
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/template/vo/MesQcTemplateRespVO.java
@@ -33,6 +33,10 @@
    @ExcelProperty("状态")
    private Integer status;
    @Schema(description = "默认执行标准(新建检验单选择本方案时自动带入)", example = "GB/T 6027")
    @ExcelProperty("默认执行标准")
    private String defaultExecuteStandard;
    @Schema(description = "备注", example = "备注")
    @ExcelProperty("备注")
    private String remark;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/template/vo/MesQcTemplateSaveReqVO.java
@@ -32,6 +32,10 @@
    @NotNull(message = "状态不能为空")
    private Integer status;
    @Schema(description = "默认执行标准(新建检验单选择本方案时自动带入)", example = "GB/T 6027")
    @Size(max = 50, message = "默认执行标准长度不能超过 50")
    private String defaultExecuteStandard;
    @Schema(description = "备注", example = "备注")
    private String remark;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/template/vo/indicator/MesQcTemplateIndicatorRespVO.java
@@ -23,6 +23,12 @@
    @Schema(description = "质检指标ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    private Long indicatorId;
    @Schema(description = "展示排序号", example = "10")
    private Integer sortOrder;
    @Schema(description = "是否参与合格判定", example = "true")
    private Boolean judgeFlag;
    @Schema(description = "检测方法", example = "目视检查外观")
    @ExcelProperty("检测方法")
    private String checkMethod;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/template/vo/indicator/MesQcTemplateIndicatorSaveReqVO.java
@@ -21,6 +21,12 @@
    @NotNull(message = "质检指标不能为空")
    private Long indicatorId;
    @Schema(description = "展示排序号", example = "10")
    private Integer sortOrder;
    @Schema(description = "是否参与合格判定(分组/中间参数行为 false)", example = "true")
    private Boolean judgeFlag;
    @Schema(description = "检测方法", example = "目视检查外观")
    private String checkMethod;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/indicator/MesQcIndicatorDO.java
@@ -36,6 +36,29 @@
     */
    private String name;
    /**
     * çˆ¶æŒ‡æ ‡ç¼–号(0=顶级)
     */
    private Long parentId;
    /**
     * æ¡ç›®ç±»åž‹
     *
     * æžšä¸¾ {@link cn.iocoder.yudao.module.mes.enums.qc.MesQcIndicatorItemTypeEnum}
     * 1=录入项(参与录入与判定),2=分组项(仅作报告分组标题)
     */
    private Integer itemType;
    /**
     * å…¬å¼å±•示文本(打印报告用,不参与计算)
     */
    private String formulaText;
    /**
     * å•位文本(如 kg/m³、/%,打印展示用)
     */
    private String unitText;
    /**
     * åŒçº§æŽ’序号
     */
    private Integer sortOrder;
    /**
     * æ£€æµ‹é¡¹ç±»åž‹
     *
     * æžšä¸¾ {@link cn.iocoder.yudao.module.mes.enums.DictTypeConstants#MES_INDICATOR_TYPE}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/indicatorresult/MesQcIndicatorResultDetailDO.java
@@ -40,6 +40,10 @@
     */
    private Long indicatorId;
    /**
     * æµ‹é‡æ‰¹æ¬¡å·ï¼ˆä»Ž 1 å¼€å§‹ï¼Œé»˜è®¤ 1,支持平行测量多批次)
     */
    private Integer measureNo;
    /**
     * æ£€æµ‹å€¼ï¼ˆç»Ÿä¸€å­˜ä¸ºå­—符串)
     */
    private String value;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/ipqc/MesQcIpqcDO.java
@@ -202,6 +202,33 @@
     * æžšä¸¾ {@link MesQcStatusEnum}
     */
    private Integer status;
    // ========== æŠ¥å‘Šè¡¨å¤´ä¸Žç­¾å­— ==========
    /**
     * é‡‡æ ·åœ°ç‚¹
     */
    private String samplingLocation;
    /**
     * è¯ä¹¦ç¼–号/合格证号
     */
    private String certificateNo;
    /**
     * æ‰§è¡Œæ ‡å‡†ï¼ˆå¦‚ GB/T6027)
     */
    private String executeStandard;
    /**
     * å¤æ ¸äººç”¨æˆ·ç¼–号
     *
     * å…³è” AdminUserDO çš„ id
     */
    private Long auditUserId;
    /**
     * å®¡æ ¸äººç”¨æˆ·ç¼–号
     *
     * å…³è” AdminUserDO çš„ id
     */
    private Long approveUserId;
    /**
     * å¤‡æ³¨
     */
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/iqc/MesQcIqcDO.java
@@ -178,6 +178,33 @@
     * æžšä¸¾ {@link MesQcStatusEnum}
     */
    private Integer status;
    // ========== æŠ¥å‘Šè¡¨å¤´ä¸Žç­¾å­— ==========
    /**
     * é‡‡æ ·åœ°ç‚¹
     */
    private String samplingLocation;
    /**
     * è¯ä¹¦ç¼–号/合格证号
     */
    private String certificateNo;
    /**
     * æ‰§è¡Œæ ‡å‡†ï¼ˆå¦‚ GB/T6027)
     */
    private String executeStandard;
    /**
     * å¤æ ¸äººç”¨æˆ·ç¼–号
     *
     * å…³è” AdminUserDO çš„ id
     */
    private Long auditUserId;
    /**
     * å®¡æ ¸äººç”¨æˆ·ç¼–号
     *
     * å…³è” AdminUserDO çš„ id
     */
    private Long approveUserId;
    /**
     * å¤‡æ³¨
     */
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/oqc/MesQcOqcDO.java
@@ -181,6 +181,33 @@
     * æžšä¸¾ {@link cn.iocoder.yudao.module.mes.enums.qc.MesQcStatusEnum}
     */
    private Integer status;
    // ========== æŠ¥å‘Šè¡¨å¤´ä¸Žç­¾å­— ==========
    /**
     * é‡‡æ ·åœ°ç‚¹
     */
    private String samplingLocation;
    /**
     * è¯ä¹¦ç¼–号/合格证号
     */
    private String certificateNo;
    /**
     * æ‰§è¡Œæ ‡å‡†ï¼ˆå¦‚ GB/T6027)
     */
    private String executeStandard;
    /**
     * å¤æ ¸äººç”¨æˆ·ç¼–号
     *
     * å…³è” AdminUserDO çš„ id
     */
    private Long auditUserId;
    /**
     * å®¡æ ¸äººç”¨æˆ·ç¼–号
     *
     * å…³è” AdminUserDO çš„ id
     */
    private Long approveUserId;
    /**
     * å¤‡æ³¨
     */
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/rqc/MesQcRqcDO.java
@@ -159,6 +159,33 @@
     * æžšä¸¾ {@link MesQcStatusEnum}
     */
    private Integer status;
    // ========== æŠ¥å‘Šè¡¨å¤´ä¸Žç­¾å­— ==========
    /**
     * é‡‡æ ·åœ°ç‚¹
     */
    private String samplingLocation;
    /**
     * è¯ä¹¦ç¼–号/合格证号
     */
    private String certificateNo;
    /**
     * æ‰§è¡Œæ ‡å‡†ï¼ˆå¦‚ GB/T6027)
     */
    private String executeStandard;
    /**
     * å¤æ ¸äººç”¨æˆ·ç¼–号
     *
     * å…³è” AdminUserDO çš„ id
     */
    private Long auditUserId;
    /**
     * å®¡æ ¸äººç”¨æˆ·ç¼–号
     *
     * å…³è” AdminUserDO çš„ id
     */
    private Long approveUserId;
    /**
     * å¤‡æ³¨
     */
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/template/MesQcTemplateDO.java
@@ -47,6 +47,10 @@
    @TableField(typeHandler = IntegerListTypeHandler.class)
    private List<Integer> types;
    /**
     * é»˜è®¤æ‰§è¡Œæ ‡å‡†ï¼ˆå¦‚ GB/T6027,建单时带入检验单主表)
     */
    private String defaultExecuteStandard;
    /**
     * çŠ¶æ€
     *
     * æžšä¸¾ {@link cn.iocoder.yudao.framework.common.enums.CommonStatusEnum}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/template/MesQcTemplateIndicatorDO.java
@@ -43,6 +43,14 @@
     */
    private Long indicatorId;
    /**
     * å±•示排序号
     */
    private Integer sortOrder;
    /**
     * æ˜¯å¦å‚与合格判定:true=是,false=否(分组/中间参数行为否)
     */
    private Boolean judgeFlag;
    /**
     * æ£€æµ‹æ–¹æ³•
     */
    private String checkMethod;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
@@ -372,6 +372,10 @@
    ErrorCode QC_INDICATOR_CODE_DUPLICATE = new ErrorCode(1_040_601_001, "质检指标编码已存在");
    ErrorCode QC_INDICATOR_NAME_DUPLICATE = new ErrorCode(1_040_601_002, "质检指标名称已存在");
    ErrorCode QC_INDICATOR_RESULT_SPECIFICATION_REQUIRED = new ErrorCode(1_040_601_003, "结果值属性不能为空");
    ErrorCode QC_INDICATOR_EXISTS_CHILDREN = new ErrorCode(1_040_601_004, "检测项【{}】下存在子项,请先删除子项后再删除本项");
    ErrorCode QC_INDICATOR_PARENT_NOT_GROUP = new ErrorCode(1_040_601_005, "所属分组【{}】不是分组项,请选择分组类型的检测项作为父级");
    ErrorCode QC_INDICATOR_PARENT_LEVEL_EXCEEDED = new ErrorCode(1_040_601_006, "检测项层级最多两级,所属分组【{}】本身已是子项,不能再作为父级");
    ErrorCode QC_INDICATOR_RESULT_TYPE_REQUIRED = new ErrorCode(1_040_601_007, "录入项的结果值类型不能为空;如仅作报告分组标题,请将条目类型设为分组项");
    // ========== MES è´¨é‡ç®¡ç†-缺陷类型(1-040-602-000) ==========
    ErrorCode QC_DEFECT_NOT_EXISTS = new ErrorCode(1_040_602_000, "缺陷类型不存在");
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/qc/MesQcIndicatorItemTypeEnum.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.mes.enums.qc;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
 * MES è´¨æ£€æŒ‡æ ‡æ¡ç›®ç±»åž‹æžšä¸¾
 *
 * <p>RECORD å½•入项:参与结果录入与判定;GROUP åˆ†ç»„项:仅作报告分组标题,不录入不判定</p>
 *
 * @author è¶…级管理员
 */
@Getter
@AllArgsConstructor
public enum MesQcIndicatorItemTypeEnum implements ArrayValuable<Integer> {
    RECORD(1, "录入项"),
    GROUP(2, "分组项");
    public static final Integer[] ARRAYS = Arrays.stream(values()).map(MesQcIndicatorItemTypeEnum::getType).toArray(Integer[]::new);
    /**
     * ç±»åž‹å€¼
     */
    private final Integer type;
    /**
     * ç±»åž‹å
     */
    private final String name;
    @Override
    public Integer[] array() {
        return ARRAYS;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/indicator/MesQcIndicatorServiceImpl.java
@@ -6,10 +6,12 @@
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.qc.indicator.vo.MesQcIndicatorPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.qc.indicator.vo.MesQcIndicatorSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicator.MesQcIndicatorDO;
import cn.iocoder.yudao.module.mes.dal.mysql.qc.indicator.MesQcIndicatorMapper;
import cn.iocoder.yudao.module.mes.enums.qc.MesQcIndicatorItemTypeEnum;
import cn.iocoder.yudao.module.mes.enums.qc.MesQcResultValueTypeEnum;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
@@ -38,6 +40,8 @@
    @Override
    public Long createIndicator(MesQcIndicatorSaveReqVO createReqVO) {
        // è§„范化层级字段
        normalizeHierarchyFields(createReqVO);
        // æ ¡éªŒç›¸å…³å‚æ•°
        validateIndicatorSaveData(null, createReqVO);
@@ -51,6 +55,8 @@
    public void updateIndicator(MesQcIndicatorSaveReqVO updateReqVO) {
        // æ ¡éªŒå­˜åœ¨
        validateIndicatorExists(updateReqVO.getId());
        // è§„范化层级字段
        normalizeHierarchyFields(updateReqVO);
        // æ ¡éªŒç›¸å…³å‚æ•°
        validateIndicatorSaveData(updateReqVO.getId(), updateReqVO);
@@ -62,24 +68,72 @@
    @Override
    public void deleteIndicator(Long id) {
        // æ ¡éªŒå­˜åœ¨
        validateIndicatorExists(id);
        MesQcIndicatorDO indicator = validateIndicatorExists(id);
        // æ ¡éªŒæ— å­é¡¹
        Long childCount = indicatorMapper.selectCount(new LambdaQueryWrapperX<MesQcIndicatorDO>()
                .eq(MesQcIndicatorDO::getParentId, id));
        if (childCount != null && childCount > 0) {
            throw exception(QC_INDICATOR_EXISTS_CHILDREN, indicator.getName());
        }
        // åˆ é™¤
        indicatorMapper.deleteById(id);
    }
    private void normalizeHierarchyFields(MesQcIndicatorSaveReqVO saveReqVO) {
        if (saveReqVO.getParentId() == null) {
            saveReqVO.setParentId(0L);
        }
        if (saveReqVO.getItemType() == null) {
            saveReqVO.setItemType(MesQcIndicatorItemTypeEnum.RECORD.getType());
        }
        if (saveReqVO.getSortOrder() == null) {
            saveReqVO.setSortOrder(0);
        }
    }
    private void validateIndicatorSaveData(Long id, MesQcIndicatorSaveReqVO saveReqVO) {
        // æ ¡éªŒç¼–码唯一
        validateIndicatorCodeUnique(id, saveReqVO.getCode());
        // æ ¡éªŒåç§°å”¯ä¸€
        validateIndicatorNameUnique(id, saveReqVO.getName());
        // æ ¡éªŒåŒå±‚级名称唯一(不同分组下允许同名参数行,如"试样体积V/mL")
        validateIndicatorNameUnique(id, saveReqVO.getName(), saveReqVO.getParentId());
        // æ ¡éªŒå±‚级合法性(父项存在、父项为分组项、最多两级)
        validateIndicatorHierarchy(id, saveReqVO);
        // å½•入项必须指定结果值类型
        if (ObjUtil.notEqual(MesQcIndicatorItemTypeEnum.GROUP.getType(), saveReqVO.getItemType())
                && saveReqVO.getResultType() == null) {
            throw exception(QC_INDICATOR_RESULT_TYPE_REQUIRED);
        }
        // æ ¡éªŒç»“果值属性
        validateResultSpecification(saveReqVO.getResultType(), saveReqVO.getResultSpecification());
    }
    private void validateIndicatorExists(Long id) {
        if (indicatorMapper.selectById(id) == null) {
    private void validateIndicatorHierarchy(Long id, MesQcIndicatorSaveReqVO saveReqVO) {
        Long parentId = saveReqVO.getParentId();
        if (parentId == null || parentId == 0L) {
            return;
        }
        if (ObjUtil.equal(parentId, id)) {
            throw exception(QC_INDICATOR_PARENT_NOT_GROUP, saveReqVO.getName());
        }
        MesQcIndicatorDO parent = indicatorMapper.selectById(parentId);
        if (parent == null) {
            throw exception(QC_INDICATOR_NOT_EXISTS);
        }
        if (ObjUtil.notEqual(MesQcIndicatorItemTypeEnum.GROUP.getType(), parent.getItemType())) {
            throw exception(QC_INDICATOR_PARENT_NOT_GROUP, parent.getName());
        }
        // ä»…允许两级:父分组本身必须是顶级
        if (ObjUtil.defaultIfNull(parent.getParentId(), 0L) != 0L) {
            throw exception(QC_INDICATOR_PARENT_LEVEL_EXCEEDED, parent.getName());
        }
    }
    private MesQcIndicatorDO validateIndicatorExists(Long id) {
        MesQcIndicatorDO indicator = indicatorMapper.selectById(id);
        if (indicator == null) {
            throw exception(QC_INDICATOR_NOT_EXISTS);
        }
        return indicator;
    }
    private void validateIndicatorCodeUnique(Long id, String code) {
@@ -92,12 +146,12 @@
        }
    }
    private void validateIndicatorNameUnique(Long id, String name) {
        MesQcIndicatorDO indicator = indicatorMapper.selectByName(name);
        if (indicator == null) {
            return;
        }
        if (ObjUtil.notEqual(indicator.getId(), id)) {
    private void validateIndicatorNameUnique(Long id, String name, Long parentId) {
        Long count = indicatorMapper.selectCount(new LambdaQueryWrapperX<MesQcIndicatorDO>()
                .eq(MesQcIndicatorDO::getName, name)
                .eq(MesQcIndicatorDO::getParentId, ObjUtil.defaultIfNull(parentId, 0L))
                .ne(id != null, MesQcIndicatorDO::getId, id));
        if (count != null && count > 0) {
            throw exception(QC_INDICATOR_NAME_DUPLICATE);
        }
    }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/indicatorresult/MesQcIndicatorResultServiceImpl.java
@@ -14,6 +14,7 @@
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.oqc.MesQcOqcDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.rqc.MesQcRqcDO;
import cn.iocoder.yudao.module.mes.dal.mysql.qc.indicatorresult.MesQcIndicatorResultMapper;
import cn.iocoder.yudao.module.mes.enums.qc.MesQcIndicatorItemTypeEnum;
import cn.iocoder.yudao.module.mes.enums.qc.MesQcResultValueTypeEnum;
import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum;
import cn.iocoder.yudao.module.mes.service.qc.indicator.MesQcIndicatorService;
@@ -82,7 +83,8 @@
        // 2.2 æ‰¹é‡æ’入明细
        List<MesQcIndicatorResultDetailDO> details = BeanUtils.toBean(createReqVO.getItems(),
                MesQcIndicatorResultDetailDO.class);
        details.forEach(detail -> detail.setResultId(result.getId()));
        details.forEach(detail -> detail.setResultId(result.getId())
                .setMeasureNo(Objects.requireNonNullElse(detail.getMeasureNo(), 1)));
        resultDetailService.createDetailList(details);
        return result.getId();
    }
@@ -102,6 +104,7 @@
        // 2.2 æ‰¹é‡æ›´æ–°æ˜Žç»†
        List<MesQcIndicatorResultDetailDO> details = BeanUtils.toBean(updateReqVO.getItems(),
                MesQcIndicatorResultDetailDO.class);
        details.forEach(detail -> detail.setMeasureNo(Objects.requireNonNullElse(detail.getMeasureNo(), 1)));
        resultDetailService.createOrUpdateDetailList(details);
    }
@@ -225,6 +228,10 @@
            if (indicator == null || indicator.getResultType() == null) {
                continue;
            }
            // åˆ†ç»„项(GROUP)仅承载层级/公式文本,不录入值,跳过格式校验
            if (Objects.equals(indicator.getItemType(), MesQcIndicatorItemTypeEnum.GROUP.getType())) {
                continue;
            }
            Integer resultType = indicator.getResultType();
            if (Objects.equals(resultType, MesQcResultValueTypeEnum.FLOAT.getType())) {
                try {
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/indicatorresult/MesQcReportService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.mes.service.qc.indicatorresult;
import cn.iocoder.yudao.module.mes.controller.admin.qc.indicatorresult.vo.MesQcQcReportRespVO;
/**
 * MES æ£€éªŒæŠ¥å‘Šå• Service(打印数据聚合,四类质检单通用)
 *
 * @author è¶…级管理员
 */
public interface MesQcReportService {
    /**
     * èŽ·å¾—æ£€éªŒæŠ¥å‘Šå•æ•°æ®ï¼ˆè¡¨å¤´ + æ£€éªŒé¡¹æ ‘ + å¹³è¡Œæµ‹é‡å€¼ + ç­¾å­—区)
     *
     * @param qcId     è´¨æ£€å• ID
     * @param qcType   è´¨æ£€ç±»åž‹ï¼ˆ{@link cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum})
     * @param resultId æ£€éªŒç»“果(样品)ID,可为空;为空时取该检验单下最早的样品
     * @return æŠ¥å‘Šæ•°æ®
     */
    MesQcQcReportRespVO getQcReportData(Long qcId, Integer qcType, Long resultId);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/indicatorresult/MesQcReportServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,314 @@
package cn.iocoder.yudao.module.mes.service.qc.indicatorresult;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.module.mes.controller.admin.qc.indicatorresult.vo.MesQcQcReportRespVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicator.MesQcIndicatorDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicatorresult.MesQcIndicatorResultDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicatorresult.MesQcIndicatorResultDetailDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.ipqc.MesQcIpqcDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.ipqc.MesQcIpqcLineDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcLineDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.oqc.MesQcOqcDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.oqc.MesQcOqcLineDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.rqc.MesQcRqcDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.rqc.MesQcRqcLineDO;
import cn.iocoder.yudao.module.mes.enums.qc.MesQcIndicatorItemTypeEnum;
import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.qc.indicator.MesQcIndicatorService;
import cn.iocoder.yudao.module.mes.service.qc.ipqc.MesQcIpqcLineService;
import cn.iocoder.yudao.module.mes.service.qc.ipqc.MesQcIpqcService;
import cn.iocoder.yudao.module.mes.service.qc.iqc.MesQcIqcLineService;
import cn.iocoder.yudao.module.mes.service.qc.iqc.MesQcIqcService;
import cn.iocoder.yudao.module.mes.service.qc.oqc.MesQcOqcLineService;
import cn.iocoder.yudao.module.mes.service.qc.oqc.MesQcOqcService;
import cn.iocoder.yudao.module.mes.service.qc.rqc.MesQcRqcLineService;
import cn.iocoder.yudao.module.mes.service.qc.rqc.MesQcRqcService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
/**
 * MES æ£€éªŒæŠ¥å‘Šå• Service å®žçŽ°ç±»
 *
 * @author è¶…级管理员
 */
@Service
@Validated
public class MesQcReportServiceImpl implements MesQcReportService {
    @Resource
    private MesQcIndicatorResultService resultService;
    @Resource
    private MesQcIndicatorService indicatorService;
    @Resource
    private MesQcIqcService iqcService;
    @Resource
    private MesQcIqcLineService iqcLineService;
    @Resource
    @Lazy
    private MesQcIpqcService ipqcService;
    @Resource
    @Lazy
    private MesQcIpqcLineService ipqcLineService;
    @Resource
    @Lazy
    private MesQcOqcService oqcService;
    @Resource
    @Lazy
    private MesQcOqcLineService oqcLineService;
    @Resource
    @Lazy
    private MesQcRqcService rqcService;
    @Resource
    @Lazy
    private MesQcRqcLineService rqcLineService;
    @Resource
    private MesMdItemService itemService;
    @Resource
    private AdminUserApi adminUserApi;
    @Override
    public MesQcQcReportRespVO getQcReportData(Long qcId, Integer qcType, Long resultId) {
        MesQcQcReportRespVO report = new MesQcQcReportRespVO();
        report.setQcType(qcType);
        // 1. ä¸»è¡¨ä¿¡æ¯ï¼ˆæŒ‰ç±»åž‹å–公共字段)
        Long itemId = null;
        List<Long> lineIndicatorIds = new ArrayList<>();
        Map<Long, LineInfo> lineInfoMap = new LinkedHashMap<>();
        if (Objects.equals(qcType, MesQcTypeEnum.IQC.getType())) {
            MesQcIqcDO doc = iqcService.validateIqcExists(qcId);
            itemId = doc.getItemId();
            report.setDocCode(doc.getCode()).setDocName(doc.getName())
                    .setBatchNo(doc.getVendorBatch()).setBatchQuantity(doc.getReceivedQuantity())
                    .setSamplingTime(doc.getInspectDate() != null ? doc.getInspectDate() : doc.getReceiveDate())
                    .setSamplingLocation(doc.getSamplingLocation()).setCertificateNo(doc.getCertificateNo())
                    .setExecuteStandard(doc.getExecuteStandard())
                    .setInspectorUserId(doc.getInspectorUserId()).setAuditUserId(doc.getAuditUserId())
                    .setApproveUserId(doc.getApproveUserId()).setCheckResult(doc.getCheckResult())
                    .setRemark(doc.getRemark());
            List<MesQcIqcLineDO> lines = iqcLineService.getIqcLineListByIqcId(qcId);
            for (MesQcIqcLineDO line : lines) {
                lineIndicatorIds.add(line.getIndicatorId());
                lineInfoMap.putIfAbsent(line.getIndicatorId(), new LineInfo(line.getCheckMethod(),
                        line.getStandardValue(), line.getMinThreshold(), line.getMaxThreshold()));
            }
        } else if (Objects.equals(qcType, MesQcTypeEnum.IPQC.getType())) {
            MesQcIpqcDO doc = ipqcService.validateIpqcExists(qcId);
            itemId = doc.getItemId();
            report.setDocCode(doc.getCode()).setDocName(doc.getName())
                    .setBatchNo(doc.getSourceDocCode()).setBatchQuantity(doc.getCheckQuantity())
                    .setSamplingTime(doc.getInspectDate())
                    .setSamplingLocation(doc.getSamplingLocation()).setCertificateNo(doc.getCertificateNo())
                    .setExecuteStandard(doc.getExecuteStandard())
                    .setInspectorUserId(doc.getInspectorUserId()).setAuditUserId(doc.getAuditUserId())
                    .setApproveUserId(doc.getApproveUserId()).setCheckResult(doc.getCheckResult())
                    .setRemark(doc.getRemark());
            List<MesQcIpqcLineDO> lines = ipqcLineService.getIpqcLineListByIpqcId(qcId);
            for (MesQcIpqcLineDO line : lines) {
                lineIndicatorIds.add(line.getIndicatorId());
                lineInfoMap.putIfAbsent(line.getIndicatorId(), new LineInfo(line.getCheckMethod(),
                        line.getStandardValue(), line.getMinThreshold(), line.getMaxThreshold()));
            }
        } else if (Objects.equals(qcType, MesQcTypeEnum.OQC.getType())) {
            MesQcOqcDO doc = oqcService.validateOqcExists(qcId);
            itemId = doc.getItemId();
            report.setDocCode(doc.getCode()).setDocName(doc.getName())
                    .setBatchNo(doc.getBatchCode()).setBatchQuantity(doc.getOutQuantity())
                    .setSamplingTime(doc.getInspectDate() != null ? doc.getInspectDate() : doc.getOutDate())
                    .setSamplingLocation(doc.getSamplingLocation()).setCertificateNo(doc.getCertificateNo())
                    .setExecuteStandard(doc.getExecuteStandard())
                    .setInspectorUserId(doc.getInspectorUserId()).setAuditUserId(doc.getAuditUserId())
                    .setApproveUserId(doc.getApproveUserId()).setCheckResult(doc.getCheckResult())
                    .setRemark(doc.getRemark());
            List<MesQcOqcLineDO> lines = oqcLineService.getOqcLineListByOqcId(qcId);
            for (MesQcOqcLineDO line : lines) {
                lineIndicatorIds.add(line.getIndicatorId());
                lineInfoMap.putIfAbsent(line.getIndicatorId(), new LineInfo(line.getCheckMethod(),
                        line.getStandardValue(), line.getMinThreshold(), line.getMaxThreshold()));
            }
        } else if (Objects.equals(qcType, MesQcTypeEnum.RQC.getType())) {
            MesQcRqcDO doc = rqcService.validateRqcExists(qcId);
            itemId = doc.getItemId();
            report.setDocCode(doc.getCode()).setDocName(doc.getName())
                    .setBatchNo(doc.getBatchCode()).setBatchQuantity(doc.getCheckQuantity())
                    .setSamplingTime(doc.getInspectDate())
                    .setSamplingLocation(doc.getSamplingLocation()).setCertificateNo(doc.getCertificateNo())
                    .setExecuteStandard(doc.getExecuteStandard())
                    .setInspectorUserId(doc.getInspectorUserId()).setAuditUserId(doc.getAuditUserId())
                    .setApproveUserId(doc.getApproveUserId()).setCheckResult(doc.getCheckResult())
                    .setRemark(doc.getRemark());
            List<MesQcRqcLineDO> lines = rqcLineService.getRqcLineListByRqcId(qcId);
            for (MesQcRqcLineDO line : lines) {
                lineIndicatorIds.add(line.getIndicatorId());
                lineInfoMap.putIfAbsent(line.getIndicatorId(), new LineInfo(line.getCheckMethod(),
                        line.getStandardValue(), line.getMinThreshold(), line.getMaxThreshold()));
            }
        } else {
            throw new IllegalArgumentException("暂不支持 qcType=" + qcType);
        }
        // 2. æ ·å“åˆ—表与选中的检验结果
        List<MesQcIndicatorResultDO> results = resultService.getIndicatorResultListByQcIdAndType(qcId, qcType);
        Long effectiveResultId = resultId;
        if (effectiveResultId == null && CollUtil.isNotEmpty(results)) {
            effectiveResultId = results.stream()
                    .min(Comparator.comparing(MesQcIndicatorResultDO::getId))
                    .map(MesQcIndicatorResultDO::getId).orElse(null);
        }
        // 3. æŒ‡æ ‡ï¼ˆå«çˆ¶åˆ†ç»„自动补全)
        Set<Long> indicatorIds = new HashSet<>(lineIndicatorIds);
        Map<Long, MesQcIndicatorDO> indicatorMap = indicatorService.getIndicatorMap(indicatorIds);
        Set<Long> missingParentIds = indicatorMap.values().stream()
                .filter(ind -> ind.getParentId() != null && ind.getParentId() > 0 && !indicatorMap.containsKey(ind.getParentId()))
                .map(MesQcIndicatorDO::getParentId).collect(Collectors.toSet());
        if (CollUtil.isNotEmpty(missingParentIds)) {
            indicatorMap.putAll(indicatorService.getIndicatorMap(missingParentIds));
        }
        // 4. æ£€éªŒç»“果明细(按 indicatorId åˆ†ç»„、measureNo å‡åºï¼‰
        Map<Long, List<MesQcIndicatorResultDetailDO>> detailMap = Collections.emptyMap();
        if (effectiveResultId != null) {
            List<MesQcIndicatorResultDetailDO> details =
                    resultService.getIndicatorResultDetailListByResultId(effectiveResultId);
            detailMap = details.stream().collect(Collectors.groupingBy(MesQcIndicatorResultDetailDO::getIndicatorId));
        }
        // 5. ç»„装检验项树
        List<MesQcQcReportRespVO.ReportItem> roots = buildReportItems(indicatorMap, lineInfoMap, detailMap, report);
        report.setItems(roots);
        // 6. è¡¨å¤´å…³è”数据:物料、人员
        if (itemId != null) {
            MesMdItemDO item = itemService.getItemMap(Collections.singleton(itemId)).get(itemId);
            if (item != null) {
                report.setItemCode(item.getCode()).setItemName(item.getName())
                        .setItemSpecification(item.getSpecification());
            }
        }
        Set<Long> userIds = new HashSet<>();
        userIds.add(report.getInspectorUserId());
        userIds.add(report.getAuditUserId());
        userIds.add(report.getApproveUserId());
        userIds.removeIf(Objects::isNull);
        if (CollUtil.isNotEmpty(userIds)) {
            Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
            if (report.getInspectorUserId() != null && userMap.get(report.getInspectorUserId()) != null) {
                report.setInspectorName(userMap.get(report.getInspectorUserId()).getNickname());
            }
            if (report.getAuditUserId() != null && userMap.get(report.getAuditUserId()) != null) {
                report.setAuditName(userMap.get(report.getAuditUserId()).getNickname());
            }
            if (report.getApproveUserId() != null && userMap.get(report.getApproveUserId()) != null) {
                report.setApproveName(userMap.get(report.getApproveUserId()).getNickname());
            }
        }
        return report;
    }
    /**
     * ç»„装报告检验项树;顺带收集全局 measureNos åˆ—头
     */
    private List<MesQcQcReportRespVO.ReportItem> buildReportItems(
            Map<Long, MesQcIndicatorDO> indicatorMap,
            Map<Long, LineInfo> lineInfoMap,
            Map<Long, List<MesQcIndicatorResultDetailDO>> detailMap,
            MesQcQcReportRespVO report) {
        Map<Long, MesQcQcReportRespVO.ReportItem> nodeMap = new LinkedHashMap<>();
        Set<Integer> measureNoSet = new HashSet<>();
        for (Long indicatorId : indicatorMap.keySet()) {
            MesQcIndicatorDO indicator = indicatorMap.get(indicatorId);
            MesQcQcReportRespVO.ReportItem node = new MesQcQcReportRespVO.ReportItem();
            node.setIndicatorId(indicator.getId());
            node.setIndicatorCode(indicator.getCode());
            node.setIndicatorName(indicator.getName());
            node.setParentId(Objects.requireNonNullElse(indicator.getParentId(), 0L));
            node.setItemType(Objects.requireNonNullElse(indicator.getItemType(),
                    MesQcIndicatorItemTypeEnum.RECORD.getType()));
            node.setFormulaText(indicator.getFormulaText());
            node.setUnitText(indicator.getUnitText());
            node.setSortOrder(Objects.requireNonNullElse(indicator.getSortOrder(), 0));
            LineInfo lineInfo = lineInfoMap.get(indicator.getId());
            if (lineInfo != null) {
                node.setCheckMethod(lineInfo.checkMethod);
                node.setStandardValue(lineInfo.standardValue);
                node.setThresholdMin(lineInfo.thresholdMin);
                node.setThresholdMax(lineInfo.thresholdMax);
            }
            List<MesQcIndicatorResultDetailDO> details = detailMap.getOrDefault(indicator.getId(), Collections.emptyList());
            List<MesQcQcReportRespVO.ReportMeasure> measures = new ArrayList<>(details.size());
            details.stream()
                    .sorted(Comparator.comparing(d -> Objects.requireNonNullElse(d.getMeasureNo(), 1)))
                    .forEach(d -> {
                        MesQcQcReportRespVO.ReportMeasure measure = new MesQcQcReportRespVO.ReportMeasure();
                        measure.setMeasureNo(Objects.requireNonNullElse(d.getMeasureNo(), 1));
                        measure.setValue(d.getValue());
                        measure.setRemark(d.getRemark());
                        measures.add(measure);
                        measureNoSet.add(measure.getMeasureNo());
                    });
            node.setMeasures(measures);
            nodeMap.put(indicatorId, node);
        }
        // æŒ‚树:父不在集合内的子项提升为顶级
        List<MesQcQcReportRespVO.ReportItem> roots = new ArrayList<>();
        Comparator<MesQcQcReportRespVO.ReportItem> byOrder =
                Comparator.comparing(MesQcQcReportRespVO.ReportItem::getSortOrder)
                        .thenComparing(MesQcQcReportRespVO.ReportItem::getIndicatorId);
        for (MesQcQcReportRespVO.ReportItem node : nodeMap.values()) {
            MesQcQcReportRespVO.ReportItem parent = node.getParentId() > 0 ? nodeMap.get(node.getParentId()) : null;
            if (parent == null) {
                roots.add(node);
            } else {
                if (parent.getChildren() == null) {
                    parent.setChildren(new ArrayList<>());
                }
                parent.getChildren().add(node);
            }
        }
        roots.sort(byOrder);
        roots.forEach(root -> {
            if (root.getChildren() != null) {
                root.getChildren().sort(byOrder);
            }
        });
        // åˆ—头:至少一列
        if (measureNoSet.isEmpty()) {
            report.setMeasureNos(Collections.singletonList(1));
        } else {
            report.setMeasureNos(new ArrayList<>(measureNoSet).stream().sorted().collect(Collectors.toList()));
        }
        return roots;
    }
    /**
     * æ£€éªŒå•行的判定信息(四类 line çš„公共字段快照)
     */
    private record LineInfo(String checkMethod, BigDecimal standardValue, BigDecimal thresholdMin, BigDecimal thresholdMax) {
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ipqc/MesQcIpqcServiceImpl.java
@@ -3,6 +3,7 @@
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceLineDO;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum;
@@ -16,6 +17,7 @@
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defectrecord.MesQcDefectRecordDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.ipqc.MesQcIpqcDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateItemDO;
import cn.iocoder.yudao.module.mes.dal.mysql.qc.ipqc.MesQcIpqcMapper;
import cn.iocoder.yudao.module.mes.enums.qc.MesQcDefectLevelEnum;
@@ -33,6 +35,7 @@
import cn.iocoder.yudao.module.mes.service.qc.defectrecord.MesQcDefectRecordService;
import cn.iocoder.yudao.module.mes.service.qc.indicatorresult.MesQcIndicatorResultService;
import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateItemService;
import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateService;
import cn.iocoder.yudao.module.mes.controller.admin.qc.ncr.vo.MesQcNcrSaveReqVO;
import cn.iocoder.yudao.module.mes.service.qc.ncr.MesQcNcrService;
import cn.iocoder.yudao.module.mes.service.wm.productproduce.MesWmProductProduceLineService;
@@ -75,6 +78,9 @@
    @Resource
    private MesQcTemplateItemService templateItemService;
    @Resource
    @Lazy
    private MesQcTemplateService templateService;
    @Resource
    private MesQcIpqcLineService ipqcLineService;
    @Resource
@@ -151,6 +157,13 @@
                .setTemplateId(templateId)
                .setSourceDocCode(sourceDocCode)
                .setStatus(MesQcStatusEnum.DRAFT.getStatus());
        // æ‰§è¡Œæ ‡å‡†ï¼šç”¨æˆ·æœªå¡«æ—¶å›žé€€æ¨¡æ¿é»˜è®¤å€¼
        if (StrUtil.isBlank(ipqc.getExecuteStandard())) {
            MesQcTemplateDO template = templateService.getTemplate(templateId);
            if (template != null) {
                ipqc.setExecuteStandard(template.getDefaultExecuteStandard());
            }
        }
        ipqcMapper.insert(ipqc);
        // 3. ä»Žæ¨¡æ¿æŒ‡æ ‡è‡ªåŠ¨ç”Ÿæˆæ£€éªŒè¡Œ
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImpl.java
@@ -2,6 +2,7 @@
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.hutool.core.util.ObjectUtil;
@@ -9,6 +10,7 @@
import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defectrecord.MesQcDefectRecordDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.arrivalnotice.MesWmArrivalNoticeLineDO;
import cn.iocoder.yudao.module.mes.dal.mysql.qc.iqc.MesQcIqcMapper;
@@ -20,6 +22,7 @@
import cn.iocoder.yudao.module.mes.service.qc.defectrecord.MesQcDefectRecordService;
import cn.iocoder.yudao.module.mes.service.qc.indicatorresult.MesQcIndicatorResultService;
import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateItemService;
import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateService;
import cn.iocoder.yudao.module.mes.service.wm.arrivalnotice.MesWmArrivalNoticeService;
import cn.iocoder.yudao.module.mes.service.wm.outsourcereceipt.MesWmOutsourceReceiptService;
import cn.iocoder.yudao.module.mes.controller.admin.qc.ncr.vo.MesQcNcrSaveReqVO;
@@ -57,6 +60,9 @@
    @Resource
    private MesQcTemplateItemService templateItemService;
    @Resource
    @Lazy
    private MesQcTemplateService templateService;
    @Resource
    private MesQcIqcLineService iqcLineService;
    @Resource
@@ -114,6 +120,13 @@
                .setCheckQuantity(createReqVO.getQualifiedQuantity().add(createReqVO.getUnqualifiedQuantity()))
                .setSourceDocCode(sourceDocCode)
                .setStatus(MesQcStatusEnum.DRAFT.getStatus());
        // æ‰§è¡Œæ ‡å‡†ï¼šç”¨æˆ·æœªå¡«æ—¶å›žé€€æ¨¡æ¿é»˜è®¤å€¼
        if (StrUtil.isBlank(iqc.getExecuteStandard())) {
            MesQcTemplateDO template = templateService.getTemplate(templateId);
            if (template != null) {
                iqc.setExecuteStandard(template.getDefaultExecuteStandard());
            }
        }
        iqcMapper.insert(iqc);
        // 3. ä»Žæ¨¡æ¿æŒ‡æ ‡è‡ªåŠ¨ç”Ÿæˆæ£€éªŒè¡Œ
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/oqc/MesQcOqcServiceImpl.java
@@ -2,12 +2,14 @@
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.qc.oqc.vo.MesQcOqcPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.qc.oqc.vo.MesQcOqcSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defectrecord.MesQcDefectRecordDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.oqc.MesQcOqcDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateItemDO;
import cn.iocoder.yudao.module.mes.dal.mysql.qc.oqc.MesQcOqcMapper;
import cn.iocoder.yudao.module.mes.enums.qc.MesQcDefectLevelEnum;
@@ -17,6 +19,7 @@
import cn.iocoder.yudao.module.mes.service.qc.defectrecord.MesQcDefectRecordService;
import cn.iocoder.yudao.module.mes.service.qc.indicatorresult.MesQcIndicatorResultService;
import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateItemService;
import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateService;
import cn.iocoder.yudao.module.mes.service.wm.productsales.MesWmProductSalesLineService;
import cn.iocoder.yudao.module.mes.controller.admin.qc.ncr.vo.MesQcNcrSaveReqVO;
import cn.iocoder.yudao.module.mes.service.qc.ncr.MesQcNcrService;
@@ -55,6 +58,9 @@
    @Resource
    private MesQcTemplateItemService templateItemService;
    @Resource
    @Lazy
    private MesQcTemplateService templateService;
    @Resource
    private MesQcOqcLineService oqcLineService;
    @Resource
@@ -111,6 +117,13 @@
                .setTemplateId(templateId)
                .setSourceDocCode(sourceDocCode)
                .setStatus(MesQcStatusEnum.DRAFT.getStatus());
        // æ‰§è¡Œæ ‡å‡†ï¼šç”¨æˆ·æœªå¡«æ—¶å›žé€€æ¨¡æ¿é»˜è®¤å€¼
        if (StrUtil.isBlank(oqc.getExecuteStandard())) {
            MesQcTemplateDO template = templateService.getTemplate(templateId);
            if (template != null) {
                oqc.setExecuteStandard(template.getDefaultExecuteStandard());
            }
        }
        oqcMapper.insert(oqc);
        // 3. ä»Žæ¨¡æ¿æŒ‡æ ‡è‡ªåŠ¨ç”Ÿæˆæ£€éªŒè¡Œ
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/rqc/MesQcRqcServiceImpl.java
@@ -2,12 +2,14 @@
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.qc.rqc.vo.MesQcRqcPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.qc.rqc.vo.MesQcRqcSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defectrecord.MesQcDefectRecordDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.rqc.MesQcRqcDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateItemDO;
import cn.iocoder.yudao.module.mes.dal.mysql.qc.rqc.MesQcRqcMapper;
import cn.iocoder.yudao.module.mes.enums.qc.MesQcStatusEnum;
@@ -20,6 +22,7 @@
import cn.iocoder.yudao.module.mes.service.wm.returnissue.MesWmReturnIssueService;
import cn.iocoder.yudao.module.mes.service.wm.returnsales.MesWmReturnSalesService;
import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateItemService;
import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateService;
import cn.iocoder.yudao.module.mes.service.wm.returnissue.MesWmReturnIssueLineService;
import cn.iocoder.yudao.module.mes.service.wm.returnsales.MesWmReturnSalesLineService;
import cn.iocoder.yudao.module.mes.controller.admin.qc.ncr.vo.MesQcNcrSaveReqVO;
@@ -54,6 +57,9 @@
    @Resource
    private MesQcTemplateItemService templateItemService;
    @Resource
    @Lazy
    private MesQcTemplateService templateService;
    @Resource
    private MesQcRqcLineService rqcLineService;
    @Resource
@@ -115,6 +121,13 @@
                .setTemplateId(templateId)
                .setSourceDocCode(sourceDocCode)
                .setStatus(MesQcStatusEnum.DRAFT.getStatus());
        // æ‰§è¡Œæ ‡å‡†ï¼šç”¨æˆ·æœªå¡«æ—¶å›žé€€æ¨¡æ¿é»˜è®¤å€¼
        if (StrUtil.isBlank(rqc.getExecuteStandard())) {
            MesQcTemplateDO template = templateService.getTemplate(templateId);
            if (template != null) {
                rqc.setExecuteStandard(template.getDefaultExecuteStandard());
            }
        }
        rqcMapper.insert(rqc);
        // 3. ä»Žæ¨¡æ¿æŒ‡æ ‡è‡ªåŠ¨ç”Ÿæˆæ£€éªŒè¡Œ