liyong
5 天以前 536774eea8902efc088c5b904ff75bbe0af33418
工作台
已添加16个文件
已修改43个文件
3334 ■■■■■ 文件已修改
.claude/settings.local.json 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/mes_auto_code_rule_init.sql 255 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/mes_compliance_gap_analysis.md 242 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/product_issue_task_integration.md 255 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/production_data_cleanup.md 244 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/production_feedback_integration.md 599 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/work_order_param_record_integration.md 413 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/item/dto/MdmItemSyncReqDTO.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/item/vo/MdmItemSaveReqVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/item/MdmItemServiceImpl.java 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/item/dto/MesMdItemSyncReqDTO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/api/item/MesMdItemApiImpl.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/item/vo/MesMdItemSaveReqVO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/feedback/vo/MesProFeedbackPageReqVO.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/feedback/vo/MesProFeedbackSaveReqVO.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderParamRecordController.java 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/paramrecord/MesProWorkOrderParamRecordPageReqVO.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/paramrecord/MesProWorkOrderParamRecordRespVO.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/paramrecord/MesProWorkOrderParamRecordSaveReqVO.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productissue/MesWmProductIssueController.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productissue/MesWmProductIssueLineController.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productissue/vo/MesWmProductIssueRespVO.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productissue/vo/MesWmProductIssueSaveReqVO.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productissue/vo/line/MesWmProductIssueLineRespVO.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productissue/vo/line/MesWmProductIssueLineSaveReqVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnissue/MesWmReturnIssueController.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnissue/MesWmReturnIssueLineController.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnissue/vo/MesWmReturnIssuePageReqVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnissue/vo/MesWmReturnIssueRespVO.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnissue/vo/MesWmReturnIssueSaveReqVO.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnissue/vo/line/MesWmReturnIssueLineRespVO.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnissue/vo/line/MesWmReturnIssueLineSaveReqVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/md/item/MesMdItemDO.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkOrderParamRecordDO.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkOrderParamRecordDetailDO.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productissue/MesWmProductIssueDO.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productissue/MesWmProductIssueLineDO.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/returnissue/MesWmReturnIssueDO.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/returnissue/MesWmReturnIssueLineDO.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/md/item/MesMdItemTypeMapper.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/feedback/MesProFeedbackMapper.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/task/MesProTaskIssueMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/workorder/MesProWorkOrderParamRecordDetailMapper.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/workorder/MesProWorkOrderParamRecordMapper.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productissue/MesWmProductIssueLineMapper.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productissue/MesWmProductIssueMapper.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/returnissue/MesWmReturnIssueMapper.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdItemServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/mdm/MesMdmItemService.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/mdm/MesMdmItemServiceImpl.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskIssueService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskIssueServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderParamRecordService.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderParamRecordServiceImpl.java 204 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueLineServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueServiceImpl.java 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueServiceImpl.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.claude/settings.local.json
@@ -31,7 +31,11 @@
      "Bash(/tmp/fix_controller.sh)",
      "Bash(chmod +x /tmp/fix2.sh)",
      "Bash(/tmp/fix2.sh)",
      "Bash(\"E:/soft/IntelliJ IDEA 2025.3.4/plugins/maven/lib/maven3/bin/mvn.cmd\" -DskipTests=true -q compile -pl yudao-module-mdm-api,yudao-module-mdm,yudao-module-mes -am)"
      "Bash(\"E:/soft/IntelliJ IDEA 2025.3.4/plugins/maven/lib/maven3/bin/mvn.cmd\" -DskipTests=true -q compile -pl yudao-module-mdm-api,yudao-module-mdm,yudao-module-mes -am)",
      "Bash(JAVA_HOME=\"C:/Program Files/Java/jdk-25\" mvn compile -pl yudao-module-mes -am -q -Dsource=25)",
      "Read(//c/Program Files/Java//**)",
      "Read(//d/Program Files/**)",
      "Bash(where java *)"
    ],
    "deny": [
      "Bash(rm -rf *)",
docs/mes_auto_code_rule_init.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,255 @@
-- ============================================================
-- MES ç¼–码规则初始化数据
-- åŒ…含:mes_md_auto_code_rule(规则主表)+ mes_md_auto_code_part(规则分段组成)
-- åˆ†æ®µç±»åž‹ï¼š1=输入字符, 2=当前日期, 3=固定字符, 4=流水号
-- å¾ªçŽ¯æ–¹å¼ï¼š1=按年, 2=按月, 3=按天, 4=按小时, 5=按分钟, 10=按传入字符
-- è¡¥é½æ–¹å¼ï¼š1=左补齐, 2=右补齐
-- ============================================================
-- ==================== 1. ç¼–码规则主表 ====================
INSERT INTO mes_md_auto_code_rule (id, code, name, description, max_length, padded, padded_char, padded_method, status, remark, creator, updater) VALUES
-- åŸºç¡€æ•°æ®
(1,  'MD_ITEM_TYPE_CODE',      '物料分类编码',     '物料分类自动编码',       6,  1, '0', 1, 0, NULL, '1', '1'),
(2,  'MD_ITEM_CODE',           '物料编码',         '物料自动编码',           17, 1, '0', 1, 0, NULL, '1', '1'),
(3,  'MD_VENDOR_CODE',         '供应商编码',       '供应商自动编码',         15, 1, '0', 1, 0, NULL, '1', '1'),
(4,  'MD_CLIENT_CODE',         '客户编码',         '客户自动编码',           15, 1, '0', 1, 0, NULL, '1', '1'),
(5,  'MD_WORKSTATION_CODE',    '工作站编码',       '工作站自动编码',         6,  1, '0', 1, 0, NULL, '1', '1'),
(6,  'MD_WORKSHOP_CODE',       '车间编码',         '车间自动编码',           6,  1, '0', 1, 0, NULL, '1', '1'),
(7,  'TM_TOOL_TYPE_CODE',      '工具类型编码',     '工具类型自动编码',       6,  1, '0', 1, 0, NULL, '1', '1'),
-- ä»“储物流
(10, 'WM_WAREHOUSE_CODE',      '仓库编码',         '仓库自动编码',           6,  1, '0', 1, 0, NULL, '1', '1'),
(11, 'WM_LOCATION_CODE',       '库区编码',         '库区自动编码',           6,  1, '0', 1, 0, NULL, '1', '1'),
(12, 'WM_AREA_CODE',           '库位编码',         '库位自动编码',           6,  1, '0', 1, 0, NULL, '1', '1'),
(13, 'WM_ARRIVAL_NOTICE_CODE', '到货通知单编码',   '到货通知单自动编码',     15, 1, '0', 1, 0, NULL, '1', '1'),
(14, 'WM_ITEM_RECEIPT_CODE',   '采购入库单编码',   '采购入库单自动编码',     15, 1, '0', 1, 0, NULL, '1', '1'),
(15, 'WM_RETURN_VENDOR_CODE',  '采购退货单编码',   '采购退货单自动编码',     15, 1, '0', 1, 0, NULL, '1', '1'),
(16, 'WM_RETURN_ISSUE_CODE',   '生产退料单编码',   '生产退料单自动编码',     15, 1, '0', 1, 0, NULL, '1', '1'),
(17, 'WM_SN_CODE',             'SN码',             'SN码自动编码',           18, 1, '0', 1, 0, NULL, '1', '1'),
(18, 'WM_PACKAGE_CODE',        '装箱单编码',       '装箱单自动编码',         17, 1, '0', 1, 0, NULL, '1', '1'),
(19, 'WM_BATCH_CODE',          '批次编码',         '批次自动编码',           17, 1, '0', 1, 0, NULL, '1', '1'),
(20, 'WM_PRODUCT_SALES_CODE',  '销售出库单编码',   '销售出库单自动编码',     15, 1, '0', 1, 0, NULL, '1', '1'),
(21, 'WM_MISC_RECEIPT_CODE',   '杂项入库单编码',   '杂项入库单自动编码',     15, 1, '0', 1, 0, NULL, '1', '1'),
(22, 'WM_MISC_ISSUE_CODE',     '杂项出库单编码',   '杂项出库单自动编码',     15, 1, '0', 1, 0, NULL, '1', '1'),
(23, 'WM_STOCK_TAKING_PLAN_CODE','盘点方案编码',   '盘点方案自动编码',       15, 1, '0', 1, 0, NULL, '1', '1'),
(24, 'WM_STOCK_TAKING_CODE',   '盘点任务编码',     '盘点任务自动编码',       15, 1, '0', 1, 0, NULL, '1', '1'),
(25, 'TRANSFER_CODE',          '转移调拨单编码',   '转移调拨单自动编码',     15, 1, '0', 1, 0, NULL, '1', '1'),
(26, 'WM_OUTSOURCE_ISSUE_CODE','外协发料单编码',   '外协发料单自动编码',     15, 1, '0', 1, 0, NULL, '1', '1'),
(27, 'WM_OUTSOURCE_RECEIPT_CODE','外协入库单编码', '外协入库单自动编码',     15, 1, '0', 1, 0, NULL, '1', '1'),
(28, 'WM_SALES_NOTICE_CODE',   '发货通知单编码',   '发货通知单自动编码',     15, 1, '0', 1, 0, NULL, '1', '1'),
(29, 'WM_RETURN_SALES_CODE',   '销售退货单编码',   '销售退货单自动编码',     15, 1, '0', 1, 0, NULL, '1', '1'),
-- ç”Ÿäº§
(30, 'PRO_WORK_ORDER_CODE',    '生产工单编码',     '生产工单自动编码',       15, 1, '0', 1, 0, NULL, '1', '1'),
(31, 'PRO_TASK_CODE',          '生产任务编码',     '生产任务自动编码',       15, 1, '0', 1, 0, NULL, '1', '1'),
(32, 'PRO_ROUTE_CODE',         '工艺路线编码',     '工艺路线自动编码',       15, 1, '0', 1, 0, NULL, '1', '1'),
(33, 'PRO_FEEDBACK_CODE',      '生产报工单编码',   '生产报工单自动编码',     15, 1, '0', 1, 0, NULL, '1', '1'),
-- è´¨æ£€
(40, 'QC_IQC_CODE',            '来料检验单编码',   '来料检验单自动编码',     16, 1, '0', 1, 0, NULL, '1', '1'),
(41, 'QC_IPQC_CODE',           '过程检验单编码',   '过程检验单自动编码',     17, 1, '0', 1, 0, NULL, '1', '1'),
(42, 'QC_OQC_CODE',            '出货检验单编码',   '出货检验单自动编码',     17, 1, '0', 1, 0, NULL, '1', '1'),
(43, 'QC_RQC_CODE',            '退货检验单编码',   '退货检验单自动编码',     17, 1, '0', 1, 0, NULL, '1', '1'),
(44, 'QC_DEFECT_CODE',         '缺陷类型编码',     '缺陷类型自动编码',       6,  1, '0', 1, 0, NULL, '1', '1'),
(45, 'QC_INDICATOR_RESULT_CODE','样品检验结果编码', '样品检验结果自动编码',   15, 1, '0', 1, 0, NULL, '1', '1'),
-- è®¾å¤‡
(50, 'DV_MACHINERY_CODE',      '设备编码',         '设备自动编码',           15, 1, '0', 1, 0, NULL, '1', '1'),
(51, 'DV_MACHINERY_TYPE_CODE', '设备类型编码',     '设备类型自动编码',       6,  1, '0', 1, 0, NULL, '1', '1'),
(52, 'DV_SUBJECT_CODE',        '点检保养项目编码', '点检保养项目自动编码',   6,  1, '0', 1, 0, NULL, '1', '1'),
(53, 'DV_REPAIR_CODE',         '维修单编码',       '维修单自动编码',         15, 1, '0', 1, 0, NULL, '1', '1'),
-- å·¥è‰ºæ–‡æ¡£
(60, 'PD_DOCUMENT_AUDIT_CODE', '资料审核单编码',   '资料审核单自动编码',     15, 1, '0', 1, 0, NULL, '1', '1'),
(61, 'PD_ARCHIVE_CODE',        '归档台账编码',     '归档台账自动编码',       15, 1, '0', 1, 0, NULL, '1', '1'),
(62, 'PD_PROCESS_PARAM_CODE',  '工艺参数编码',     '工艺参数自动编码',       15, 1, '0', 1, 0, NULL, '1', '1'),
(63, 'PD_PROCESS_PARAM_TEMPLATE_CODE', '工艺参数模板编码', '工艺参数模板自动编码', 15, 1, '0', 1, 0, NULL, '1', '1');
-- ==================== 2. ç¼–码规则分段组成 ====================
--
-- é•¿åº¦å¯¹ç…§ï¼š
--   å›ºå®šå­—符(IT_) = 3, æ—¥æœŸ(yyyyMMdd) = 8, æµæ°´å·(001) = 3
--   ä¾‹: IT_001 = 3+3 = 6        â†’ max_length=6
--   ä¾‹: ITEM_20260716_001 = 6+8+3 = 17  â†’ max_length=17
--   ä¾‹: WO_20260716_001 = 3+8+3 = 14    â†’ max_length=15 (留余量)
--
INSERT INTO mes_md_auto_code_part (rule_id, sort, type, length, date_format, fix_character, serial_start_no, serial_step, cycle_flag, cycle_method, remark, creator, updater) VALUES
-- ===== åŸºç¡€æ•°æ® =====
-- 1. ç‰©æ–™åˆ†ç±»ï¼šIT_001 = 3+3=6
(1,  1, 3, 3,  NULL,       'IT_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(1,  2, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 2. ç‰©æ–™ï¼šITEM_20260716001 = 6+8+3=17
(2,  1, 3, 6,  NULL,       'ITEM_', NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(2,  2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(2,  3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 3. ä¾›åº”商:VND_20260716001 = 4+8+3=15
(3,  1, 3, 4,  NULL,       'VND_',  NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(3,  2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(3,  3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 4. å®¢æˆ·ï¼šCLT_20260716001 = 4+8+3=15
(4,  1, 3, 4,  NULL,       'CLT_',  NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(4,  2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(4,  3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 5. å·¥ä½œç«™ï¼šWS_001 = 3+3=6
(5,  1, 3, 3,  NULL,       'WS_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(5,  2, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 6. è½¦é—´ï¼šWP_001 = 3+3=6
(6,  1, 3, 3,  NULL,       'WP_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(6,  2, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 7. å·¥å…·ç±»åž‹ï¼šTL_001 = 3+3=6
(7,  1, 3, 3,  NULL,       'TL_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(7,  2, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- ===== ä»“储物流 =====
-- 10. ä»“库:WH_001 = 3+3=6
(10, 1, 3, 3,  NULL,       'WH_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(10, 2, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 11. åº“区:LC_001 = 3+3=6
(11, 1, 3, 3,  NULL,       'LC_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(11, 2, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 12. åº“位:AR_001 = 3+3=6
(12, 1, 3, 3,  NULL,       'AR_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(12, 2, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 13. åˆ°è´§é€šçŸ¥ï¼šAN_20260716001 = 3+8+3=14→15
(13, 1, 3, 3,  NULL,       'AN_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(13, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(13, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 14. é‡‡è´­å…¥åº“:IR_20260716001 = 3+8+3=14→15
(14, 1, 3, 3,  NULL,       'IR_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(14, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(14, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 15. é‡‡è´­é€€è´§ï¼šRV_20260716001 = 3+8+3=14→15
(15, 1, 3, 3,  NULL,       'RV_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(15, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(15, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 16. ç”Ÿäº§é€€æ–™ï¼šRI_20260716001 = 3+8+3=14→15
(16, 1, 3, 3,  NULL,       'RI_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(16, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(16, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 17. SN码:SN_20260716000001 = 3+8+4+3=18
(17, 1, 3, 3,  NULL,       'SN_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(17, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(17, 3, 4, 4,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 18. è£…箱:PK_20260716001 = 3+8+3=14→17
(18, 1, 3, 3,  NULL,       'PK_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(18, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(18, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 19. æ‰¹æ¬¡ï¼šBT_20260716001 = 3+8+3=14→17
(19, 1, 3, 3,  NULL,       'BT_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(19, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(19, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 20. é”€å”®å‡ºåº“:SL_20260716001 = 3+8+3=14→15
(20, 1, 3, 3,  NULL,       'SL_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(20, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(20, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 21. æ‚项入库:MI_20260716001 = 3+8+3=14→15
(21, 1, 3, 3,  NULL,       'MI_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(21, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(21, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 22. æ‚项出库:MO_20260716001 = 3+8+3=14→15
(22, 1, 3, 3,  NULL,       'MO_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(22, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(22, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 23. ç›˜ç‚¹æ–¹æ¡ˆï¼šSP_20260716001 = 3+8+3=14→15
(23, 1, 3, 3,  NULL,       'SP_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(23, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(23, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 24. ç›˜ç‚¹ä»»åŠ¡ï¼šST_20260716001 = 3+8+3=14→15
(24, 1, 3, 3,  NULL,       'ST_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(24, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(24, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 25. è°ƒæ‹¨ï¼šTF_20260716001 = 3+8+3=14→15
(25, 1, 3, 3,  NULL,       'TF_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(25, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(25, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 26. å¤–协发料:OI_20260716001 = 3+8+3=14→15
(26, 1, 3, 3,  NULL,       'OI_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(26, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(26, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 27. å¤–协入库:OR_20260716001 = 3+8+3=14→15
(27, 1, 3, 3,  NULL,       'OR_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(27, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(27, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 28. å‘货通知:SN_20260716001 = 3+8+3=14→15
(28, 1, 3, 3,  NULL,       'SN_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(28, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(28, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 29. é”€å”®é€€è´§ï¼šRS_20260716001 = 3+8+3=14→15
(29, 1, 3, 3,  NULL,       'RS_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(29, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(29, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- ===== ç”Ÿäº§ =====
-- 30. å·¥å•:WO_20260716001 = 3+8+3=14→15
(30, 1, 3, 3,  NULL,       'WO_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(30, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(30, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 31. ä»»åŠ¡ï¼šTK_20260716001 = 3+8+3=14→15
(31, 1, 3, 3,  NULL,       'TK_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(31, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(31, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 32. å·¥è‰ºè·¯çº¿ï¼šRT_20260716001 = 3+8+3=14→15
(32, 1, 3, 3,  NULL,       'RT_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(32, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(32, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 33. æŠ¥å·¥ï¼šFB_20260716001 = 3+8+3=14→15
(33, 1, 3, 3,  NULL,       'FB_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(33, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(33, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- ===== è´¨æ£€ =====
-- 40. æ¥æ–™æ£€éªŒï¼šIQC_20260716001 = 4+8+3=15→16
(40, 1, 3, 4,  NULL,       'IQC_',  NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(40, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(40, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 41. è¿‡ç¨‹æ£€éªŒï¼šIPQC_20260716001 = 5+8+3=16→17
(41, 1, 3, 5,  NULL,       'IPQC_', NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(41, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(41, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 42. å‡ºè´§æ£€éªŒï¼šOQC_20260716001 = 4+8+3=15→17
(42, 1, 3, 4,  NULL,       'OQC_',  NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(42, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(42, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 43. é€€è´§æ£€éªŒï¼šRQC_20260716001 = 4+8+3=15→17
(43, 1, 3, 4,  NULL,       'RQC_',  NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(43, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(43, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 44. ç¼ºé™·ï¼šDF_001 = 3+3=6
(44, 1, 3, 3,  NULL,       'DF_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(44, 2, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 45. æ£€éªŒç»“果:IR_20260716001 = 3+8+3=14→15
(45, 1, 3, 3,  NULL,       'IR_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(45, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(45, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- ===== è®¾å¤‡ =====
-- 50. è®¾å¤‡ï¼šMC_20260716001 = 3+8+3=14→15
(50, 1, 3, 3,  NULL,       'MC_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(50, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(50, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 51. è®¾å¤‡ç±»åž‹ï¼šMT_001 = 3+3=6
(51, 1, 3, 3,  NULL,       'MT_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(51, 2, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 52. ç‚¹æ£€ä¿å…»ï¼šSJ_001 = 3+3=6
(52, 1, 3, 3,  NULL,       'SJ_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(52, 2, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 53. ç»´ä¿®ï¼šRP_20260716001 = 3+8+3=14→15
(53, 1, 3, 3,  NULL,       'RP_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(53, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(53, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- ===== å·¥è‰ºæ–‡æ¡£ =====
-- 60. èµ„料审核:DA_20260716001 = 3+8+3=14→15
(60, 1, 3, 3,  NULL,       'DA_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(60, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(60, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 61. å½’档:AC_20260716001 = 3+8+3=14→15
(61, 1, 3, 3,  NULL,       'AC_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(61, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(61, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 62. å·¥è‰ºå‚数:PP_20260716001 = 3+8+3=14→15
(62, 1, 3, 3,  NULL,       'PP_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(62, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(62, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1'),
-- 63. å·¥è‰ºå‚数模板:PT_20260716001 = 3+8+3=14→15
(63, 1, 3, 3,  NULL,       'PT_',   NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(63, 2, 2, 8,  'yyyyMMdd', NULL,    NULL, NULL, NULL,  NULL, NULL, '1', '1'),
(63, 3, 4, 3,  NULL,       NULL,    1,    1,    1,    3,    NULL, '1', '1');
docs/mes_compliance_gap_analysis.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,242 @@
# MES ç”Ÿäº§ç®¡æŽ§æ ¸æŸ¥ - åŠŸèƒ½ç¬¦åˆæ€§åˆ†æž
## æ ¸æŸ¥è¦æ±‚与现有功能对照
---
### è¦æ±‚1:信息化系统部署与运行
> æ ¸æŸ¥ä¼ä¸šæ˜¯å¦éƒ¨ç½²ç”Ÿäº§ç®¡æŽ§ç›¸å…³ä¿¡æ¯åŒ–系统,如MES制造执行系统、生产管理专用模块等,确认系统已正式投入使用且处于正常运行状态。
**符合度:✅ ç¬¦åˆ**
| æ ¸æŸ¥é¡¹ | çŽ°çŠ¶ | è¯´æ˜Ž |
|--------|------|------|
| MES系统部署 | âœ… å·²éƒ¨ç½² | é¡¹ç›®åŒ…含完整的 `yudao-module-mes` æ¨¡å— |
| ç”Ÿäº§ç®¡ç†ä¸“用模块 | âœ… å·²å…·å¤‡ | åŒ…含工单管理、生产任务、报工、排产等子模块 |
| ç³»ç»Ÿè¿è¡ŒçŠ¶æ€ | âœ… æ­£å¸¸ | ç³»ç»Ÿå¯æ­£å¸¸ç¼–译运行,数据库表和数据已就绪 |
---
### è¦æ±‚2:生产工单管理制度与系统流程匹配
> æ ¸æŸ¥ä¼ä¸šæ˜¯å¦åˆ¶å®šç”Ÿäº§å·¥å•管理相关制度文件,包括生产工单管理制度、生产进度跟踪细则、物料领用/耗用管理规定等,确认制度中明确了工单创建、派工、报工、核销、归档等全流程规范,且制度要求与信息化系统操作流程相匹配。
**符合度:⚠️ éƒ¨åˆ†ç¬¦åˆï¼ˆç³»ç»Ÿæµç¨‹åŸºæœ¬è¦†ç›–,但缺少核销和归档环节)**
| æ ¸æŸ¥é¡¹ | çŽ°çŠ¶ | è¯´æ˜Ž |
|--------|------|------|
| å·¥å•创建 | âœ… å·²å®žçް | `POST /mes/pro/work-order/create`,草稿状态 |
| å·¥å•确认(审批) | âœ… å·²å®žçް | `PUT /mes/pro/work-order/confirm`,草稿→已确认 |
| æ´¾å·¥(排产) | âœ… å·²å®žçް | ç”Ÿäº§ä»»åŠ¡(Task)从工单派生,含工作站、工序、排产数量 |
| æŠ¥å·¥ | âœ… å·²å®žçް | `POST /mes/pro-feedback/create`,支持自行报工和统一报工 |
| æŠ¥å·¥æäº¤ | âœ… å·²å®žçް | `PUT /mes/pro-feedback/submit`,草稿→审批中 |
| æŠ¥å·¥å®¡æ‰¹ | âœ… å·²å®žçް | `PUT /mes/pro-feedback/approve`,触发入库/扣料 |
| ç‰©æ–™é¢†ç”¨ | âœ… å·²å®žçް | ç”Ÿäº§å‘æ–™(ProductIssue)模块,关联工单 |
| ç‰©æ–™è€—用(倒冲) | âœ… å·²å®žçް | æŠ¥å·¥å®¡æ‰¹æ—¶è‡ªåŠ¨æŒ‰BOM扣料(ItemConsume) |
| æˆå“å…¥åº“ | âœ… å·²å®žçް | ç”Ÿäº§äº§å‡º(ProductProduce)模块,审批后自动入库 |
| **工单核销** | âŒ **缺失** | æ— å·¥å•核销功能,无法对工单进行物料/产量的最终对账核销 |
| **工单归档** | âŒ **缺失** | æ— å·¥å•归档功能,已完成/已取消的工单无归档状态和归档操作 |
| åˆ¶åº¦æ–‡ä»¶ç®¡ç† | âŒ ä¸å±žäºŽç³»ç»ŸèŒƒå›´ | åˆ¶åº¦æ–‡ä»¶ä¸ºçº¿ä¸‹ç®¡ç†ï¼Œç³»ç»Ÿä¸æä¾›åˆ¶åº¦æ–‡ä»¶ç®¡ç†åŠŸèƒ½ |
**系统操作流程:**
```
工单创建(草稿) â†’ å·¥å•确认(已确认) â†’ æ´¾å·¥/排产(生成任务)
    â†’ æŠ¥å·¥(草稿) â†’ æäº¤(审批中) â†’ å®¡æ‰¹é€šè¿‡(已完成)
    â†’ è‡ªåŠ¨å…¥åº“(关键工序) / è‡ªåŠ¨æ‰£æ–™(倒冲工序)
    â†’ å·¥å•完工(已完成) / å·¥å•取消(已取消)
缺失环节:工单核销 â†’ å·¥å•å½’æ¡£
```
---
### è¦æ±‚3:工单全生命周期线上管理能力
> çŽ°åœºæ¼”ç¤ºä¿¡æ¯åŒ–ç³»ç»Ÿçš„ç”Ÿäº§å·¥å•æ ¸å¿ƒåŠŸèƒ½ï¼Œç”±ä¼ä¸šæ“ä½œäººå‘˜ä¾æ¬¡æ¼”ç¤ºå·¥å•åˆ›å»ºã€å®¡æ‰¹ã€ä¸‹å‘ã€è¿›åº¦æ›´æ–°ã€å®Œå·¥ç¡®è®¤çš„å…¨æµç¨‹æ“ä½œï¼Œæ ¸å®žç³»ç»Ÿå…·å¤‡å·¥å•å…¨ç”Ÿå‘½å‘¨æœŸçº¿ä¸Šç®¡ç†èƒ½åŠ›ï¼Œå„çŽ¯èŠ‚æ“ä½œèŠ‚ç‚¹æ¸…æ™°ã€å¯è¿½æº¯ã€‚
**符合度:⚠️ éƒ¨åˆ†ç¬¦åˆï¼ˆæ ¸å¿ƒæµç¨‹å…·å¤‡ï¼Œä½†å·¥å•级缺少"下发"环节,进度更新依赖报工间接实现)**
| æ ¸æŸ¥é¡¹ | çŽ°çŠ¶ | è¯´æ˜Ž |
|--------|------|------|
| å·¥å•创建 | âœ… å·²å®žçް | åˆ›å»ºå·¥å• + è‡ªåŠ¨ç”ŸæˆBOM + è‡ªåŠ¨ç”Ÿæˆæ¡ç  |
| å·¥å•审批(确认) | âœ… å·²å®žçް | confirmWorkOrder,草稿→已确认 |
| **工单下发** | âš ï¸ **弱化** | å·¥å•确认后即可排产/报工,无独立的"下发"操作和状态,确认即生效 |
| è¿›åº¦æ›´æ–° | âœ… å·²å®žçް | é€šè¿‡æŠ¥å·¥å®¡æ‰¹åŽè‡ªåŠ¨ç´¯åŠ  `quantityProduced`,间接更新进度 |
| å®Œå·¥ç¡®è®¤ | âœ… å·²å®žçް | finishWorkOrder,级联完成所有任务 |
| å„环节可追溯 | âš ï¸ éƒ¨åˆ†å…·å¤‡ | è¯¦è§è¦æ±‚4分析 |
**工单状态流转(现有):**
```
草稿(0) â†’ å·²ç¡®è®¤(1) â†’ å·²å®Œæˆ(2)
                    â†’ å·²å–消(3)
缺失:已确认 â†’ å·²ä¸‹å‘(未实现独立下发环节)
```
---
### è¦æ±‚4:工单操作痕迹完整性与可追溯性
> æŠ½æŸ¥ç³»ç»Ÿå†…近期生产工单数据,按开工中、工序完工、整体完工、异常停工四种状态分类抽查,核实每笔工单的各环节操作痕迹完整,包含操作人、操作时间、操作内容等信息,工单状态更新及时、准确。
**符合度:⚠️ éƒ¨åˆ†ç¬¦åˆï¼ˆåŸºç¡€å®¡è®¡ä¿¡æ¯å…·å¤‡ï¼Œä½†æ“ä½œç—•迹不够完整,四种状态分类不完整)**
| æ ¸æŸ¥é¡¹ | çŽ°çŠ¶ | è¯´æ˜Ž |
|--------|------|------|
| åˆ›å»ºäºº/创建时间 | âœ… å·²å…·å¤‡ | BaseDO çš„ `creator`、`create_time` å­—段 |
| æ›´æ–°äºº/更新时间 | âœ… å·²å…·å¤‡ | BaseDO çš„ `updater`、`update_time` å­—段 |
| æŠ¥å·¥äºº | âœ… å·²å…·å¤‡ | FeedbackDO çš„ `feedbackUserId` + `feedbackTime` |
| å®¡æ‰¹äºº | âœ… å·²å…·å¤‡ | FeedbackDO çš„ `approveUserId` |
| å®Œå·¥æ—¶é—´ | âœ… å·²å…·å¤‡ | WorkOrderDO çš„ `finishDate` |
| å–消时间 | âœ… å·²å…·å¤‡ | WorkOrderDO çš„ `cancelDate` |
| ç³»ç»Ÿæ“ä½œæ—¥å¿— | âœ… å·²å…·å¤‡ | `system_operate_log` è¡¨ï¼Œè®°å½•所有API请求的操作人、时间、URL、IP等 |
| **工单级操作时间线** | âŒ **缺失** | æ— å·¥å•专属的操作历史表,无法按工单ID查询"谁在什么时间做了什么操作"的时间线 |
| **审批人自动记录** | âŒ **缺失** | æŠ¥å·¥å®¡æ‰¹æ—¶æœªè‡ªåЍ填充 `approveUserId` ä¸ºå½“前登录用户 |
| **状态分类:开工中** | âš ï¸ å¯¹åº”工单"已确认" | ä½†"已确认"不等于"开工中",缺少明确的开工状态 |
| **状态分类:工序完工** | âš ï¸ é—´æŽ¥å¯æŸ¥ | æŠ¥å·¥çŠ¶æ€=已完成代表工序完工,但无"工序完工"的工单级状态 |
| **状态分类:整体完工** | âœ… å·²å…·å¤‡ | å·¥å•状态=已完成(2) |
| **状态分类:异常停工** | âŒ **缺失** | å·¥å•æ— "异常停工"状态,详见要求6分析 |
---
### è¦æ±‚5:系统数据与实际生产执行资料核对
> æ ¸å¯¹ç³»ç»Ÿå†…工单数据与实际生产执行资料,包括物料领用单、生产进度日报表、成品入库单、设备运行记录等,确认工单中的产量、物料耗用、生产工期等数据与实际一致,偏差率在合理范围内。
**符合度:⚠️ éƒ¨åˆ†ç¬¦åˆï¼ˆæ•°æ®å…³è”链路存在,但缺少核对/对账工具和偏差分析能力)**
| æ ¸æŸ¥é¡¹ | çŽ°çŠ¶ | è¯´æ˜Ž |
|--------|------|------|
| ç‰©æ–™é¢†ç”¨å• | âœ… å·²å…·å¤‡ | ç”Ÿäº§å‘æ–™(ProductIssue)模块,关联 workOrderId |
| ç‰©æ–™è€—用记录 | âœ… å·²å…·å¤‡ | ç‰©æ–™æ¶ˆè€—(ItemConsume)模块,报工审批时自动生成,关联 feedbackId |
| æˆå“å…¥åº“单 | âœ… å·²å…·å¤‡ | ç”Ÿäº§äº§å‡º(ProductProduce)模块,报工审批时自动生成,关联 feedbackId |
| äº§é‡æ•°æ® | âœ… å·²å…·å¤‡ | å·¥å• `quantityProduced`,任务 `producedQuantity`/`qualifyQuantity`/`unqualifyQuantity` |
| ç”Ÿäº§å·¥æœŸ | âœ… å·²å…·å¤‡ | ä»»åŠ¡ `startTime`/`endTime`/`duration`,工单 `requestDate`/`finishDate` |
| è®¾å¤‡è¿è¡Œè®°å½• | âœ… å·²å…·å¤‡ | è®¾å¤‡ç»´ä¿®/点检/保养记录模块(DvRepair/DvCheckRecord/DvMaintenRecord) |
| **工单-物料对账** | âŒ **缺失** | æ— å·¥å•物料核销/对账功能,无法自动比对BOM需求量 vs å®žé™…领用量 vs å®žé™…耗用量 |
| **工单-产量对账** | âŒ **缺失** | æ— å·¥å•产量核销功能,无法自动比对工单数量 vs æŠ¥å·¥åˆè®¡ vs å…¥åº“合计 |
| **偏差率分析** | âŒ **缺失** | æ— åå·®çŽ‡è®¡ç®—å’Œé¢„è­¦åŠŸèƒ½ |
| **生产进度日报表** | âŒ **缺失** | æ— æŒ‰æ—¥æ±‡æ€»ç”Ÿäº§è¿›åº¦çš„æŠ¥è¡¨åŠŸèƒ½ |
---
### è¦æ±‚6:工单异常管理能力
> æ ¸æŸ¥ç³»ç»Ÿçš„工单异常管理能力,抽查系统内异常停工工单的处理记录,核实是否包含异常原因分析、处理措施、复工时间等信息,异常工单实现闭环管理,无长期未处理的情况。
**符合度:⚠️ éƒ¨åˆ†ç¬¦åˆï¼ˆæœ‰å®‰ç¯å‘¼å«è®°å½•,但工单级异常停工管理严重不足)**
| æ ¸æŸ¥é¡¹ | çŽ°çŠ¶ | è¯´æ˜Ž |
|--------|------|------|
| å®‰ç¯å‘¼å«è®°å½• | âœ… å·²å…·å¤‡ | AndonRecord æ¨¡å—,记录呼叫原因、级别、处置状态、处置人、处置时间 |
| å®‰ç¯å…³è”工单 | âœ… å·²å…·å¤‡ | AndonRecordDO å…³è” workOrderId |
| å®‰ç¯é—­çŽ¯ç®¡ç† | âš ï¸ éƒ¨åˆ†å…·å¤‡ | æœ‰"未处置→已处置"状态流转,但无超时预警和升级机制 |
| **工单"异常停工"状态** | âŒ **缺失** | å·¥å•状态仅有:草稿/已确认/已完成/已取消,无"异常停工"状态 |
| **工单暂停/挂起操作** | âŒ **缺失** | æ—  pauseWorkOrder / suspendWorkOrder æŽ¥å£ |
| **工单恢复/复工操作** | âŒ **缺失** | æ—  resumeWorkOrder æŽ¥å£å’Œå¤å·¥æ—¶é—´è®°å½• |
| **异常原因分析** | âŒ **缺失** | å®‰ç¯è®°å½•仅有 reason å­—段(呼叫原因),无结构化的异常原因分析(如设备故障/物料短缺/质量异常等分类) |
| **处理措施记录** | âŒ **缺失** | å®‰ç¯è®°å½•仅有 remark å­—段(备注),无结构化的处理措施记录 |
| **复工时间记录** | âŒ **缺失** | æ— åœå·¥å¼€å§‹æ—¶é—´ã€åœå·¥ç»“束时间、停工时长等字段 |
| **长期未处理预警** | âŒ **缺失** | æ— å¼‚常超时预警机制,无法发现长期未处理的异常工单 |
| **异常闭环管理** | âŒ **缺失** | å·¥å•无法从"异常停工"恢复到"生产中",无闭环流程 |
---
## ç¼ºå¤±åŠŸèƒ½æ±‡æ€»
### ðŸ”´ ä¸¥é‡ç¼ºå¤±ï¼ˆç›´æŽ¥å½±å“æ ¸æŸ¥é€šè¿‡ï¼‰
| # | ç¼ºå¤±åŠŸèƒ½ | å¯¹åº”要求 | è¯´æ˜Ž |
|---|---------|---------|------|
| 1 | **工单异常停工状态与闭环管理** | è¦æ±‚4、6 | å·¥å•æ— "异常停工"状态,无暂停/复工操作,无法实现异常工单闭环管理 |
| 2 | **工单操作历史时间线** | è¦æ±‚4 | æ— å·¥å•专属操作历史表,无法按工单查询完整的操作痕迹时间线 |
| 3 | **审批人自动记录** | è¦æ±‚4 | æŠ¥å·¥å®¡æ‰¹æ—¶æœªè‡ªåŠ¨å¡«å……å®¡æ‰¹äººä¸ºå½“å‰ç™»å½•ç”¨æˆ· |
### ðŸŸ¡ ä¸­åº¦ç¼ºå¤±ï¼ˆå½±å“æ ¸æŸ¥è´¨é‡ä½†ä¸è‡´å‘½ï¼‰
| # | ç¼ºå¤±åŠŸèƒ½ | å¯¹åº”要求 | è¯´æ˜Ž |
|---|---------|---------|------|
| 4 | **工单下发环节** | è¦æ±‚3 | å·¥å•无独立的"下发"操作和状态,确认即生效,缺少下发节点 |
| 5 | **工单核销功能** | è¦æ±‚2 | æ— å·¥å•物料/产量的最终对账核销 |
| 6 | **工单归档功能** | è¦æ±‚2 | æ— å·¥å•归档状态和归档操作 |
| 7 | **工单-物料对账** | è¦æ±‚5 | æ— æ³•自动比对BOM需求量 vs å®žé™…领用量 vs å®žé™…耗用量 |
| 8 | **工单-产量对账** | è¦æ±‚5 | æ— æ³•自动比对工单数量 vs æŠ¥å·¥åˆè®¡ vs å…¥åº“合计 |
| 9 | **异常原因结构化分析** | è¦æ±‚6 | å®‰ç¯è®°å½•仅有文本原因,无异常分类(设备故障/物料短缺/质量异常等) |
| 10 | **处理措施结构化记录** | è¦æ±‚6 | å®‰ç¯è®°å½•仅有备注,无处理措施、复工时间等结构化字段 |
### ðŸŸ¢ è½»åº¦ç¼ºå¤±ï¼ˆé”¦ä¸Šæ·»èŠ±ï¼‰
| # | ç¼ºå¤±åŠŸèƒ½ | å¯¹åº”要求 | è¯´æ˜Ž |
|---|---------|---------|------|
| 11 | **偏差率分析与预警** | è¦æ±‚5 | æ— åå·®çŽ‡è®¡ç®—å’Œé¢„è­¦åŠŸèƒ½ |
| 12 | **生产进度日报表** | è¦æ±‚5 | æ— æŒ‰æ—¥æ±‡æ€»ç”Ÿäº§è¿›åº¦çš„æŠ¥è¡¨ |
| 13 | **异常超时预警** | è¦æ±‚6 | æ— é•¿æœŸæœªå¤„理异常的预警机制 |
| 14 | **开工中/工序完工的工单级状态** | è¦æ±‚4 | å·¥å•状态粒度不够,无法直接按"开工中""工序完工"分类 |
| 15 | **工单自动完工** | è¦æ±‚3 | å·¥å•在 quantityProduced >= quantity æ—¶ä¸ä¼šè‡ªåŠ¨è½¬ä¸ºå·²å®Œæˆï¼Œéœ€æ‰‹åŠ¨ finish |
| 16 | **任务"进行中"状态** | è¦æ±‚4 | ä»»åŠ¡ä»…æœ‰è‰ç¨¿/已完成/已取消,无"进行中/生产中"状态,无法区分"已排产待生产"和"正在生产" |
| 17 | **工单多级审批** | è¦æ±‚3 | å·¥å•确认是单步操作(草稿→已确认),无BPM工作流多级审批 |
| 18 | **quantityScheduled/quantityChanged å­—段** | è¦æ±‚5 | å·¥å•DO中存在但从未被更新,属于死字段 |
---
## å»ºè®®ä¼˜å…ˆè¡¥å……的功能(按优先级排序)
### P0 - å¿…须补充(核查硬伤)
**1. å·¥å•异常停工闭环管理**
- å·¥å•新增状态:`SUSPENDED(4, "异常停工")`
- æ–°å¢žæŽ¥å£ï¼š`PUT /mes/pro/work-order/suspend` â€” æš‚停工单(已确认→异常停工),记录停工原因、停工时间
- æ–°å¢žæŽ¥å£ï¼š`PUT /mes/pro/work-order/resume` â€” æ¢å¤å·¥å•(异常停工→已确认),记录复工时间、处理措施
- å·¥å•DO新增字段:`suspendDate`(停工时间)、`resumeDate`(复工时间)、`suspendReason`(停工原因)、`resumeMeasure`(复工措施)
- ä»»åŠ¡åŒæ­¥æ”¯æŒæš‚åœ/恢复
**2. å·¥å•操作历史记录**
- æ–°å¢žè¡¨ `mes_pro_work_order_log`,记录工单全生命周期操作
- å­—段:workOrderId、operation(创建/确认/下发/完工/取消/暂停/恢复)、operatorId、operateTime、remark
- æ–°å¢žæŽ¥å£ï¼š`GET /mes/pro/work-order/log` â€” æŸ¥è¯¢å·¥å•操作历史时间线
- åœ¨å·¥å•各状态变更操作中自动写入操作记录
- **快捷方案**:框架已有 `@LogRecord`(mzt-biz-log)+ `system_operate_log` è¡¨ï¼ˆæ”¯æŒ `bizId` æŒ‰ä¸šåŠ¡ID查询),CRM/System æ¨¡å—已在用,MES æ¨¡å—仅需在工单 Service æ–¹æ³•上加 `@LogRecord` æ³¨è§£å³å¯å®žçŽ°åŸºç¡€æ“ä½œæ—¥å¿—è®°å½•ï¼Œæ— éœ€æ–°å»ºè¡¨
**3. æŠ¥å·¥å®¡æ‰¹è‡ªåŠ¨è®°å½•å®¡æ‰¹äºº**
- `approveFeedback` æ–¹æ³•中自动设置 `approveUserId = getLoginUserId()`
**4. å·¥å•状态转换记录"谁操作"**
- å·¥å•DO新增字段:`confirmUserId`(确认人)、`finishUserId`(完工人)、`cancelUserId`(取消人)
- å„状态变更方法中自动填充当前登录用户ID
### P1 - å»ºè®®è¡¥å……(提升核查质量)
**4. å·¥å•下发环节**
- å·¥å•新增状态:`ISSUED(2, "已下发")`(在已确认和已完成之间)
- æ–°å¢žæŽ¥å£ï¼š`PUT /mes/pro/work-order/issue` â€” ä¸‹å‘工单(已确认→已下发)
- è°ƒæ•´å®Œå·¥ç¡®è®¤å‰ç½®æ¡ä»¶ï¼šå·²ä¸‹å‘→已完成
**5. å·¥å•核销与归档**
- å·¥å•新增状态:`SETTLED(5, "已核销")`、`ARCHIVED(6, "已归档")`
- æ ¸é”€ï¼šæ¯”对工单BOM需求量 vs å®žé™…消耗量,工单数量 vs å…¥åº“数量,生成核销报告
- å½’档:核销通过后归档,归档后只读不可修改
**6. å¼‚常记录结构化增强**
- å®‰ç¯è®°å½•新增字段:`exceptionType`(异常分类:设备故障/物料短缺/质量异常/人员不足/其他)、`handleMeasure`(处理措施)、`downtimeStart`(停工开始时间)、`downtimeEnd`(停工结束时间)、`downtimeDuration`(停工时长)
- æ–°å¢žå¼‚常超时预警:未处置超过N小时自动升级通知
**7. ä»»åŠ¡çŠ¶æ€å¢žå¼º**
- ä»»åŠ¡æ–°å¢žçŠ¶æ€ï¼š`IN_PROGRESS(1, "进行中")`,在草稿和已完成之间
- æŠ¥å·¥æäº¤/审批时自动将任务从草稿→进行中
- ä¸Žå·¥å•状态联动:工单暂停时任务同步暂停
**8. å·¥å•自动完工**
- å½“工单所有任务均已完工,或 `quantityProduced >= quantity` æ—¶ï¼Œæç¤ºç”¨æˆ·å¯å®Œå·¥ï¼ˆå¯é€‰è‡ªåŠ¨å®Œå·¥ï¼‰
- ä¿®å¤ `quantityScheduled` å’Œ `quantityChanged` æ­»å­—段:排产时更新 quantityScheduled,调整数量时更新 quantityChanged
docs/product_issue_task_integration.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,255 @@
# ç”Ÿäº§é¢†æ–™/退料关联 Task - å‰ç«¯è”调方案
## æ¶‰åŠé¡µé¢
- ç”Ÿäº§é¢†æ–™å‡ºåº“单列表页 / æ–°å¢žç¼–辑页 / è¡Œç®¡ç†é¡µ
- ç”Ÿäº§é€€æ–™å•列表页 / æ–°å¢žç¼–辑页 / è¡Œç®¡ç†é¡µ
- ç”Ÿäº§ä»»åŠ¡åˆ—è¡¨é¡µï¼ˆé€‰æ‹© task ç”¨ï¼‰
## ä¸šåŠ¡æµç¨‹ä¸Žæ•°æ®å¸¦å…¥
### é¢†æ–™æµç¨‹
1. **新增领料出库单**:选择 Task â†’ å·¥å•(workOrderId)、工作站(workstationId) ä»Ž Task è‡ªåЍ另入
2. **新增领料出库单行**:行可选填 taskId,默认取主表的 taskId
3. **完成领料出库**:系统自动创建任务投料记录(mes_pro_task_issue),issuedQuantity = availableQuantity = è¡Œæ•°é‡
4. **取消领料出库**:系统自动删除通过该领料单创建的任务投料记录
### é€€æ–™æµç¨‹
1. **新增退料单**:选择 Task â†’ å·¥å•(workOrderId)、工作站(workstationId) ä»Ž Task è‡ªåЍ另入
2. **新增退料单行**:行可选填 taskId,默认取主表的 taskId
3. **完成退料**:系统自动扣减任务投料记录的 issuedQuantity å’Œ availableQuantity
4. **取消退料**:无额外操作(退料未完成时不影响投料记录)
### é¢†æ–™ â†’ é€€æ–™é—­çޝ
```
领料完成 â†’ task_issue: issued=100, available=100, used=0
退料完成 â†’ task_issue: issued=80,  available=80,  used=0  (退了20)
```
---
## ä¸€ã€é¢†æ–™å‡ºåº“单 API
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| POST | /mes/wm/product-issue/create | åˆ›å»ºé¢†æ–™å‡ºåº“单 |
| PUT | /mes/wm/product-issue/update | ä¿®æ”¹é¢†æ–™å‡ºåº“单 |
| GET | /mes/wm/product-issue/get | èŽ·å¾—é¢†æ–™å‡ºåº“å• |
| GET | /mes/wm/product-issue/page | èŽ·å¾—é¢†æ–™å‡ºåº“å•åˆ†é¡µ |
| PUT | /mes/wm/product-issue/submit | æäº¤é¢†æ–™å‡ºåº“单 |
| PUT | /mes/wm/product-issue/stock | æ‰§è¡Œæ‹£è´§ |
| PUT | /mes/wm/product-issue/finish | å®Œæˆé¢†æ–™å‡ºåº“(自动创建投料记录) |
| PUT | /mes/wm/product-issue/cancel | å–消领料出库(自动删除投料记录) |
| POST | /mes/wm/product-issue-line/create | åˆ›å»ºé¢†æ–™å‡ºåº“单行 |
| PUT | /mes/wm/product-issue-line/update | ä¿®æ”¹é¢†æ–™å‡ºåº“单行 |
### åˆ›å»º/修改领料出库单 - è¯·æ±‚参数变更
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| taskId | Long | **是(新增必填)** | ç”Ÿäº§ä»»åŠ¡ ID,以 task ä¸ºä¸» |
| workOrderId | Long | **否(原必填改为选填)** | ç”Ÿäº§å·¥å• ID,从 task è‡ªåЍ另入 |
| workstationId | Long | å¦ | å·¥ä½œç«™ ID,从 task è‡ªåЍ另入 |
| requiredTime | LocalDateTime | æ˜¯ | éœ€æ±‚æ—¶é—´ |
**请求示例:**
```json
{
  "code": "ISSUE20260716001",
  "name": "生产领料",
  "taskId": 1,
  "requiredTime": "2026-07-16 10:00:00",
  "remark": ""
}
```
**响应:** `{ "code": 0, "data": 1 }`
### é¢†æ–™å‡ºåº“单 - å“åº”字段新增
| å­—段 | ç±»åž‹ | è¯´æ˜Ž |
|------|------|------|
| taskId | Long | ç”Ÿäº§ä»»åŠ¡ ID |
| taskCode | String | **新增** ç”Ÿäº§ä»»åŠ¡ç¼–ç  |
| taskName | String | **新增** ç”Ÿäº§ä»»åŠ¡åç§° |
| requiredTime | LocalDateTime | éœ€æ±‚æ—¶é—´ |
**响应示例:**
```json
{
  "code": 0,
  "data": {
    "id": 1,
    "code": "ISSUE20260716001",
    "name": "生产领料",
    "taskId": 1,
    "taskCode": "TASK001",
    "taskName": "注塑任务",
    "workOrderId": 10,
    "workOrderCode": "WO001",
    "workstationId": 5,
    "workstationName": "装配工作站",
    "clientCode": "C001",
    "clientName": "某某客户",
    "issueDate": null,
    "requiredTime": "2026-07-16 10:00:00",
    "status": 0,
    "remark": "",
    "createTime": "2026-07-16 10:00:00"
  }
}
```
### é¢†æ–™å‡ºåº“单行 - è¯·æ±‚/响应字段新增
| å­—段 | ç±»åž‹ | è¯·æ±‚ | å“åº” | è¯´æ˜Ž |
|------|------|------|------|------|
| taskId | Long | é€‰å¡« | - | ç”Ÿäº§ä»»åŠ¡ ID,默认取主表的 taskId |
| taskCode | String | - | æ–°å¢ž | ç”Ÿäº§ä»»åŠ¡ç¼–ç  |
| taskName | String | - | æ–°å¢ž | ç”Ÿäº§ä»»åŠ¡åç§° |
---
## äºŒã€é€€æ–™å• API
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| POST | /mes/wm/return-issue/create | åˆ›å»ºé€€æ–™å• |
| PUT | /mes/wm/return-issue/update | ä¿®æ”¹é€€æ–™å• |
| GET | /mes/wm/return-issue/get | èŽ·å¾—é€€æ–™å• |
| GET | /mes/wm/return-issue/page | èŽ·å¾—é€€æ–™å•åˆ†é¡µ |
| PUT | /mes/wm/return-issue/submit | æäº¤é€€æ–™å• |
| PUT | /mes/wm/return-issue/stock | å…¥åº“上架 |
| PUT | /mes/wm/return-issue/finish | å®Œæˆé€€æ–™ï¼ˆè‡ªåŠ¨æ‰£å‡æŠ•æ–™è®°å½•å¯ç”¨é‡ï¼‰ |
| PUT | /mes/wm/return-issue/cancel | å–消退料 |
| POST | /mes/wm/return-issue-line/create | åˆ›å»ºé€€æ–™å•行 |
| PUT | /mes/wm/return-issue-line/update | ä¿®æ”¹é€€æ–™å•行 |
### åˆ›å»º/修改退料单 - è¯·æ±‚参数变更
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| taskId | Long | **是(新增必填)** | ç”Ÿäº§ä»»åŠ¡ ID,以 task ä¸ºä¸» |
| workOrderId | Long | **否(原必填改为选填)** | ç”Ÿäº§å·¥å• ID,从 task è‡ªåЍ另入 |
| workstationId | Long | å¦ | å·¥ä½œç«™ ID,从 task è‡ªåЍ另入 |
**请求示例:**
```json
{
  "code": "RI20260716001",
  "name": "生产退料",
  "taskId": 1,
  "type": 1,
  "remark": ""
}
```
**响应:** `{ "code": 0, "data": 1 }`
### é€€æ–™å• - å“åº”字段新增
| å­—段 | ç±»åž‹ | è¯´æ˜Ž |
|------|------|------|
| taskId | Long | **新增** ç”Ÿäº§ä»»åŠ¡ ID |
| taskCode | String | **新增** ç”Ÿäº§ä»»åŠ¡ç¼–ç  |
| taskName | String | **新增** ç”Ÿäº§ä»»åŠ¡åç§° |
**响应示例:**
```json
{
  "code": 0,
  "data": {
    "id": 1,
    "code": "RI20260716001",
    "name": "生产退料",
    "taskId": 1,
    "taskCode": "TASK001",
    "taskName": "注塑任务",
    "workOrderId": 10,
    "workOrderCode": "WO001",
    "workstationId": 5,
    "workstationName": "装配工作站",
    "type": 1,
    "returnDate": null,
    "status": 0,
    "remark": "",
    "createTime": "2026-07-16 10:00:00"
  }
}
```
### é€€æ–™å•行 - è¯·æ±‚/响应字段新增
| å­—段 | ç±»åž‹ | è¯·æ±‚ | å“åº” | è¯´æ˜Ž |
|------|------|------|------|------|
| taskId | Long | é€‰å¡« | - | ç”Ÿäº§ä»»åŠ¡ ID,默认取主表的 taskId |
| taskCode | String | - | æ–°å¢ž | ç”Ÿäº§ä»»åŠ¡ç¼–ç  |
| taskName | String | - | æ–°å¢ž | ç”Ÿäº§ä»»åŠ¡åç§° |
### é€€æ–™å•分页 - è¯·æ±‚参数新增
| å‚æ•° | ç±»åž‹ | è¯´æ˜Ž |
|------|------|------|
| taskId | Long | **新增** æŒ‰ç”Ÿäº§ä»»åŠ¡ ID è¿‡æ»¤ |
---
## å­—段展示规则
### é¢†æ–™å‡ºåº“单
| å­—段 | å±•示位置 | è¯´æ˜Ž |
|------|----------|------|
| taskId | æ–°å¢ž/编辑表单 | å¿…填,选择 Task ä¸‹æ‹‰æ¡† |
| taskCode | åˆ—表、详情 | ä»Ž Task è‡ªåŠ¨å¸¦å…¥å±•ç¤º |
| taskName | åˆ—表、详情 | ä»Ž Task è‡ªåŠ¨å¸¦å…¥å±•ç¤º |
| workOrderId | æ–°å¢ž/编辑表单 | é€‰å¡«ï¼Œé€‰æ‹© Task åŽè‡ªåŠ¨å¡«å…¥ï¼Œå¯æ‰‹åŠ¨ä¿®æ”¹ |
| workOrderCode | åˆ—表、详情 | ä»Žå·¥å•自动带入展示 |
| workstationId | æ–°å¢ž/编辑表单 | é€‰å¡«ï¼Œé€‰æ‹© Task åŽè‡ªåŠ¨å¡«å…¥ï¼Œå¯æ‰‹åŠ¨ä¿®æ”¹ |
| workstationName | åˆ—表、详情 | ä»Žå·¥ä½œç«™è‡ªåŠ¨å¸¦å…¥å±•ç¤º |
| requiredTime | æ–°å¢ž/编辑表单、详情 | å¿…填,需求时间 |
| è¡Œçº§ taskId | è¡Œæ–°å¢ž/编辑表单 | é€‰å¡«ï¼Œé»˜è®¤å–主表 taskId |
### é€€æ–™å•
| å­—段 | å±•示位置 | è¯´æ˜Ž |
|------|----------|------|
| taskId | æ–°å¢ž/编辑表单 | å¿…填,选择 Task ä¸‹æ‹‰æ¡† |
| taskCode | åˆ—表、详情 | ä»Ž Task è‡ªåŠ¨å¸¦å…¥å±•ç¤º |
| taskName | åˆ—表、详情 | ä»Ž Task è‡ªåŠ¨å¸¦å…¥å±•ç¤º |
| workOrderId | æ–°å¢ž/编辑表单 | é€‰å¡«ï¼Œé€‰æ‹© Task åŽè‡ªåŠ¨å¡«å…¥ï¼Œå¯æ‰‹åŠ¨ä¿®æ”¹ |
| workOrderCode | åˆ—表、详情 | ä»Žå·¥å•自动带入展示 |
| workstationId | æ–°å¢ž/编辑表单 | é€‰å¡«ï¼Œé€‰æ‹© Task åŽè‡ªåŠ¨å¡«å…¥ï¼Œå¯æ‰‹åŠ¨ä¿®æ”¹ |
| workstationName | åˆ—表、详情 | ä»Žå·¥ä½œç«™è‡ªåŠ¨å¸¦å…¥å±•ç¤º |
| è¡Œçº§ taskId | è¡Œæ–°å¢ž/编辑表单 | é€‰å¡«ï¼Œé»˜è®¤å–主表 taskId |
---
## ä¸šåŠ¡è§„åˆ™è¯´æ˜Ž
| åœºæ™¯ | è§„则 |
|------|------|
| æ–°å¢žé¢†æ–™/退料单 | taskId å¿…填;选择 task åŽè‡ªåЍ另入 workOrderId å’Œ workstationId |
| ä¿®æ”¹é¢†æ–™/退料单 | taskId å¿…填;同新增规则 |
| é¢†æ–™å‡ºåº“完成 | ç³»ç»Ÿè‡ªåŠ¨æŒ‰è¡Œåˆ›å»º mes_pro_task_issue æŠ•料记录,sourceDocType=PRODUCT_ISSUE |
| å–消领料出库 | ç³»ç»Ÿè‡ªåŠ¨åˆ é™¤é€šè¿‡è¯¥é¢†æ–™å•åˆ›å»ºçš„æŠ•æ–™è®°å½• |
| é€€æ–™å®Œæˆ | ç³»ç»Ÿè‡ªåŠ¨æ‰£å‡å¯¹åº” task_issue çš„ issuedQuantity å’Œ availableQuantity |
| è¡Œçº§ taskId | è¡Œçš„ taskId ä¼˜å…ˆäºŽä¸»è¡¨çš„ taskId;如果行为空则取主表的 |
| æŠ•料记录匹配 | é€€æ–™æ‰£å‡æ—¶ï¼ŒæŒ‰ taskId + itemId + sourceDocType=PRODUCT_ISSUE åŒ¹é…æŠ•料记录 |
---
## æ³¨æ„äº‹é¡¹
- **以 task ä¸ºä¸»**:创建领料/退料单时,必须先选择 task,工单和工作站从 task è‡ªåЍ另入
- **workOrderId ä¸å†æ˜¯å¿…å¡«**:前端表单中 workOrderId æ”¹ä¸ºé€‰å¡«ï¼Œé€‰æ‹© task åŽè‡ªåЍ填入
- **行级 taskId**:行表新增 taskId å­—段,前端行编辑时可选择不同的 task(默认取主表的)
- **投料记录自动管理**:领料完成自动创建投料记录,退料完成自动扣减投料可用量,前端无需额外操作
- **分页查询支持 taskId**:领料出库单和退料单分页接口均支持按 taskId è¿‡æ»¤æŸ¥è¯¢
- **领料取消**:取消领料时,只有通过该领料单创建的投料记录会被删除(按 sourceDocType + sourceDocId åŒ¹é…ï¼‰
- **退料扣减**:退料扣减投料记录时,issuedQuantity å’Œ availableQuantity æœ€ä½Žä¸º 0,不会出现负数
docs/production_data_cleanup.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,244 @@
# MES ä¸šåŠ¡æ•°æ®æ¸…ç† SQL
## æ¸…理范围
清理 MES æ¨¡å—全部业务数据,保留系统配置类数据。按依赖顺序执行。
### æ¸…理分类
| åˆ†ç±» | æ¶‰åŠè¡¨ | è¯´æ˜Ž |
|------|--------|------|
| åº“存事务/占用 | transaction, stock_reserve | ç”Ÿäº§ç›¸å…³ biz_type |
| ç‰©æ–™æ¶ˆè€—单 | item_consume + è¡Œ/明细 | å€’冲消耗记录 |
| äº§å‡ºå•/入库单 | product_produce, product_receipt + è¡Œ/明细 | ç”Ÿäº§äº§å‡º |
| é€€æ–™å• | return_issue + è¡Œ/明细 | ç”Ÿäº§é€€æ–™ |
| é¢†æ–™å‡ºåº“单 | product_issue + è¡Œ/明细 | ç”Ÿäº§é¢†æ–™ |
| è´¨æ£€å• | ipqc, rqc, iqc, oqc + è¡Œ/缺陷/指标 | è´¨æ£€è®°å½• |
| æŠ¥å·¥ | feedback | ç”Ÿäº§æŠ¥å·¥ |
| ä»»åŠ¡æŠ•æ–™ | task_issue | æŠ•料记录 |
| ç”Ÿäº§ä»»åŠ¡ | task | æŽ’产任务 |
| å·¥å• | work_order + bom + å‚数记录 | ç”Ÿäº§å·¥å• |
| æµè½¬å¡ | card + å·¥åºè®°å½• | ç”Ÿäº§æµè½¬å¡ |
| å®‰ç¯ | andon_config, andon_record | å®‰ç¯å‘¼å« |
| ä¸Šä¸‹å·¥ | work_record, work_record_log | å·¥ä½œç«™ç»‘定/上下工 |
| å·¥è‰ºè·¯çº¿ | route + å·¥åº/产品/BOM | å·¥è‰ºè·¯çº¿åŠå…³è” |
| å·¥åº | process + å†…容 | ç”Ÿäº§å·¥åº |
| äº§å“BOM/SIP/SOP | product_bom, product_sip, product_sop | äº§å“æ–‡æ¡£ |
| ç‰©æ–™ | item + åˆ†ç±»/批次配置 | MES ç‰©æ–™ |
| è®¡é‡å•位 | unit_measure | MES è®¡é‡å•位 |
| å®¢æˆ·/供应商 | client, vendor | MES å®¢æˆ·/供应商 |
| è½¦é—´/工作站 | workshop, workstation + èµ„源 | MES è½¦é—´/工作站 |
| ç¼–码规则 | auto_code_rule + ç»„成/记录 | ç¼–码规则 |
## å½“前数据量
| è¡¨ | è®°å½•æ•° | è¡¨ | è®°å½•æ•° |
|----|--------|----|--------|
| mes_pro_work_order | 2 | mes_pro_task | 3 |
| mes_pro_feedback | 13 | mes_pro_task_issue | 0 |
| mes_pro_process | 6 | mes_pro_route | 2 |
| mes_pro_route_process | 5 | mes_pro_route_product | 2 |
| mes_pro_route_product_bom | 6 | mes_pro_work_order_bom | 3 |
| mes_wm_product_issue | 4 | mes_wm_return_issue | 3 |
| mes_wm_item_consume | 5 | mes_wm_transaction(生产) | 9 |
| mes_wm_stock_reserve | 1 | mes_md_item | 9 |
| mes_md_product_bom | 11 | mes_md_workstation | 1 |
| mes_md_unit_measure | 2 | mes_md_item_type | 5 |
## æ¸…理 SQL
```sql
-- ============================================================
-- MES å…¨éƒ¨ä¸šåŠ¡æ•°æ®æ¸…ç†è„šæœ¬
-- æ‰§è¡Œå‰è¯·ç¡®è®¤ï¼šè¿™äº›æ•°æ®å¯ä»¥åˆ é™¤ï¼Œä¸å¯æ¢å¤ï¼
-- ============================================================
-- ==================== 1. åº“存占用(生产相关) ====================
DELETE FROM mes_wm_stock_reserve
WHERE biz_type IN (4, 5);  -- 4=生产领料, 5=生产产出
-- ==================== 2. åº“存事务流水(生产相关 biz_type) ====================
-- 116=生产领料, 117=生产退料, 118=生产入库, 123=生产消耗, 124=产品产出
DELETE FROM mes_wm_transaction
WHERE biz_type IN (116, 117, 118, 123, 124);
-- ==================== 3. ç‰©æ–™æ¶ˆè€—单 ====================
DELETE FROM mes_wm_item_consume_detail;
DELETE FROM mes_wm_item_consume_line;
DELETE FROM mes_wm_item_consume;
-- ==================== 4. ç”Ÿäº§äº§å‡ºå• ====================
DELETE FROM mes_wm_product_produce_detail;
DELETE FROM mes_wm_product_produce_line;
DELETE FROM mes_wm_product_produce;
-- ==================== 5. ç”Ÿäº§å…¥åº“单 ====================
DELETE FROM mes_wm_product_receipt_detail;
DELETE FROM mes_wm_product_receipt_line;
DELETE FROM mes_wm_product_receipt;
-- ==================== 6. ç”Ÿäº§é€€æ–™å• ====================
DELETE FROM mes_wm_return_issue_detail;
DELETE FROM mes_wm_return_issue_line;
DELETE FROM mes_wm_return_issue;
-- ==================== 7. ç”Ÿäº§é¢†æ–™å‡ºåº“单 ====================
DELETE FROM mes_wm_product_issue_detail;
DELETE FROM mes_wm_product_issue_line;
DELETE FROM mes_wm_product_issue;
-- ==================== 8. è´¨æ£€è®°å½• ====================
DELETE FROM mes_qc_indicator_result_detail;
DELETE FROM mes_qc_indicator_result;
DELETE FROM mes_qc_defect_record;
DELETE FROM mes_qc_ipqc_line;
DELETE FROM mes_qc_ipqc;
DELETE FROM mes_qc_rqc_line;
DELETE FROM mes_qc_rqc;
DELETE FROM mes_qc_iqc_line;
DELETE FROM mes_qc_iqc;
DELETE FROM mes_qc_oqc_line;
DELETE FROM mes_qc_oqc;
-- ==================== 9. ç”Ÿäº§æŠ¥å·¥ ====================
DELETE FROM mes_pro_feedback;
-- ==================== 10. ä»»åŠ¡æŠ•æ–™è®°å½• ====================
DELETE FROM mes_pro_task_issue;
-- ==================== 11. ç”Ÿäº§ä»»åŠ¡ ====================
DELETE FROM mes_pro_task;
-- ==================== 12. å·¥å•参数记录 ====================
DELETE FROM mes_pro_work_order_param_record_detail;
DELETE FROM mes_pro_work_order_param_record;
-- ==================== 13. å·¥å• BOM ====================
DELETE FROM mes_pro_work_order_bom;
-- ==================== 14. ç”Ÿäº§å·¥å• ====================
DELETE FROM mes_pro_work_order;
-- ==================== 15. æµè½¬å¡ ====================
DELETE FROM mes_pro_card_process;
DELETE FROM mes_pro_card;
-- ==================== 16. å®‰ç¯ ====================
DELETE FROM mes_pro_andon_record;
DELETE FROM mes_pro_andon_config;
-- ==================== 17. ä¸Šä¸‹å·¥è®°å½• ====================
DELETE FROM mes_pro_work_record_log;
DELETE FROM mes_pro_work_record;
-- ==================== 18. å·¥è‰ºè·¯çº¿äº§å“ BOM ====================
DELETE FROM mes_pro_route_product_bom;
-- ==================== 19. å·¥è‰ºè·¯çº¿äº§å“ ====================
DELETE FROM mes_pro_route_product;
-- ==================== 20. å·¥è‰ºè·¯çº¿å·¥åº ====================
DELETE FROM mes_pro_route_process;
-- ==================== 21. å·¥è‰ºè·¯çº¿ ====================
DELETE FROM mes_pro_route;
-- ==================== 22. å·¥åºå†…容 ====================
DELETE FROM mes_pro_process_content;
-- ==================== 23. ç”Ÿäº§å·¥åº ====================
DELETE FROM mes_pro_process;
-- ==================== 24. äº§å“ SOP ====================
DELETE FROM mes_md_product_sop;
-- ==================== 25. äº§å“ SIP ====================
DELETE FROM mes_md_product_sip;
-- ==================== 26. äº§å“ BOM ====================
DELETE FROM mes_md_product_bom;
-- ==================== 27. ç‰©æ–™æ‰¹æ¬¡é…ç½® ====================
DELETE FROM mes_md_item_batch_config;
-- ==================== 28. MES ç‰©æ–™ ====================
DELETE FROM mes_md_item;
-- ==================== 29. ç‰©æ–™åˆ†ç±» ====================
DELETE FROM mes_md_item_type;
-- ==================== 30. è®¡é‡å•位 ====================
DELETE FROM mes_md_unit_measure;
-- ==================== 31. å®¢æˆ· ====================
DELETE FROM mes_md_client;
-- ==================== 32. ä¾›åº”商 ====================
DELETE FROM mes_md_vendor;
-- ==================== 33. å·¥ä½œç«™èµ„源 ====================
DELETE FROM mes_md_workstation_machine;
DELETE FROM mes_md_workstation_tool;
DELETE FROM mes_md_workstation_worker;
-- ==================== 34. å·¥ä½œç«™ ====================
DELETE FROM mes_md_workstation;
-- ==================== 35. è½¦é—´ ====================
DELETE FROM mes_md_workshop;
-- ==================== 36. è´¨æ£€æ–¹æ¡ˆ ====================
DELETE FROM mes_qc_template_indicator;
DELETE FROM mes_qc_template_item;
DELETE FROM mes_qc_template;
-- ==================== 37. ç¼ºé™·ç±»åž‹ ====================
DELETE FROM mes_qc_defect;
-- ==================== 38. è´¨æ£€æŒ‡æ ‡ ====================
DELETE FROM mes_qc_indicator;
-- ==================== 39. ç¼–码规则 ====================
DELETE FROM mes_md_auto_code_record;
DELETE FROM mes_md_auto_code_part;
DELETE FROM mes_md_auto_code_rule;
```
## éªŒè¯ SQL
```sql
-- éªŒè¯æ¸…理结果,预期全部为 0
SELECT 'mes_pro_work_order' as tbl, COUNT(*) as cnt FROM mes_pro_work_order WHERE deleted=0
UNION ALL SELECT 'mes_pro_task', COUNT(*) FROM mes_pro_task WHERE deleted=0
UNION ALL SELECT 'mes_pro_feedback', COUNT(*) FROM mes_pro_feedback WHERE deleted=0
UNION ALL SELECT 'mes_pro_task_issue', COUNT(*) FROM mes_pro_task_issue WHERE deleted=0
UNION ALL SELECT 'mes_pro_process', COUNT(*) FROM mes_pro_process WHERE deleted=0
UNION ALL SELECT 'mes_pro_route', COUNT(*) FROM mes_pro_route WHERE deleted=0
UNION ALL SELECT 'mes_pro_route_process', COUNT(*) FROM mes_pro_route_process WHERE deleted=0
UNION ALL SELECT 'mes_pro_route_product', COUNT(*) FROM mes_pro_route_product WHERE deleted=0
UNION ALL SELECT 'mes_pro_route_product_bom', COUNT(*) FROM mes_pro_route_product_bom WHERE deleted=0
UNION ALL SELECT 'mes_wm_product_issue', COUNT(*) FROM mes_wm_product_issue WHERE deleted=0
UNION ALL SELECT 'mes_wm_return_issue', COUNT(*) FROM mes_wm_return_issue WHERE deleted=0
UNION ALL SELECT 'mes_wm_item_consume', COUNT(*) FROM mes_wm_item_consume WHERE deleted=0
UNION ALL SELECT 'mes_wm_product_produce', COUNT(*) FROM mes_wm_product_produce WHERE deleted=0
UNION ALL SELECT 'mes_wm_product_receipt', COUNT(*) FROM mes_wm_product_receipt WHERE deleted=0
UNION ALL SELECT 'mes_md_item', COUNT(*) FROM mes_md_item WHERE deleted=0
UNION ALL SELECT 'mes_md_item_type', COUNT(*) FROM mes_md_item_type WHERE deleted=0
UNION ALL SELECT 'mes_md_product_bom', COUNT(*) FROM mes_md_product_bom WHERE deleted=0
UNION ALL SELECT 'mes_md_unit_measure', COUNT(*) FROM mes_md_unit_measure WHERE deleted=0
UNION ALL SELECT 'mes_md_client', COUNT(*) FROM mes_md_client WHERE deleted=0
UNION ALL SELECT 'mes_md_vendor', COUNT(*) FROM mes_md_vendor WHERE deleted=0
UNION ALL SELECT 'mes_md_workstation', COUNT(*) FROM mes_md_workstation WHERE deleted=0
UNION ALL SELECT 'mes_md_workshop', COUNT(*) FROM mes_md_workshop WHERE deleted=0
UNION ALL SELECT 'mes_wm_stock_reserve(生产)', COUNT(*) FROM mes_wm_stock_reserve WHERE deleted=0 AND biz_type IN (4, 5)
UNION ALL SELECT 'mes_wm_transaction(生产)', COUNT(*) FROM mes_wm_transaction WHERE deleted=0 AND biz_type IN (116, 117, 118, 123, 124);
```
## æ³¨æ„äº‹é¡¹
- ä»¥ä¸Š SQL ä½¿ç”¨ç‰©ç†åˆ é™¤ï¼ˆDELETE),**执行后不可恢复**
- æ‰§è¡Œé¡ºåºå¾ˆé‡è¦ï¼šå…ˆåˆ å­è¡¨/明细,再删主表,避免外键约束报错
- **库存记录(mes_wm_material_stock)不清理**:清理事务流水后,库存台账可能有残留数量,如需重置需单独处理
- **仓库/库区/库位(mes_wm_warehouse ç­‰)不清理**:基础设施数据保留
- **批次(mes_wm_batch)不清理**:批次基础数据保留
- **采购/销售/调拨等其他业务数据不清理**:只清理 MES ç”Ÿäº§ç›¸å…³
docs/production_feedback_integration.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,599 @@
# ç”Ÿäº§æŠ¥å·¥ - å‰ç«¯è”调方案
## æ¶‰åŠé¡µé¢
- å¾…报工台账(报工入口,按工序筛选任务,选择任务进行报工)
- ç”Ÿäº§æŠ¥å·¥ï¼ˆæ–°å¢ž/编辑报工单)
- æŠ¥å·¥å®¡æ‰¹ï¼ˆå®¡æ‰¹/驳回操作)
- æŠ¥å·¥è¯¦æƒ…(查看报工信息 + è¿½æº¯äº§å‡º/消耗/质检)
## ä¸šåŠ¡æµç¨‹ä¸Žæ•°æ®å¸¦å…¥
```
1. å¾…报工台账(按工序筛选) â†’ é€‰æ‹©ä»»åŠ¡ â†’ æŠ¥å·¥
   é€‰æ‹©ä»»åŠ¡åŽï¼Œè‡ªåŠ¨å¸¦å…¥ï¼štaskId、workOrderId、routeId、processId、
   workstationId、itemId、scheduledQuantity(排产数量)
2. å¡«å†™æŠ¥å·¥ â†’ ä¿å­˜(草稿) â†’ æäº¤(审批中) â†’ å®¡æ‰¹é€šè¿‡(已完成/待检验)
   - è´¨æ£€å·¥åºï¼šåªéœ€å¡«æŠ¥å·¥æ•°é‡ï¼Œåˆæ ¼/不合格由质检结果回填
   - éžè´¨æ£€å·¥åºï¼šéœ€å¡«åˆæ ¼å“ + ä¸è‰¯å“æ•°é‡
3. å®¡æ‰¹é©³å›ž â†’ é€€å›žè‰ç¨¿ â†’ ä¿®æ”¹åŽé‡æ–°æäº¤
```
## çŠ¶æ€æµè½¬
### ç”Ÿäº§ä»»åŠ¡çŠ¶æ€
| çŠ¶æ€å€¼ | çŠ¶æ€å | è¯´æ˜Ž |
|--------|--------|------|
| 0 | è‰ç¨¿(进行中) | å¯æŠ¥å·¥ |
| 4 | å·²å®Œæˆ | ä¸å¯æŠ¥å·¥ |
| 5 | å·²å–消 | ä¸å¯æŠ¥å·¥ |
### ç”Ÿäº§æŠ¥å·¥çŠ¶æ€
| çŠ¶æ€å€¼ | çŠ¶æ€å | å¯æ‰§è¡Œæ“ä½œ | è¯´æ˜Ž |
|--------|--------|-----------|------|
| 0 | è‰ç¨¿ | ç¼–辑、删除、提交 | åˆå§‹çŠ¶æ€ |
| 2 | å®¡æ‰¹ä¸­ | å®¡æ‰¹ã€é©³å›ž | å·²æäº¤å¾…审批 |
| 3 | å¾…检验 | â€” | å…³é”®+质检工序审批后进入,等IPQC回调 |
| 4 | å·²å®Œæˆ | â€” | æœ€ç»ˆçŠ¶æ€ |
---
## API
### ä¸€ã€å¾…报工台账接口
#### 1.1 å¾…报工台账(分页)
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| GET | /mes/pro/task/pending-feedback-page | å·²æŽ’产且仍有待报工量的任务 |
**说明:** æ­¤æŽ¥å£ä»…返回状态=进行中(0)且 `quantity - produced_quantity > 0` çš„任务,是报工页面的入口数据源。
**请求参数:**
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| processId | Long | å¦ | å·¥åºç¼–号(按工序筛选任务) |
| routeId | Long | å¦ | å·¥è‰ºè·¯çº¿ç¼–号 |
| workOrderId | Long | å¦ | ç”Ÿäº§å·¥å•编号 |
| workstationId | Long | å¦ | å·¥ä½œç«™ç¼–号 |
| code | String | å¦ | ä»»åŠ¡ç¼–ç ï¼ˆæ¨¡ç³ŠåŒ¹é…ï¼‰ |
| name | String | å¦ | ä»»åŠ¡åç§°ï¼ˆæ¨¡ç³ŠåŒ¹é…ï¼‰ |
| checkFlag | Boolean | å¦ | æ˜¯å¦è´¨æ£€å·¥åº |
| createTime | String[] | å¦ | åˆ›å»ºæ—¶é—´èŒƒå›´ |
| pageNo | Integer | æ˜¯ | é¡µç ï¼ˆé»˜è®¤1) |
| pageSize | Integer | æ˜¯ | æ¯é¡µæ¡æ•°ï¼ˆé»˜è®¤10) |
**响应字段:**
| å­—段 | ç±»åž‹ | è¯´æ˜Ž |
|------|------|------|
| id | Long | ä»»åŠ¡ç¼–å· |
| code | String | ä»»åŠ¡ç¼–ç  |
| name | String | ä»»åŠ¡åç§° |
| workOrderId | Long | å·¥å•编号 |
| workOrderCode | String | å·¥å•编码 |
| workOrderName | String | å·¥å•名称 |
| workstationId | Long | å·¥ä½œç«™ç¼–号 |
| workstationCode | String | å·¥ä½œç«™ç¼–码 |
| workstationName | String | å·¥ä½œç«™åç§° |
| routeId | Long | å·¥è‰ºè·¯çº¿ç¼–号 |
| processId | Long | å·¥åºç¼–号 |
| processName | String | å·¥åºåç§° |
| itemId | Long | äº§å“ç‰©æ–™ç¼–号 |
| itemCode | String | ç‰©æ–™ç¼–码 |
| itemName | String | ç‰©æ–™åç§° |
| itemSpecification | String | è§„格型号 |
| unitMeasureName | String | è®¡é‡å•位 |
| quantity | BigDecimal | æŽ’产数量 |
| producedQuantity | BigDecimal | å·²ç”Ÿäº§æ•°é‡ |
| qualifyQuantity | BigDecimal | åˆæ ¼å“æ•°é‡ |
| unqualifyQuantity | BigDecimal | ä¸è‰¯å“æ•°é‡ |
| pendingQuantity | BigDecimal | å¾…报工数量(= æŽ’产 - å·²ç”Ÿäº§ï¼‰ |
| inTransitQuantity | BigDecimal | åœ¨é€”报工数量(草稿/审批中/待检验状态的报工量合计) |
| reportableQuantity | BigDecimal | å¯æŠ¥å·¥æ•°é‡ï¼ˆ= æŽ’产 - å·²ç”Ÿäº§ - åœ¨é€”) |
| feedbackRate | BigDecimal | æŠ¥å·¥å®Œæˆçއ(%),如 50.00 è¡¨ç¤º 50% |
| checkFlag | Boolean | æ˜¯å¦è´¨æ£€å·¥åº |
| clientId | Long | å®¢æˆ·ç¼–号 |
| clientName | String | å®¢æˆ·åç§° |
| startTime | LocalDateTime | å¼€å§‹ç”Ÿäº§æ—¶é—´ |
| endTime | LocalDateTime | ç»“束生产时间 |
| duration | Integer | ç”Ÿäº§æ—¶é•¿ï¼ˆå·¥ä½œæ—¥ï¼‰ |
| requestDate | LocalDateTime | éœ€æ±‚日期 |
| status | Integer | ä»»åŠ¡çŠ¶æ€ |
| remark | String | å¤‡æ³¨ |
| createTime | LocalDateTime | åˆ›å»ºæ—¶é—´ |
**响应示例:**
```json
{
  "code": 0,
  "data": {
    "list": [
      {
        "id": 1,
        "code": "PT202503150001",
        "name": "注塑任务",
        "workOrderId": 10,
        "workOrderCode": "MO202503120008",
        "workOrderName": "博世螺丝刀",
        "workstationId": 5,
        "workstationCode": "WS-001",
        "workstationName": "注塑工作站",
        "routeId": 3,
        "processId": 7,
        "processName": "注塑",
        "itemId": 75,
        "itemCode": "I-075",
        "itemName": "博世螺丝刀",
        "itemSpecification": "一字型",
        "unitMeasureName": "个",
        "quantity": 5000.00,
        "producedQuantity": 2000.00,
        "qualifyQuantity": 1950.00,
        "unqualifyQuantity": 50.00,
        "pendingQuantity": 3000.00,
        "inTransitQuantity": 500.00,
        "reportableQuantity": 2500.00,
        "feedbackRate": 40.00,
        "checkFlag": true,
        "status": 0,
        "createTime": "2025-03-15 10:00:00"
      }
    ],
    "total": 1
  }
}
```
---
#### 1.2 æŸ¥è¯¢ä»»åŠ¡è¯¦æƒ…
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| GET | /mes/pro/task/get | æ ¹æ®ID获取任务详情 |
**请求参数:**
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| id | Long | æ˜¯ | ä»»åŠ¡ç¼–å· |
**响应字段:** åŒ 1.1 å“åº”字段(单条对象,非分页)
**响应示例:**
```json
{
  "code": 0,
  "data": {
    "id": 1,
    "code": "PT202503150001",
    "reportableQuantity": 2500.00,
    "checkFlag": true
  }
}
```
---
### äºŒã€ç”Ÿäº§æŠ¥å·¥æŽ¥å£
#### 2.1 åˆ›å»ºæŠ¥å·¥
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| POST | /mes/pro-feedback/create | æ–°å»ºæŠ¥å·¥å•(状态=草稿) |
**请求参数:**
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| code | String | æ˜¯ | æŠ¥å·¥å•编号 |
| type | Integer | æ˜¯ | æŠ¥å·¥ç±»åž‹ï¼š1=自行报工,2=统一报工 |
| workstationId | Long | æ˜¯ | å·¥ä½œç«™ç¼–号 |
| routeId | Long | æ˜¯ | å·¥è‰ºè·¯çº¿ç¼–号 |
| processId | Long | æ˜¯ | å·¥åºç¼–号 |
| workOrderId | Long | æ˜¯ | ç”Ÿäº§å·¥å•编号 |
| taskId | Long | æ˜¯ | ç”Ÿäº§ä»»åŠ¡ç¼–å· |
| itemId | Long | æ˜¯ | äº§å“ç‰©æ–™ç¼–号 |
| feedbackQuantity | BigDecimal | æ˜¯ | æœ¬æ¬¡æŠ¥å·¥æ•°é‡ï¼ˆå¿…é¡» > 0) |
| feedbackUserId | Long | æ˜¯ | æŠ¥å·¥äººç¼–号 |
| feedbackTime | LocalDateTime | æ˜¯ | æŠ¥å·¥æ—¶é—´ |
| approveUserId | Long | æ˜¯ | å®¡æ ¸äººç¼–号 |
| qualifiedQuantity | BigDecimal | æ¡ä»¶å¿…å¡« | åˆæ ¼å“æ•°é‡ï¼ˆéžè´¨æ£€å·¥åºå¿…填) |
| unqualifiedQuantity | BigDecimal | æ¡ä»¶å¿…å¡« | ä¸è‰¯å“æ•°é‡ï¼ˆéžè´¨æ£€å·¥åºå¿…填) |
| uncheckQuantity | BigDecimal | å¦ | å¾…检测数量 |
| laborScrapQuantity | BigDecimal | å¦ | å·¥åºŸæ•°é‡ |
| materialScrapQuantity | BigDecimal | å¦ | æ–™åºŸæ•°é‡ |
| otherScrapQuantity | BigDecimal | å¦ | å…¶ä»–废品数量 |
| scheduledQuantity | BigDecimal | å¦ | æŽ’产数量 |
| expireDate | LocalDateTime | å¦ | è¿‡æœŸæ—¥æœŸ |
| lotNumber | String | å¦ | ç”Ÿäº§æ‰¹å· |
| remark | String | å¦ | å¤‡æ³¨ |
**响应:** `Long`(报工记录ID)
```json
{
  "code": 0,
  "data": 1024
}
```
**数据带入说明:**
从待报工台账选择任务后,以下字段自动带入报工表单,无需用户手动填写:
| æŠ¥å·¥è¡¨å•字段 | æ•°æ®æ¥æº | æ¥æºå­—段 |
|-------------|---------|---------|
| taskId | ä»»åŠ¡ | id |
| workOrderId | ä»»åŠ¡ | workOrderId |
| routeId | ä»»åŠ¡ | routeId |
| processId | ä»»åŠ¡ | processId | ä»»åŠ¡ | processId |
| workstationId | ä»»åŠ¡ | workstationId |
| itemId | ä»»åŠ¡ | itemId |
| scheduledQuantity | ä»»åŠ¡ | quantity(排产数量) |
用户需手动填写:报工数量、合格/不良品数量、报工人、报工时间、审核人等。
---
#### 2.2 æ›´æ–°æŠ¥å·¥
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| PUT | /mes/pro-feedback/update | ä¿®æ”¹æŠ¥å·¥å•(仅草稿状态可修改) |
**请求参数:** åŒ 2.1(需额外传 id)
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| id | Long | æ˜¯ | æŠ¥å·¥ç¼–号 |
**响应:** `{ "code": 0, "data": true }`
---
#### 2.3 æäº¤æŠ¥å·¥
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| PUT | /mes/pro-feedback/submit | è‰ç¨¿ â†’ å®¡æ‰¹ä¸­ |
**请求参数:**
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| id | Long | æ˜¯ | æŠ¥å·¥ç¼–号 |
**响应:** `{ "code": 0, "data": true }`
**状态变化:** è‰ç¨¿(0) â†’ å®¡æ‰¹ä¸­(2)
---
#### 2.4 å®¡æ‰¹æŠ¥å·¥
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| PUT | /mes/pro-feedback/approve | å®¡æ‰¹é€šè¿‡ï¼Œè§¦å‘入库/扣料/质检 |
**请求参数:**
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| id | Long | æ˜¯ | æŠ¥å·¥ç¼–号 |
**响应:**
```json
{
  "code": 0,
  "data": true   // true=审批完成(已入库),false=进入待检验(等质检回调)
}
```
**状态变化:**
| åœºæ™¯ | çŠ¶æ€å˜åŒ– | è¿”回值 |
|------|---------|--------|
| éžå…³é”®å·¥åº | å®¡æ‰¹ä¸­(2) â†’ å·²å®Œæˆ(4) | true |
| å…³é”®+非质检工序 | å®¡æ‰¹ä¸­(2) â†’ å·²å®Œæˆ(4),同时入库 | true |
| å…³é”®+质检工序 | å®¡æ‰¹ä¸­(2) â†’ å¾…检验(3),等IPQC回调 | false |
---
#### 2.5 é©³å›žæŠ¥å·¥
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| PUT | /mes/pro-feedback/reject | å®¡æ‰¹ä¸­ â†’ è‰ç¨¿ |
**请求参数:**
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| id | Long | æ˜¯ | æŠ¥å·¥ç¼–号 |
**响应:** `{ "code": 0, "data": true }`
**状态变化:** å®¡æ‰¹ä¸­(2) â†’ è‰ç¨¿(0)
---
#### 2.6 æŸ¥è¯¢æŠ¥å·¥è¯¦æƒ…
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| GET | /mes/pro-feedback/get | èŽ·å–å•æ¡æŠ¥å·¥è¯¦æƒ… |
**请求参数:**
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| id | Long | æ˜¯ | æŠ¥å·¥ç¼–号 |
**响应字段:**
| å­—段 | ç±»åž‹ | è¯´æ˜Ž |
|------|------|------|
| id | Long | ç¼–号 |
| code | String | æŠ¥å·¥å•编号 |
| type | Integer | æŠ¥å·¥ç±»åž‹ï¼š1=自行报工,2=统一报工 |
| channel | String | æŠ¥å·¥é€”径 |
| feedbackTime | LocalDateTime | æŠ¥å·¥æ—¶é—´ |
| workstationId | Long | å·¥ä½œç«™ç¼–号 |
| workstationCode | String | å·¥ä½œç«™ç¼–码 |
| workstationName | String | å·¥ä½œç«™åç§° |
| routeId | Long | å·¥è‰ºè·¯çº¿ç¼–号 |
| routeCode | String | å·¥è‰ºè·¯çº¿ç¼–码 |
| processId | Long | å·¥åºç¼–号 |
| processCode | String | å·¥åºç¼–码 |
| processName | String | å·¥åºåç§° |
| checkFlag | Boolean | æ˜¯å¦éœ€è¦æ£€éªŒ |
| workOrderId | Long | å·¥å•编号 |
| workOrderCode | String | å·¥å•编码 |
| workOrderName | String | å·¥å•名称 |
| taskId | Long | ä»»åŠ¡ç¼–å· |
| taskCode | String | ä»»åŠ¡ç¼–ç  |
| itemId | Long | ç‰©æ–™ç¼–号 |
| itemCode | String | ç‰©æ–™ç¼–码 |
| itemName | String | ç‰©æ–™åç§° |
| itemSpecification | String | è§„格型号 |
| unitMeasureId | Long | å•位编号 |
| unitMeasureName | String | å•位名称 |
| scheduledQuantity | BigDecimal | æŽ’产数量 |
| feedbackQuantity | BigDecimal | æŠ¥å·¥æ•°é‡ |
| qualifiedQuantity | BigDecimal | åˆæ ¼å“æ•°é‡ |
| unqualifiedQuantity | BigDecimal | ä¸è‰¯å“æ•°é‡ |
| uncheckQuantity | BigDecimal | å¾…检测数量 |
| laborScrapQuantity | BigDecimal | å·¥åºŸæ•°é‡ |
| materialScrapQuantity | BigDecimal | æ–™åºŸæ•°é‡ |
| otherScrapQuantity | BigDecimal | å…¶ä»–废品数量 |
| feedbackUserId | Long | æŠ¥å·¥äººç¼–号 |
| feedbackUserNickname | String | æŠ¥å·¥äººæ˜µç§° |
| approveUserId | Long | å®¡æ ¸äººç¼–号 |
| approveUserNickname | String | å®¡æ ¸äººæ˜µç§° |
| status | Integer | çŠ¶æ€ |
| remark | String | å¤‡æ³¨ |
| createTime | LocalDateTime | åˆ›å»ºæ—¶é—´ |
---
#### 2.7 æŠ¥å·¥åˆ†é¡µæŸ¥è¯¢
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| GET | /mes/pro-feedback/page | åˆ†é¡µæŸ¥è¯¢æŠ¥å·¥åˆ—表 |
**请求参数:**
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| code | String | å¦ | æŠ¥å·¥å•编号(模糊匹配) |
| type | Integer | å¦ | æŠ¥å·¥ç±»åž‹ |
| workOrderId | Long | å¦ | ç”Ÿäº§å·¥å•编号 |
| taskId | Long | å¦ | ç”Ÿäº§ä»»åŠ¡ç¼–å·ï¼ˆç²¾ç¡®åŒ¹é…ï¼‰ |
| taskCode | String | å¦ | ç”Ÿäº§ä»»åŠ¡ç¼–ç ï¼ˆæ¨¡ç³ŠåŒ¹é…ï¼Œå¦‚ "PT202503") |
| status | Integer | å¦ | çŠ¶æ€ |
| itemId | Long | å¦ | äº§å“ç‰©æ–™ç¼–号 |
| feedbackUserId | Long | å¦ | æŠ¥å·¥äººç¼–号 |
| creator | String | å¦ | è®°å½•人编号 |
| feedbackTime | String[] | å¦ | æŠ¥å·¥æ—¶é—´èŒƒå›´ |
| pageNo | Integer | æ˜¯ | é¡µç  |
| pageSize | Integer | æ˜¯ | æ¯é¡µæ¡æ•° |
**响应字段:** åŒ 2.6(分页列表)
**查询说明:**
| åœºæ™¯ | ç”¨æ³• | ç¤ºä¾‹ |
|------|------|------|
| æŒ‰ä»»åŠ¡ç¼–ç æ¨¡ç³ŠæŸ¥è¯¢ | ä¼  taskCode | `taskCode=PT202503` åŒ¹é… PT202503150001、PT202503150002 ç­‰ |
| æŒ‰ä»»åŠ¡ID精确查询 | ä¼  taskId | `taskId=1024` |
| åŒæ—¶ä¼  taskId å’Œ taskCode | taskId ä¼˜å…ˆç²¾ç¡®åŒ¹é…ï¼ŒtaskCode é¢å¤–模糊过滤 | ä¸€èˆ¬ä¸åŒæ—¶ä¼  |
| ä¸ä¼ ä»»åŠ¡ç›¸å…³å‚æ•° | æŸ¥è¯¢æ‰€æœ‰æŠ¥å·¥ | â€” |
---
#### 2.8 æŒ‰ä»»åŠ¡æŸ¥è¯¢æŠ¥å·¥åŽ†å²
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| GET | /mes/pro-feedback/list-by-task | æŸ¥è¯¢æŸä»»åŠ¡ä¸‹æ‰€æœ‰æŠ¥å·¥è®°å½• |
**请求参数:**
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| taskId | Long | æ˜¯ | ä»»åŠ¡ç¼–å· |
**响应字段:** `List<MesProFeedbackRespVO>`(同 2.6 å­—段,数组形式)
**说明:** è¿”回该任务下所有状态的报工记录(草稿/审批中/待检验/已完成),用于在任务详情中展示报工历史。
---
#### 2.9 æŠ¥å·¥è¿½æº¯ä¿¡æ¯
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| GET | /mes/pro-feedback/trace/{id} | æŠ¥å·¥ + äº§å‡º + æ¶ˆè€— + IPQC检验 |
**请求参数:**
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| id | Long | æ˜¯ | æŠ¥å·¥ç¼–号(路径参数) |
**响应字段:**
| å­—段 | ç±»åž‹ | è¯´æ˜Ž |
|------|------|------|
| feedback | MesProFeedbackRespVO | æŠ¥å·¥ä¿¡æ¯ï¼ˆåŒ 2.6 å­—段) |
| productProduce | Object | ç”Ÿäº§äº§å‡ºå•(关键工序才有,否则 null) |
| productProduceLines | Array | äº§å‡ºæ˜Žç»†è¡Œï¼ˆæŒ‰åˆæ ¼/不合格/待检验拆行) |
| itemConsume | Object | ç‰©æ–™æ¶ˆè€—单(倒冲工序才有,否则 null) |
| itemConsumeLines | Array | æ¶ˆè€—明细行(按 BOM ç‰©æ–™æ‹†è¡Œï¼‰ |
| ipqcList | Array | IPQC è¿‡ç¨‹æ£€éªŒå•列表(关键+需检验工序才有) |
**响应示例:**
```json
{
  "code": 0,
  "data": {
    "feedback": {
      "id": 1024,
      "code": "FB202503160001",
      "status": 4,
      "feedbackQuantity": 500.00,
      "qualifiedQuantity": 490.00,
      "unqualifiedQuantity": 10.00
    },
    "productProduce": {
      "id": 50,
      "code": "PP202503160001"
    },
    "productProduceLines": [
      { "id": 101, "qualityStatus": 1, "quantity": 490.00 },
      { "id": 102, "qualityStatus": 2, "quantity": 10.00 }
    ],
    "itemConsume": null,
    "itemConsumeLines": [],
    "ipqcList": []
  }
}
```
---
#### 2.10 åˆ é™¤æŠ¥å·¥
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| DELETE | /mes/pro-feedback/delete | åˆ é™¤æŠ¥å·¥ï¼ˆä»…草稿状态可删除) |
**请求参数:**
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| id | Long | æ˜¯ | æŠ¥å·¥ç¼–号 |
**响应:** `{ "code": 0, "data": true }`
---
## å­—段展示规则
### å¾…报工台账列表
| å­—段 | å±•示位置 | è¯´æ˜Ž |
|------|----------|------|
| code | åˆ—表 | ä»»åŠ¡ç¼–ç  |
| workOrderCode + workOrderName | åˆ—表 | å·¥å•编码+名称 |
| workstationName | åˆ—表 | å·¥ä½œç«™ |
| processName | åˆ—表 | å·¥åºåç§° |
| itemCode + itemName | åˆ—表 | ç‰©æ–™ç¼–码+名称 |
| quantity | åˆ—表 | æŽ’产数量 |
| producedQuantity | åˆ—表 | å·²ç”Ÿäº§æ•°é‡ |
| reportableQuantity | åˆ—表 | å¯æŠ¥å·¥æ•°é‡ï¼ˆé«˜äº®æ˜¾ç¤ºï¼‰ |
| feedbackRate | åˆ—表 | æŠ¥å·¥å®Œæˆçއ(%) |
| checkFlag | åˆ—表 | æ˜¯å¦è´¨æ£€ï¼ˆæ ‡ç­¾å±•示) |
### æŠ¥å·¥è¡¨å•
| å­—段 | å±•示位置 | è¯´æ˜Ž |
|------|----------|------|
| taskId â†’ taskCode | è¡¨å•(只读) | ä»Žä»»åŠ¡è‡ªåŠ¨å¸¦å…¥ |
| workOrderId â†’ workOrderCode + workOrderName | è¡¨å•(只读) | ä»Žä»»åŠ¡è‡ªåŠ¨å¸¦å…¥ |
| processId â†’ processName | è¡¨å•(只读) | ä»Žä»»åŠ¡è‡ªåŠ¨å¸¦å…¥ |
| workstationId â†’ workstationName | è¡¨å•(只读) | ä»Žä»»åŠ¡è‡ªåŠ¨å¸¦å…¥ |
| itemId â†’ itemCode + itemName | è¡¨å•(只读) | ä»Žä»»åŠ¡è‡ªåŠ¨å¸¦å…¥ |
| scheduledQuantity | è¡¨å•(只读) | æŽ’产数量,从任务带入 |
| feedbackQuantity | è¡¨å•(可编辑) | æœ¬æ¬¡æŠ¥å·¥æ•°é‡ï¼Œå¿…å¡« |
| qualifiedQuantity | è¡¨å•(条件可编辑) | éžè´¨æ£€å·¥åºå¿…填;质检工序隐藏或禁用 |
| unqualifiedQuantity | è¡¨å•(条件可编辑) | éžè´¨æ£€å·¥åºå¿…填;质检工序隐藏或禁用 |
| feedbackUserId â†’ feedbackUserNickname | è¡¨å•(可编辑) | æŠ¥å·¥äººï¼Œé€‰æ‹©ç”¨æˆ· |
| feedbackTime | è¡¨å•(可编辑) | æŠ¥å·¥æ—¶é—´ï¼Œé»˜è®¤å½“前时间 |
| approveUserId â†’ approveUserNickname | è¡¨å•(可编辑) | å®¡æ ¸äººï¼Œé€‰æ‹©ç”¨æˆ· |
| remark | è¡¨å•(可编辑) | å¤‡æ³¨ |
### æŠ¥å·¥åˆ—表
| å­—段 | å±•示位置 | è¯´æ˜Ž |
|------|----------|------|
| code | åˆ—表 | æŠ¥å·¥å•编号 |
| workOrderCode | åˆ—表 | å·¥å•编码 |
| taskCode | åˆ—表 | ä»»åŠ¡ç¼–ç  |
| processName | åˆ—表 | å·¥åº |
| itemName | åˆ—表 | ç‰©æ–™ |
| feedbackQuantity | åˆ—表 | æŠ¥å·¥æ•°é‡ |
| qualifiedQuantity | åˆ—表 | åˆæ ¼å“ |
| unqualifiedQuantity | åˆ—表 | ä¸è‰¯å“ |
| feedbackUserNickname | åˆ—表 | æŠ¥å·¥äºº |
| approveUserNickname | åˆ—表 | å®¡æ ¸äºº |
| status | åˆ—表 | çŠ¶æ€ï¼ˆå­—å…¸æ ‡ç­¾ï¼Œä¸åŒçŠ¶æ€ä¸åŒé¢œè‰²ï¼‰ |
| feedbackTime | åˆ—表 | æŠ¥å·¥æ—¶é—´ |
**搜索条件:**
| å­—段 | æŽ§ä»¶ç±»åž‹ | è¯´æ˜Ž |
|------|---------|------|
| taskCode | è¾“入框 | ç”Ÿäº§ä»»åŠ¡ç¼–ç ï¼ˆæ¨¡ç³ŠåŒ¹é…ï¼Œä¸»è¦æœç´¢æ¡ä»¶ï¼‰ |
| code | è¾“入框 | æŠ¥å·¥å•编号(模糊匹配) |
| status | ä¸‹æ‹‰é€‰æ‹© | çŠ¶æ€ç­›é€‰ |
| feedbackTime | æ—¥æœŸèŒƒå›´ | æŠ¥å·¥æ—¶é—´èŒƒå›´ |
---
## ä¸šåŠ¡è§„åˆ™è¯´æ˜Ž
| åœºæ™¯ | è§„则 |
|------|------|
| æŠ¥å·¥æ•°é‡ä¸Šé™ | æŠ¥å·¥æ•°é‡ä¸åº”超过任务的 `reportableQuantity`(可报工数量),前端应做校验提示 |
| è´¨æ£€å·¥åºæŠ¥å·¥ | checkFlag=true æ—¶ï¼Œåªéœ€å¡«æŠ¥å·¥æ•°é‡ï¼Œåˆæ ¼/不合格数量由质检结果回填,前端应隐藏或禁用合格/不良品输入 |
| éžè´¨æ£€å·¥åºæŠ¥å·¥ | checkFlag=false æ—¶ï¼Œåˆæ ¼å“ + ä¸è‰¯å“æ•°é‡å¿…é¡» > 0,前端应校验 |
| è‰ç¨¿çŠ¶æ€æ“ä½œ | ä»…草稿(0)状态可编辑、删除 |
| å®¡æ‰¹ä¸­çŠ¶æ€æ“ä½œ | ä»…审批中(2)状态可审批、驳回 |
| å¾…检验状态 | å¾…检验(3)状态不可操作,等IPQC质检完成后系统自动回调更新为已完成(4) |
| å®¡æ‰¹è¿”回值 | approve æŽ¥å£è¿”回 true=已完成,false=待检验,前端应根据返回值展示不同提示 |
| ä»»åŠ¡æ•°æ®å¸¦å…¥ | ä»Žå¾…报工台账选择任务后,7个字段自动带入报工表单(见数据带入说明),用户不可修改 |
## æ³¨æ„äº‹é¡¹
- æŠ¥å·¥å•编号(code)由前端生成或调用自动编码接口获取,创建时必传
- æŠ¥å·¥ç±»åž‹(type)字段:1=自行报工,2=统一报工,前端根据业务场景选择
- å®¡æ‰¹æ˜¯ç®€å•权限控制(`mes:pro-feedback:approve`),非BPM工作流,有权限的用户直接点击审批即可
- å®¡æ‰¹é€šè¿‡åŽä¼šè‡ªåŠ¨è§¦å‘ï¼šå€’å†²æ‰£æ–™ï¼ˆå€’å†²å·¥åºï¼‰ã€äº§å‡ºå…¥åº“ï¼ˆå…³é”®å·¥åºï¼‰ï¼Œè¿™äº›æ˜¯åŽç«¯è‡ªåŠ¨å¤„ç†çš„ï¼Œå‰ç«¯æ— éœ€é¢å¤–æ“ä½œ
- å¾…检验状态的报工单,其合格/不合格数量会在IPQC质检完成后由后端自动回填,前端无需手动更新
- å¾…报工台账的 `reportableQuantity` å·²æ‰£é™¤åœ¨é€”报工量,前端可直接用此值做报工数量上限校验
- åˆ é™¤æ“ä½œä»…草稿状态可用,审批中/待检验/已完成状态不可删除
docs/work_order_param_record_integration.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,413 @@
# å·¥å•生产参数记录 - å‰ç«¯è”调方案
## æ¶‰åŠé¡µé¢
- å·¥å•详情(新增"参数记录"Tab,查看/管理工单的参数记录)
- å‚数记录新增/编辑弹窗(选择参数模板,填写参数值)
## ä¸šåŠ¡æµç¨‹ä¸Žæ•°æ®å¸¦å…¥
```
1. è¿›å…¥å·¥å•详情 â†’ åˆ‡æ¢åˆ°"参数记录"Tab â†’ æŸ¥çœ‹è¯¥å·¥å•所有参数记录
2. ç‚¹å‡»"新增参数记录" â†’ å¼¹çª—选择参数模板 â†’ è‡ªåŠ¨åŠ è½½æ¨¡æ¿ä¸‹çš„å‚æ•°åˆ—è¡¨ï¼ˆå‚æ•°åã€å•ä½ã€æ˜¯å¦å¿…å¡«ï¼‰
3. å¡«å†™å„参数值 â†’ ä¿å­˜ï¼ˆä¸€æ¡è®°å½• = æ¨¡æ¿ + æ‰€æœ‰å‚数的填写值)
4. åŒä¸€å·¥å•可多次新增参数记录(每班次/每批次记录一次)
5. ç¼–辑时只能修改填写值和备注,不能更换模板
6. åˆ é™¤è®°å½•时整条记录(含所有参数明细)一起删除
```
## æ•°æ®æ¨¡åž‹è¯´æ˜Ž
```
参数记录(主表)                   å‚数记录明细
┌─────────────────────┐          â”Œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”
│ id: 1                â”‚          â”‚ id: 101                  â”‚
│ workOrderId: 10      â”‚──1:N──→│ recordId: 1              â”‚
│ templateId: 2        â”‚          â”‚ paramName: æ¸©åº¦           â”‚
│ templateCode: CSMB002│          â”‚ paramType: 2 (数值)      â”‚
│ templateName: æ³¨å¡‘模板│          â”‚ unitMeasureName: â„ƒ       â”‚
│ recordNo: BATCH-001  â”‚          â”‚ paramValue: 200          â”‚
│ remark: é¦–件记录      â”‚          â”œâ”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”¤
└─────────────────────┘          â”‚ id: 102                  â”‚
                                  â”‚ recordId: 1              â”‚
                                  â”‚ paramName: åŽ‹åŠ›           â”‚
                                  â”‚ paramType: 2 (数值)      â”‚
                                  â”‚ unitMeasureName: MPa     â”‚
                                  â”‚ paramValue: 50           â”‚
                                  â””─────────────────────────┘
```
---
## API
### ä¸€ã€å‚数模板接口(已有,用于下拉选择)
#### 1.1 èŽ·å–å‚æ•°æ¨¡æ¿ç²¾ç®€åˆ—è¡¨
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| GET | /mes/pd/process-param-template/page | èŽ·å–å‚æ•°æ¨¡æ¿åˆ†é¡µåˆ—è¡¨ |
**请求参数:**
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| pageNo | Integer | æ˜¯ | é¡µç  |
| pageSize | Integer | æ˜¯ | æ¯é¡µæ¡æ•°ï¼ˆå»ºè®® 100,用于下拉) |
**响应字段(关键字段):**
| å­—段 | ç±»åž‹ | è¯´æ˜Ž |
|------|------|------|
| id | Long | æ¨¡æ¿ç¼–号 |
| templateCode | String | æ¨¡æ¿ç¼–码 |
| templateName | String | æ¨¡æ¿åç§° |
| templateType | Integer | æ¨¡æ¿ç±»åž‹ |
---
#### 1.2 èŽ·å–å‚æ•°æ¨¡æ¿è¯¦æƒ…ï¼ˆå«æ˜Žç»†ï¼Œç”¨äºŽåŠ è½½å‚æ•°åˆ—è¡¨ï¼‰
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| GET | /mes/pd/process-param-template/get | èŽ·å–æ¨¡æ¿ + å‚数明细 |
**请求参数:**
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| id | Long | æ˜¯ | æ¨¡æ¿ç¼–号 |
**响应示例:**
```json
{
  "code": 0,
  "data": {
    "id": 2,
    "templateCode": "CSMB202607100002",
    "templateName": "注塑参数模板",
    "templateType": 1,
    "details": [
      {
        "id": 3,
        "paramCode": "TEMP",
        "paramName": "温度",
        "paramType": 2,
        "unitMeasureId": 5,
        "unitMeasureName": "℃",
        "required": true
      },
      {
        "id": 4,
        "paramCode": "PRESS",
        "paramName": "压力",
        "paramType": 2,
        "unitMeasureId": 8,
        "unitMeasureName": "MPa",
        "required": true
      },
      {
        "id": 5,
        "paramCode": "TIME",
        "paramName": "保压时间",
        "paramType": 2,
        "unitMeasureId": 12,
        "unitMeasureName": "s",
        "required": false
      }
    ]
  }
}
```
**数据带入说明:** é€‰æ‹©æ¨¡æ¿åŽï¼Œå°† `details` åˆ—表渲染为表单行,每行展示参数名 + å•位 + è¾“入框,`required=true` çš„参数标记必填。
---
### äºŒã€å·¥å•参数记录接口
#### 2.1 åˆ›å»ºå‚数记录
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| POST | /mes/pro/work-order-param-record/create | ä¸ºå·¥å•创建一条参数记录 |
**请求参数:**
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| workOrderId | Long | æ˜¯ | å·¥å•编号(URL å‚数) |
| templateId | Long | æ˜¯ | å‚数模板编号 |
| recordNo | String | å¦ | è®°å½•序号/批次号 |
| remark | String | å¦ | å¤‡æ³¨ |
| details | Array | æ˜¯ | å‚数明细列表 |
| details[].templateDetailId | Long | æ˜¯ | æ¨¡æ¿æ˜Žç»†ç¼–号(来自模板详情的 details[].id) |
| details[].paramValue | String | æ¡ä»¶å¿…å¡« | å‚数值(必填参数不能为空) |
**请求示例:**
```
POST /mes/pro/work-order-param-record/create?workOrderId=10
{
  "templateId": 2,
  "recordNo": "BATCH-001",
  "remark": "首件记录",
  "details": [
    { "templateDetailId": 3, "paramValue": "200" },
    { "templateDetailId": 4, "paramValue": "50" },
    { "templateDetailId": 5, "paramValue": "30" }
  ]
}
```
**响应:**
```json
{
  "code": 0,
  "data": 1024
}
```
**接口调用时序:**
```
1. ç”¨æˆ·ç‚¹å‡»"新增参数记录"
2. è°ƒç”¨ GET /mes/pd/process-param-template/page èŽ·å–æ¨¡æ¿ä¸‹æ‹‰åˆ—è¡¨
3. ç”¨æˆ·é€‰æ‹©æ¨¡æ¿
4. è°ƒç”¨ GET /mes/pd/process-param-template/get?id=xxx èŽ·å–æ¨¡æ¿å‚æ•°æ˜Žç»†
5. å‰ç«¯å°† details æ¸²æŸ“为表单(参数名 + å•位 + è¾“入框)
6. ç”¨æˆ·å¡«å†™å‚数值
7. è°ƒç”¨ POST /mes/pro/work-order-param-record/create æäº¤
```
---
#### 2.2 æ›´æ–°å‚数记录
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| PUT | /mes/pro/work-order-param-record/update | æ›´æ–°å‚数记录(仅修改填写值,不能换模板) |
**请求参数:**
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| id | Long | æ˜¯ | è®°å½•编号 |
| templateId | Long | æ˜¯ | å‚数模板编号(必须与创建时一致,否则报错) |
| recordNo | String | å¦ | è®°å½•序号/批次号 |
| remark | String | å¦ | å¤‡æ³¨ |
| details | Array | æ˜¯ | å‚数明细列表 |
| details[].templateDetailId | Long | æ˜¯ | æ¨¡æ¿æ˜Žç»†ç¼–号 |
| details[].paramValue | String | æ¡ä»¶å¿…å¡« | å‚数值 |
**请求示例:**
```json
{
  "id": 1024,
  "templateId": 2,
  "recordNo": "BATCH-001",
  "remark": "首件记录(已修正)",
  "details": [
    { "templateDetailId": 3, "paramValue": "210" },
    { "templateDetailId": 4, "paramValue": "52" },
    { "templateDetailId": 5, "paramValue": "28" }
  ]
}
```
**响应:** `{ "code": 0, "data": true }`
**业务规则:** ä¸å…è®¸æ›´æ¢æ¨¡æ¿ï¼ˆtemplateId å¿…须与原记录一致),如需换模板请删除后重新创建。
---
#### 2.3 åˆ é™¤å‚数记录
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| DELETE | /mes/pro/work-order-param-record/delete | åˆ é™¤å‚数记录(级联删除明细) |
**请求参数:**
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| id | Long | æ˜¯ | è®°å½•编号 |
**响应:** `{ "code": 0, "data": true }`
---
#### 2.4 èŽ·å–å‚æ•°è®°å½•è¯¦æƒ…
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| GET | /mes/pro/work-order-param-record/get | èŽ·å–å•æ¡è®°å½•ï¼ˆå«æ˜Žç»†ï¼‰ |
**请求参数:**
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| id | Long | æ˜¯ | è®°å½•编号 |
**响应字段:**
| å­—段 | ç±»åž‹ | è¯´æ˜Ž |
|------|------|------|
| id | Long | è®°å½•编号 |
| workOrderId | Long | å·¥å•编号 |
| templateId | Long | æ¨¡æ¿ç¼–号 |
| templateCode | String | æ¨¡æ¿ç¼–码(快照) |
| templateName | String | æ¨¡æ¿åç§°ï¼ˆå¿«ç…§ï¼‰ |
| recordNo | String | è®°å½•序号 |
| remark | String | å¤‡æ³¨ |
| createTime | LocalDateTime | åˆ›å»ºæ—¶é—´ |
| details | Array | å‚数明细列表 |
| details[].id | Long | æ˜Žç»†ç¼–号 |
| details[].templateDetailId | Long | æ¨¡æ¿æ˜Žç»†ç¼–号 |
| details[].paramCode | String | å‚数编码 |
| details[].paramName | String | å‚数名称 |
| details[].paramType | Integer | å‚数类型 |
| details[].unitMeasureId | Long | è®¡é‡å•位编号 |
| details[].unitMeasureName | String | è®¡é‡å•位名称 |
| details[].paramValue | String | å‚数值 |
| details[].remark | String | å¤‡æ³¨ |
**响应示例:**
```json
{
  "code": 0,
  "data": {
    "id": 1024,
    "workOrderId": 10,
    "templateId": 2,
    "templateCode": "CSMB202607100002",
    "templateName": "注塑参数模板",
    "recordNo": "BATCH-001",
    "remark": "首件记录",
    "createTime": "2026-07-15 14:30:00",
    "details": [
      { "id": 101, "templateDetailId": 3, "paramCode": "TEMP", "paramName": "温度", "paramType": 2, "unitMeasureName": "℃", "paramValue": "200" },
      { "id": 102, "templateDetailId": 4, "paramCode": "PRESS", "paramName": "压力", "paramType": 2, "unitMeasureName": "MPa", "paramValue": "50" },
      { "id": 103, "templateDetailId": 5, "paramCode": "TIME", "paramName": "保压时间", "paramType": 2, "unitMeasureName": "s", "paramValue": "30" }
    ]
  }
}
```
---
#### 2.5 æŒ‰å·¥å•分页查询参数记录
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| GET | /mes/pro/work-order-param-record/page-by-work-order | æŒ‰å·¥å•查询参数记录分页 |
**请求参数:**
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| workOrderId | Long | æ˜¯ | å·¥å•编号(URL å‚数) |
| recordNo | String | å¦ | è®°å½•序号(模糊匹配) |
| templateName | String | å¦ | æ¨¡æ¿åç§°ï¼ˆæ¨¡ç³ŠåŒ¹é…ï¼‰ |
| createTime | String[] | å¦ | åˆ›å»ºæ—¶é—´èŒƒå›´ |
| pageNo | Integer | æ˜¯ | é¡µç  |
| pageSize | Integer | æ˜¯ | æ¯é¡µæ¡æ•° |
**响应字段:** åŒ 2.4(分页列表,每条记录含 details æ˜Žç»†ï¼‰
**响应示例:**
```json
{
  "code": 0,
  "data": {
    "list": [
      {
        "id": 1024,
        "templateName": "注塑参数模板",
        "recordNo": "BATCH-001",
        "createTime": "2026-07-15 14:30:00",
        "details": [
          { "paramName": "温度", "unitMeasureName": "℃", "paramValue": "200" },
          { "paramName": "压力", "unitMeasureName": "MPa", "paramValue": "50" },
          { "paramName": "保压时间", "unitMeasureName": "s", "paramValue": "30" }
        ]
      },
      {
        "id": 1025,
        "templateName": "注塑参数模板",
        "recordNo": "BATCH-002",
        "createTime": "2026-07-15 22:15:00",
        "details": [
          { "paramName": "温度", "unitMeasureName": "℃", "paramValue": "210" },
          { "paramName": "压力", "unitMeasureName": "MPa", "paramValue": "52" },
          { "paramName": "保压时间", "unitMeasureName": "s", "paramValue": "28" }
        ]
      }
    ],
    "total": 2
  }
}
```
---
## å­—段展示规则
### å‚数记录列表(工单详情 Tab)
| å­—段 | å±•示位置 | è¯´æ˜Ž |
|------|----------|------|
| recordNo | åˆ—表 | è®°å½•序号/批次号 |
| templateName | åˆ—表 | å‚数模板名称 |
| å„参数值 | åˆ—表(展开行或标签式) | æ˜¾ç¤º"参数名: å€¼ å•位",如"温度: 200 â„ƒ" |
| createTime | åˆ—表 | è®°å½•æ—¶é—´ |
| æ“ä½œåˆ— | åˆ—表 | ç¼–辑、删除按钮 |
**列表展示建议:** æ¯æ¡è®°å½•可展开查看所有参数明细,或在列表中用标签式展示关键参数值。
### å‚数记录新增/编辑弹窗
| å­—段 | å±•示位置 | è¯´æ˜Ž |
|------|----------|------|
| templateId | å¼¹çª—顶部(下拉选择) | æ–°å¢žæ—¶å¯é€‰ï¼Œç¼–辑时只读 |
| recordNo | å¼¹çª—(输入框) | è®°å½•序号/批次号 |
| å‚数明细列表 | å¼¹çª—主体(动态表单) | æ ¹æ®æ¨¡æ¿åŠ è½½ï¼Œè§ä¸‹æ–¹ |
| remark | å¼¹çª—底部(文本域) | å¤‡æ³¨ |
**参数明细动态表单:** é€‰æ‹©æ¨¡æ¿åŽï¼Œè‡ªåŠ¨åŠ è½½å‚æ•°åˆ—è¡¨æ¸²æŸ“ä¸ºè¡¨å•è¡Œï¼š
| æ¨¡æ¿å­—段 | è¡¨å•展示 | è¯´æ˜Ž |
|---------|---------|------|
| paramName | æ ‡ç­¾æ–‡æœ¬ | å¦‚"温度" |
| unitMeasureName | æ ‡ç­¾åŽç¼€ | å¦‚"℃",显示在输入框右侧 |
| required | å¿…填标记 | required=true æ—¶åŠ çº¢è‰²æ˜Ÿå· |
| paramType | è¾“入控件类型 | 1=文本框,2=数字框,3=开关,4=日期选择器 |
| paramValue | è¾“入框 | ç”¨æˆ·å¡«å†™çš„参数值 |
---
## ä¸šåŠ¡è§„åˆ™è¯´æ˜Ž
| åœºæ™¯ | è§„则 |
|------|------|
| é€‰æ‹©æ¨¡æ¿åŽåŠ è½½å‚æ•° | è°ƒç”¨æ¨¡æ¿è¯¦æƒ…接口,将 details æ¸²æŸ“为表单行 |
| å¿…填参数校验 | required=true çš„参数,paramValue ä¸èƒ½ä¸ºç©ºï¼Œå¦åˆ™æäº¤æ—¶æŠ¥é”™ |
| ä¸å…è®¸æ›´æ¢æ¨¡æ¿ | ç¼–辑时 templateId å¿…须与创建时一致,如需换模板请删除后重新创建 |
| å¤šæ¬¡è®°å½• | åŒä¸€å·¥å•可创建多条参数记录(同一模板也可多次记录) |
| å¿«ç…§æœºåˆ¶ | è®°å½•时快照模板的参数定义,后续模板变更不影响已记录的数据 |
| åˆ é™¤çº§è” | åˆ é™¤è®°å½•时,该记录下所有参数明细一起删除 |
## æ³¨æ„äº‹é¡¹
- å‚数类型(paramType)字典:1=文本,2=数值,3=布尔,4=日期,前端可根据类型渲染不同的输入控件
- æ–°å¢žæ—¶å…ˆé€‰æ¨¡æ¿å†å¡«å€¼ï¼Œå»ºè®®æ¨¡æ¿é€‰æ‹©åŽç«‹å³è°ƒç”¨æ¨¡æ¿è¯¦æƒ…接口加载参数列表
- ç¼–辑时模板字段置灰不可修改,只能修改 paramValue å’Œ recordNo、remark
- å·¥å•参数记录的权限复用工单权限:创建/修改/删除用 `mes:pro-work-order:update`,查询用 `mes:pro-work-order:query`
- åŒä¸€æ¨¡æ¿åœ¨åŒå·¥å•下可创建多次记录(如每班次/每批次记录一次),通过 recordNo åŒºåˆ†
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/item/dto/MdmItemSyncReqDTO.java
@@ -24,7 +24,7 @@
    private String specification;
    @Schema(description = "物料类型:1原料, 2半成品, 3成品, 4辅料", example = "1")
    private Integer itemType;
    private Long itemType;
    @Schema(description = "计量单位ID", example = "1")
    private Long unitMeasureId;
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/item/vo/MdmItemSaveReqVO.java
@@ -113,4 +113,6 @@
    @Schema(description = "质量状态必填", example = "false")
    private Boolean qualityStatusFlag;
    private Long mdmItemId;
}
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/item/MdmItemServiceImpl.java
@@ -2,8 +2,10 @@
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.mdm.controller.admin.item.vo.MdmItemPageReqVO;
import cn.iocoder.yudao.module.mdm.controller.admin.item.vo.MdmItemSaveReqVO;
import cn.iocoder.yudao.module.mdm.dal.dataobject.category.MdmItemCategoryDO;
@@ -15,8 +17,12 @@
import cn.iocoder.yudao.module.mdm.service.category.MdmItemCategoryService;
import cn.iocoder.yudao.module.mdm.service.unit.MdmUnitMeasureService;
import cn.iocoder.yudao.module.mes.api.item.MesMdItemApi;
import cn.iocoder.yudao.module.mes.api.item.MesMdItemTypeApi;
import cn.iocoder.yudao.module.mes.api.item.dto.MesMdItemSyncReqDTO;
import cn.iocoder.yudao.module.mes.api.item.dto.MesMdItemTypeRespDTO;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -54,6 +60,10 @@
    @Resource
    private MdmItemBatchConfigMapper itemBatchConfigMapper;
    @Resource
    private MesMdItemTypeApi mesMdItemTypeApi;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createItem(MdmItemSaveReqVO createReqVO) {
@@ -68,6 +78,7 @@
        saveBatchConfig(item.getId(), createReqVO);
        // åŒæ­¥åˆ° MES(勾选 syncMes æ—¶ï¼‰
        createReqVO.setId(item.getId());
        if (Boolean.TRUE.equals(createReqVO.getSyncMes())) {
            syncItemToMes(createReqVO);
        }
@@ -120,25 +131,13 @@
        // è§£æžåˆ†ç±» id -> code(使用 MDM æœ¬åœ°åˆ†ç±»æœåŠ¡ï¼‰
        String itemTypeCode = null;
        if (reqVO.getCategoryId() != null) {
            MdmItemCategoryDO category = itemCategoryService.getItemCategory(reqVO.getCategoryId());
            if (category != null) {
                itemTypeCode = category.getCode();
            }
        } else if (reqVO.getItemType() != null) {
            // æ ¹æ® itemType æŸ¥æ‰¾å¯¹åº”分类编码
            // itemType: 1原料, 2半成品, 3成品, 4辅料
            String itemTypeName = convertItemTypeToName(reqVO.getItemType());
            List<MdmItemCategoryDO> categories = itemCategoryService.getItemCategoryList(
                    itemCategoryService.getItemCategoryChildrenList(null).stream().map(MdmItemCategoryDO::getId).collect(Collectors.toSet()));
            itemTypeCode = categories.stream()
                    .filter(c -> c.getName() != null && c.getName().equals(itemTypeName))
                    .findFirst()
                    .map(MdmItemCategoryDO::getCode)
                    .orElse(null);
            CommonResult<MesMdItemTypeRespDTO> itemType = mesMdItemTypeApi.getItemType(reqVO.getCategoryId());
            itemTypeCode = itemType.getData().getCode();
        }
        // ç»„装同步 DTO
        MesMdItemSyncReqDTO syncReqDTO = new MesMdItemSyncReqDTO();
        syncReqDTO.setCode(reqVO.getCode());
        syncReqDTO.setMdmItemId(reqVO.getId());
        syncReqDTO.setName(reqVO.getName());
        syncReqDTO.setSpecification(reqVO.getSpecification());
        syncReqDTO.setUnitCode(unitCode);
yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/item/dto/MesMdItemSyncReqDTO.java
@@ -65,4 +65,6 @@
    @Schema(description = "质量状态必填", example = "false")
    private Boolean qualityStatusFlag;
    private Long mdmItemId;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/api/item/MesMdItemApiImpl.java
@@ -47,7 +47,7 @@
        MesMdItemSaveReqVO saveReqVO = buildSaveReqVO(syncReqDTO);
        Long itemId = itemService.createItem(saveReqVO);
        // åŒæ­¥æ‰¹æ¬¡é…ç½®
        saveBatchConfig(itemId, syncReqDTO);
//        saveBatchConfig(itemId, syncReqDTO);
        return itemId;
    }
@@ -66,7 +66,7 @@
            itemId = existItem.getId();
        }
        // åŒæ­¥æ‰¹æ¬¡é…ç½®
        saveBatchConfig(itemId, syncReqDTO);
//        saveBatchConfig(itemId, syncReqDTO);
    }
    /**
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/item/vo/MesMdItemSaveReqVO.java
@@ -54,4 +54,6 @@
    @Schema(description = "是否同步到 MDM", example = "true")
    private Boolean syncMdm;
    private Long mdmItemId;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/feedback/vo/MesProFeedbackPageReqVO.java
@@ -29,6 +29,12 @@
    @Schema(description = "状态", example = "0")
    private Integer status;
    @Schema(description = "生产任务编号", example = "1")
    private Long taskId;
    @Schema(description = "生产任务编码(模糊匹配)", example = "PT202503")
    private String taskCode;
    @Schema(description = "报工时间")
    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
    private LocalDateTime[] feedbackTime;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/feedback/vo/MesProFeedbackSaveReqVO.java
@@ -22,28 +22,23 @@
    @NotNull(message = "报工类型不能为空")
    private Integer type;
    @Schema(description = "工作站编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "工作站不能为空")
    @Schema(description = "工作站编号", example = "1")
    private Long workstationId;
    @Schema(description = "工艺路线编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "工艺路线不能为空")
    @Schema(description = "工艺路线编号", example = "1")
    private Long routeId;
    @Schema(description = "工序编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "工序不能为空")
    @Schema(description = "工序编号", example = "1")
    private Long processId;
    @Schema(description = "生产工单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "生产工单不能为空")
    @Schema(description = "生产工单编号", example = "1")
    private Long workOrderId;
    @Schema(description = "生产任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "生产任务不能为空")
    private Long taskId;
    @Schema(description = "产品物料编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
    @NotNull(message = "产品物料不能为空")
    @Schema(description = "产品物料编号", example = "100")
    private Long itemId;
    @Schema(description = "过期日期")
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderParamRecordController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,124 @@
package cn.iocoder.yudao.module.mes.controller.admin.pro.workorder;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.paramrecord.MesProWorkOrderParamRecordPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.paramrecord.MesProWorkOrderParamRecordRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.paramrecord.MesProWorkOrderParamRecordSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderParamRecordDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderParamRecordDetailDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkOrderParamRecordDetailMapper;
import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderParamRecordService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
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.convertSet;
@Tag(name = "管理后台 - MES ç”Ÿäº§å·¥å•参数记录")
@RestController
@RequestMapping("/mes/pro/work-order-param-record")
@Validated
public class MesProWorkOrderParamRecordController {
    @Resource
    private MesProWorkOrderParamRecordService paramRecordService;
    @Resource
    private MesProWorkOrderParamRecordDetailMapper paramRecordDetailMapper;
    @Resource
    private MesMdUnitMeasureService unitMeasureService;
    @PostMapping("/create")
    @Operation(summary = "创建工单参数记录")
    @PreAuthorize("@ss.hasPermission('mes:pro-work-order:update')")
    public CommonResult<Long> createParamRecord(@RequestParam("workOrderId") Long taskId,
                                                @Valid @RequestBody MesProWorkOrderParamRecordSaveReqVO createReqVO) {
        return success(paramRecordService.createParamRecord(taskId, createReqVO));
    }
    @PutMapping("/update")
    @Operation(summary = "更新工单参数记录")
    @PreAuthorize("@ss.hasPermission('mes:pro-work-order:update')")
    public CommonResult<Boolean> updateParamRecord(@Valid @RequestBody MesProWorkOrderParamRecordSaveReqVO updateReqVO) {
        paramRecordService.updateParamRecord(updateReqVO);
        return success(true);
    }
    @DeleteMapping("/delete")
    @Operation(summary = "删除工单参数记录")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pro-work-order:update')")
    public CommonResult<Boolean> deleteParamRecord(@RequestParam("id") Long id) {
        paramRecordService.deleteParamRecord(id);
        return success(true);
    }
    @GetMapping("/get")
    @Operation(summary = "获得工单参数记录")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('mes:pro-work-order:query')")
    public CommonResult<MesProWorkOrderParamRecordRespVO> getParamRecord(@RequestParam("id") Long id) {
        MesProWorkOrderParamRecordDO record = paramRecordService.getParamRecord(id);
        if (record == null) {
            return success(null);
        }
        return success(buildRecordRespVOList(ListUtil.of(record)).get(0));
    }
    @GetMapping("/page-by-work-order")
    @Operation(summary = "按工单获得参数记录分页")
    @PreAuthorize("@ss.hasPermission('mes:pro-work-order:query')")
    public CommonResult<PageResult<MesProWorkOrderParamRecordRespVO>> getParamRecordPageByWorkOrderId(
            @RequestParam("taskId") Long taskId,
            @Valid MesProWorkOrderParamRecordPageReqVO pageReqVO) {
        PageResult<MesProWorkOrderParamRecordDO> pageResult =
                paramRecordService.getParamRecordPageByWorkOrderId(taskId, pageReqVO);
        return success(new PageResult<>(buildRecordRespVOList(pageResult.getList()), pageResult.getTotal()));
    }
    // ==================== æ‹¼æŽ¥ VO ====================
    private List<MesProWorkOrderParamRecordRespVO> buildRecordRespVOList(List<MesProWorkOrderParamRecordDO> list) {
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        // 1. æ‰¹é‡èŽ·å–æ‰€æœ‰è®°å½•çš„æ˜Žç»†
        List<MesProWorkOrderParamRecordDetailDO> allDetails = paramRecordDetailMapper.selectListByRecordIds(
                convertSet(list, MesProWorkOrderParamRecordDO::getId));
        Map<Long, List<MesProWorkOrderParamRecordDetailDO>> detailMap = allDetails.stream()
                .collect(Collectors.groupingBy(MesProWorkOrderParamRecordDetailDO::getRecordId));
        // 2. æ‰¹é‡èŽ·å–è®¡é‡å•ä½
        Set<Long> unitMeasureIds = convertSet(allDetails, MesProWorkOrderParamRecordDetailDO::getUnitMeasureId);
        Map<Long, MesMdUnitMeasureDO> unitMeasureMap = unitMeasureService.getUnitMeasureMap(unitMeasureIds);
        // 3. æ‹¼æŽ¥ VO
        return BeanUtils.toBean(list, MesProWorkOrderParamRecordRespVO.class, vo -> {
            List<MesProWorkOrderParamRecordDetailDO> details = detailMap.get(vo.getId());
            if (CollUtil.isNotEmpty(details)) {
                List<MesProWorkOrderParamRecordRespVO.DetailResp> detailResps = BeanUtils.toBean(
                        details, MesProWorkOrderParamRecordRespVO.DetailResp.class,
                        detailResp -> MapUtils.findAndThen(unitMeasureMap, detailResp.getUnitMeasureId(),
                                unit -> detailResp.setUnitMeasureName(unit.getName()))
                );
                vo.setDetails(detailResps);
            }
        });
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/paramrecord/MesProWorkOrderParamRecordPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.paramrecord;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - MES ç”Ÿäº§å·¥å•参数记录分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MesProWorkOrderParamRecordPageReqVO extends PageParam {
    @Schema(description = "记录序号/批次号", example = "BATCH-001")
    private String recordNo;
    @Schema(description = "模板名称", example = "注塑参数模板")
    private String templateName;
    @Schema(description = "创建时间")
    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
    private LocalDateTime[] createTime;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/paramrecord/MesProWorkOrderParamRecordRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,76 @@
package cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.paramrecord;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - MES ç”Ÿäº§å·¥å•参数记录 Response VO")
@Data
public class MesProWorkOrderParamRecordRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    private Long id;
    @Schema(description = "生产订单编号", example = "1")
    private Long workOrderId;
    @Schema(description = "生产工单编号")
    private Long taskId;
    @Schema(description = "参数模板编号", example = "1")
    private Long templateId;
    @Schema(description = "模板编码", example = "CSMB001")
    private String templateCode;
    @Schema(description = "模板名称", example = "注塑参数模板")
    private String templateName;
    @Schema(description = "记录序号/批次号", example = "BATCH-001")
    private String recordNo;
    @Schema(description = "备注", example = "备注")
    private String remark;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    private LocalDateTime createTime;
    @Schema(description = "参数明细列表")
    private List<DetailResp> details;
    @Schema(description = "参数明细响应")
    @Data
    public static class DetailResp {
        @Schema(description = "编号", example = "1024")
        private Long id;
        @Schema(description = "模板明细编号", example = "1")
        private Long templateDetailId;
        @Schema(description = "参数编码", example = "TEMP")
        private String paramCode;
        @Schema(description = "参数名称", example = "温度")
        private String paramName;
        @Schema(description = "参数类型", example = "1")
        private Integer paramType;
        @Schema(description = "计量单位编号", example = "1")
        private Long unitMeasureId;
        @Schema(description = "计量单位名称", example = "℃")
        private String unitMeasureName;
        @Schema(description = "参数值", example = "200")
        private String paramValue;
        @Schema(description = "备注", example = "备注")
        private String remark;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/vo/paramrecord/MesProWorkOrderParamRecordSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,46 @@
package cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.paramrecord;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - MES ç”Ÿäº§å·¥å•参数记录新增/修改 Request VO")
@Data
public class MesProWorkOrderParamRecordSaveReqVO {
    @Schema(description = "编号(修改时必传)", example = "1024")
    private Long id;
    @Schema(description = "参数模板编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "参数模板不能为空")
    private Long templateId;
    @Schema(description = "记录序号/批次号", example = "BATCH-001")
    private String recordNo;
    @Schema(description = "参数明细列表", requiredMode = Schema.RequiredMode.REQUIRED)
    @NotEmpty(message = "参数明细不能为空")
    @Valid
    private List<DetailItem> details;
    @Schema(description = "备注", example = "备注")
    private String remark;
    @Schema(description = "参数明细项")
    @Data
    public static class DetailItem {
        @Schema(description = "模板明细编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
        @NotNull(message = "模板明细编号不能为空")
        private Long templateDetailId;
        @Schema(description = "参数值", example = "200")
        private String paramValue;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productissue/MesWmProductIssueController.java
@@ -13,10 +13,12 @@
import cn.iocoder.yudao.module.mes.controller.admin.wm.productissue.vo.MesWmProductIssueSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productissue.MesWmProductIssueDO;
import cn.iocoder.yudao.module.mes.service.md.client.MesMdClientService;
import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService;
import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
import cn.iocoder.yudao.module.mes.service.wm.productissue.MesWmProductIssueService;
import io.swagger.v3.oas.annotations.Operation;
@@ -50,6 +52,8 @@
    private MesMdWorkstationService workstationService;
    @Resource
    private MesProWorkOrderService workOrderService;
    @Resource
    private MesProTaskService taskService;
    @Resource
    private MesMdClientService clientService;
@@ -165,12 +169,16 @@
                convertSet(list, MesWmProductIssueDO::getWorkstationId));
        Map<Long, MesProWorkOrderDO> workOrderMap = workOrderService.getWorkOrderMap(
                convertSet(list, MesWmProductIssueDO::getWorkOrderId));
        Map<Long, MesProTaskDO> taskMap = taskService.getTaskMap(
                convertSet(list, MesWmProductIssueDO::getTaskId));
        Map<Long, MesMdClientDO> clientMap = clientService.getClientMap(
                convertSet(workOrderMap.values(), MesProWorkOrderDO::getClientId));
        // 2. æž„建结果
        return BeanUtils.toBean(list, MesWmProductIssueRespVO.class, vo -> {
            MapUtils.findAndThen(workstationMap, vo.getWorkstationId(),
                    workstation -> vo.setWorkstationName(workstation.getName()));
            MapUtils.findAndThen(taskMap, vo.getTaskId(), task ->
                    vo.setTaskCode(task.getCode()).setTaskName(task.getName()));
            MapUtils.findAndThen(workOrderMap, vo.getWorkOrderId(), workOrder -> {
                vo.setWorkOrderCode(workOrder.getCode());
                MapUtils.findAndThen(clientMap, workOrder.getClientId(), client ->
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productissue/MesWmProductIssueLineController.java
@@ -8,13 +8,13 @@
import cn.iocoder.yudao.module.mes.controller.admin.wm.productissue.vo.line.MesWmProductIssueLinePageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.productissue.vo.line.MesWmProductIssueLineRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.productissue.vo.line.MesWmProductIssueLineSaveReqVO;
import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productissue.MesWmProductIssueLineDO;
import cn.iocoder.yudao.module.mdm.api.unit.MdmUnitMeasureApi;
import cn.iocoder.yudao.module.mdm.api.unit.dto.MdmUnitMeasureRespDTO;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService;
import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService;
import cn.iocoder.yudao.module.mes.service.wm.productissue.MesWmProductIssueLineService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -28,7 +28,6 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
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.convertSet;
@@ -42,9 +41,11 @@
    @Resource
    private MesWmProductIssueLineService issueLineService;
    @Resource
    private MdmItemApi mdmItemApi;
    private MesMdItemService mdmItemService;
    @Resource
    private MdmUnitMeasureApi mdmUnitMeasureApi;
    private MesMdUnitMeasureService unitMeasureService;
    @Resource
    private MesProTaskService taskService;
    @PostMapping("/create")
    @Operation(summary = "创建领料出库单行")
@@ -108,18 +109,21 @@
            return Collections.emptyList();
        }
        // 1. èŽ·å¾—å…³è”æ•°æ®
        Map<Long, MdmItemRespDTO> itemMap = mdmItemApi.getItemMap(
        Map<Long, MesMdItemDO> itemMap = mdmItemService.getItemMap(
                convertSet(list, MesWmProductIssueLineDO::getItemId));
        Map<Long, MdmUnitMeasureRespDTO> unitMeasureMap = mdmUnitMeasureApi.getUnitMeasureList(
                convertSet(itemMap.values(), MdmItemRespDTO::getUnitMeasureId)).getCheckedData()
                .stream().collect(Collectors.toMap(MdmUnitMeasureRespDTO::getId, e -> e));
        Map<Long, MesMdUnitMeasureDO> unitMeasureMap = unitMeasureService.getUnitMeasureMap(
                convertSet(itemMap.values(), MesMdItemDO::getUnitMeasureId));
        Map<Long, MesProTaskDO> taskMap = taskService.getTaskMap(
                convertSet(list, MesWmProductIssueLineDO::getTaskId));
        // 2. æž„建结果
        return BeanUtils.toBean(list, MesWmProductIssueLineRespVO.class, vo -> {
            MapUtils.findAndThen(itemMap, vo.getItemId(), item -> {
                vo.setItemCode(item.getCode()).setItemName(item.getName()).setSpecification(item.getSpecification());
                vo.setItemCode(item.getCode()).setItemName(item.getName()).setSpecification(item.getSpecification()).setMdmItemId(item.getMdmItemId());
                MapUtils.findAndThen(unitMeasureMap, item.getUnitMeasureId(),
                        unitMeasure -> vo.setUnitMeasureName(unitMeasure.getName()));
                        unit -> vo.setUnitMeasureName(unit.getName()));
            });
            MapUtils.findAndThen(taskMap, vo.getTaskId(), task ->
                    vo.setTaskCode(task.getCode()).setTaskName(task.getName()));
        });
    }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productissue/vo/MesWmProductIssueRespVO.java
@@ -39,6 +39,12 @@
    @Schema(description = "生产任务 ID", example = "1")
    private Long taskId;
    @Schema(description = "生产任务编码", example = "TASK001")
    private String taskCode;
    @Schema(description = "生产任务名称", example = "注塑任务")
    private String taskName;
    @Schema(description = "领料日期")
    private LocalDateTime issueDate;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productissue/vo/MesWmProductIssueSaveReqVO.java
@@ -22,8 +22,11 @@
    @NotBlank(message = "领料单名称不能为空")
    private String name;
    @Schema(description = "生产工单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "生产工单 ID ä¸èƒ½ä¸ºç©º")
    @Schema(description = "生产任务 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "生产任务 ID ä¸èƒ½ä¸ºç©º")
    private Long taskId;
    @Schema(description = "生产工单 ID", example = "1")
    private Long workOrderId;
    @Schema(description = "工作站 ID", example = "1")
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productissue/vo/line/MesWmProductIssueLineRespVO.java
@@ -16,6 +16,15 @@
    @Schema(description = "领料单ID", example = "1")
    private Long issueId;
    @Schema(description = "生产任务ID", example = "1")
    private Long taskId;
    @Schema(description = "生产任务编码", example = "TASK001")
    private String taskCode;
    @Schema(description = "生产任务名称", example = "注塑任务")
    private String taskName;
    @Schema(description = "物料ID", example = "1")
    private Long itemId;
@@ -43,4 +52,6 @@
    @Schema(description = "创建时间")
    private LocalDateTime createTime;
    private Long mdmItemId;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productissue/vo/line/MesWmProductIssueLineSaveReqVO.java
@@ -18,6 +18,9 @@
    @NotNull(message = "领料单ID不能为空")
    private Long issueId;
    @Schema(description = "生产任务ID", example = "1")
    private Long taskId;
    @Schema(description = "物料ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "物料ID不能为空")
    private Long itemId;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnissue/MesWmReturnIssueController.java
@@ -12,9 +12,11 @@
import cn.iocoder.yudao.module.mes.controller.admin.wm.returnissue.vo.MesWmReturnIssueRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.returnissue.vo.MesWmReturnIssueSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnissue.MesWmReturnIssueDO;
import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService;
import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
import cn.iocoder.yudao.module.mes.service.wm.returnissue.MesWmReturnIssueService;
import io.swagger.v3.oas.annotations.Operation;
@@ -48,6 +50,8 @@
    private MesMdWorkstationService workstationService;
    @Resource
    private MesProWorkOrderService workOrderService;
    @Resource
    private MesProTaskService taskService;
    @PostMapping("/create")
    @Operation(summary = "创建生产退料单")
@@ -153,10 +157,14 @@
                convertSet(list, MesWmReturnIssueDO::getWorkstationId));
        Map<Long, MesProWorkOrderDO> workOrderMap = workOrderService.getWorkOrderMap(
                convertSet(list, MesWmReturnIssueDO::getWorkOrderId));
        Map<Long, MesProTaskDO> taskMap = taskService.getTaskMap(
                convertSet(list, MesWmReturnIssueDO::getTaskId));
        // 2. æž„建结果
        return BeanUtils.toBean(list, MesWmReturnIssueRespVO.class, vo -> {
            MapUtils.findAndThen(workstationMap, vo.getWorkstationId(),
                    workstation -> vo.setWorkstationName(workstation.getName()));
            MapUtils.findAndThen(taskMap, vo.getTaskId(), task ->
                    vo.setTaskCode(task.getCode()).setTaskName(task.getName()));
            MapUtils.findAndThen(workOrderMap, vo.getWorkOrderId(),
                    workOrder -> vo.setWorkOrderCode(workOrder.getCode()));
        });
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnissue/MesWmReturnIssueLineController.java
@@ -8,13 +8,13 @@
import cn.iocoder.yudao.module.mes.controller.admin.wm.returnissue.vo.line.MesWmReturnIssueLinePageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.returnissue.vo.line.MesWmReturnIssueLineRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.returnissue.vo.line.MesWmReturnIssueLineSaveReqVO;
import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnissue.MesWmReturnIssueLineDO;
import cn.iocoder.yudao.module.mdm.api.unit.MdmUnitMeasureApi;
import cn.iocoder.yudao.module.mdm.api.unit.dto.MdmUnitMeasureRespDTO;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService;
import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService;
import cn.iocoder.yudao.module.mes.service.wm.returnissue.MesWmReturnIssueLineService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -28,7 +28,6 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
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.convertSet;
@@ -42,9 +41,11 @@
    @Resource
    private MesWmReturnIssueLineService issueLineService;
    @Resource
    private MdmItemApi mdmItemApi;
    private MesMdItemService mdmItemService;
    @Resource
    private MdmUnitMeasureApi mdmUnitMeasureApi;
    private MesMdUnitMeasureService unitMeasureService;
    @Resource
    private MesProTaskService taskService;
    @PostMapping("/create")
    @Operation(summary = "创建生产退料单行")
@@ -108,18 +109,21 @@
            return Collections.emptyList();
        }
        // 1. èŽ·å¾—å…³è”æ•°æ®
        Map<Long, MdmItemRespDTO> itemMap = mdmItemApi.getItemMap(
        Map<Long, MesMdItemDO> itemMap = mdmItemService.getItemMap(
                convertSet(list, MesWmReturnIssueLineDO::getItemId));
        Map<Long, MdmUnitMeasureRespDTO> unitMeasureMap = mdmUnitMeasureApi.getUnitMeasureList(
                convertSet(itemMap.values(), MdmItemRespDTO::getUnitMeasureId)).getCheckedData()
                .stream().collect(Collectors.toMap(MdmUnitMeasureRespDTO::getId, e -> e));
        Map<Long, MesMdUnitMeasureDO> unitMeasureMap = unitMeasureService.getUnitMeasureMap(
                convertSet(itemMap.values(), MesMdItemDO::getUnitMeasureId));
        Map<Long, MesProTaskDO> taskMap = taskService.getTaskMap(
                convertSet(list, MesWmReturnIssueLineDO::getTaskId));
        // 2. æž„建结果
        return BeanUtils.toBean(list, MesWmReturnIssueLineRespVO.class, vo -> {
            MapUtils.findAndThen(itemMap, vo.getItemId(), item -> {
                vo.setItemCode(item.getCode()).setItemName(item.getName()).setSpecification(item.getSpecification());
                MapUtils.findAndThen(unitMeasureMap, item.getUnitMeasureId(),
                        unitMeasure -> vo.setUnitMeasureName(unitMeasure.getName()));
                        unit -> vo.setUnitMeasureName(unit.getName()));
            });
            MapUtils.findAndThen(taskMap, vo.getTaskId(), task ->
                    vo.setTaskCode(task.getCode()).setTaskName(task.getName()));
        });
    }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnissue/vo/MesWmReturnIssuePageReqVO.java
@@ -22,6 +22,9 @@
    @Schema(description = "生产工单 ID", example = "1")
    private Long workOrderId;
    @Schema(description = "生产任务 ID", example = "1")
    private Long taskId;
    @Schema(description = "退料类型", example = "1")
    private Integer type;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnissue/vo/MesWmReturnIssueRespVO.java
@@ -24,6 +24,15 @@
    @Schema(description = "生产工单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "WO20250226001")
    private String workOrderCode;
    @Schema(description = "生产任务 ID", example = "1")
    private Long taskId;
    @Schema(description = "生产任务编码", example = "TASK001")
    private String taskCode;
    @Schema(description = "生产任务名称", example = "注塑任务")
    private String taskName;
    @Schema(description = "工作站 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    private Long workstationId;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnissue/vo/MesWmReturnIssueSaveReqVO.java
@@ -22,8 +22,11 @@
    @NotBlank(message = "退料单名称不能为空")
    private String name;
    @Schema(description = "生产工单 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "生产工单 ID ä¸èƒ½ä¸ºç©º")
    @Schema(description = "生产任务 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "生产任务 ID ä¸èƒ½ä¸ºç©º")
    private Long taskId;
    @Schema(description = "生产工单 ID", example = "1")
    private Long workOrderId;
    @Schema(description = "工作站 ID", example = "1")
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnissue/vo/line/MesWmReturnIssueLineRespVO.java
@@ -16,6 +16,15 @@
    @Schema(description = "退料单 ID", example = "1")
    private Long issueId;
    @Schema(description = "生产任务 ID", example = "1")
    private Long taskId;
    @Schema(description = "生产任务编码", example = "TASK001")
    private String taskCode;
    @Schema(description = "生产任务名称", example = "注塑任务")
    private String taskName;
    @Schema(description = "物料 ID", example = "1")
    private Long itemId;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnissue/vo/line/MesWmReturnIssueLineSaveReqVO.java
@@ -18,6 +18,9 @@
    @NotNull(message = "退料单 ID ä¸èƒ½ä¸ºç©º")
    private Long issueId;
    @Schema(description = "生产任务 ID", example = "1")
    private Long taskId;
    @Schema(description = "物料 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "物料 ID ä¸èƒ½ä¸ºç©º")
    private Long itemId;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/md/item/MesMdItemDO.java
@@ -30,6 +30,12 @@
    @TableId
    private Long id;
    /**
     * MDM ç‰©æ–™ ID
     *
     * å…³è” mdm_item.id
     */
    private Long mdmItemId;
    /**
     * ç‰©æ–™ç¼–码
     */
    private String code;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkOrderParamRecordDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,62 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.processparamtemplate.MesPdProcessParamTemplateDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
/**
 * MES ç”Ÿäº§å·¥å•参数记录 DO
 *
 * @author èŠ‹é“æºç 
 */
@TableName("mes_pro_work_order_param_record")
@KeySequence("mes_pro_work_order_param_record_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MesProWorkOrderParamRecordDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * ç”Ÿäº§å·¥å•编号
     *
     * å…³è” {@link MesProWorkOrderDO#getId()}
     */
    private Long workOrderId;
    private Long taskId;
    /**
     * å‚数模板编号
     *
     * å…³è” {@link MesPdProcessParamTemplateDO#getId()}
     */
    private Long templateId;
    /**
     * æ¨¡æ¿ç¼–码(快照)
     */
    private String templateCode;
    /**
     * æ¨¡æ¿åç§°ï¼ˆå¿«ç…§ï¼‰
     */
    private String templateName;
    /**
     * è®°å½•序号/批次号
     */
    private String recordNo;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkOrderParamRecordDetailDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,70 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.processparamtemplate.MesPdProcessParamTemplateDetailDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
/**
 * MES ç”Ÿäº§å·¥å•参数记录明细 DO
 *
 * å¿«ç…§è¡¨ï¼šè®°å½•时快照模板参数定义,后续模板变更不影响已记录的数据。
 * template_detail_id ä»…作溯源,不从模板明细主表读取显示值。
 *
 * @author èŠ‹é“æºç 
 */
@TableName("mes_pro_work_order_param_record_detail")
@KeySequence("mes_pro_work_order_param_record_detail_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MesProWorkOrderParamRecordDetailDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * å‚数记录编号
     *
     * å…³è” {@link MesProWorkOrderParamRecordDO#getId()}
     */
    private Long recordId;
    /**
     * æ¨¡æ¿æ˜Žç»†ç¼–号(仅作溯源)
     *
     * å…³è” {@link MesPdProcessParamTemplateDetailDO#getId()}
     */
    private Long templateDetailId;
    /**
     * å‚数编码(快照)
     */
    private String paramCode;
    /**
     * å‚数名称(快照)
     */
    private String paramName;
    /**
     * å‚数类型(快照)
     */
    private Integer paramType;
    /**
     * è®¡é‡å•位编号(快照)
     */
    private Long unitMeasureId;
    /**
     * å‚数值
     */
    private String paramValue;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productissue/MesWmProductIssueDO.java
@@ -51,14 +51,12 @@
     * å…³è” {@link MesProWorkOrderDO#getId()}
     */
    private Long workOrderId;
    // TODO @芋艿:【疑问,不要动】这个字段的来源?
    /**
     * ç”Ÿäº§ä»»åŠ¡ ID
     *
     * å…³è” {@link MesProTaskDO#getId()}
     */
    private Long taskId;
    // TODO @芋艿:【疑问,不要动】这个字段的更新时间;
    /**
     * é¢†æ–™æ—¥æœŸ
     */
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productissue/MesWmProductIssueLineDO.java
@@ -2,6 +2,7 @@
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@@ -34,6 +35,12 @@
     */
    private Long issueId;
    /**
     * ç”Ÿäº§ä»»åŠ¡ ID
     *
     * å…³è” {@link MesProTaskDO#getId()}
     */
    private Long taskId;
    /**
     * ç‰©æ–™ ID
     *
     * å…³è” {@link MesMdItemDO#getId()}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/returnissue/MesWmReturnIssueDO.java
@@ -2,6 +2,7 @@
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmReturnIssueStatusEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
@@ -45,6 +46,12 @@
     */
    private Long workOrderId;
    /**
     * ç”Ÿäº§ä»»åŠ¡ ID
     *
     * å…³è” {@link MesProTaskDO#getId()}
     */
    private Long taskId;
    /**
     * å·¥ä½œç«™ ID
     *
     * å…³è” {@link MesMdWorkstationDO#getId()}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/returnissue/MesWmReturnIssueLineDO.java
@@ -2,6 +2,7 @@
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.rqc.MesQcRqcDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO;
@@ -38,6 +39,12 @@
     */
    private Long issueId;
    /**
     * ç”Ÿäº§ä»»åŠ¡ ID
     *
     * å…³è” {@link MesProTaskDO#getId()}
     */
    private Long taskId;
    /**
     * åº“存记录 ID
     *
     * å…³è” {@link MesWmMaterialStockDO#getId()}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/md/item/MesMdItemTypeMapper.java
@@ -32,7 +32,7 @@
    }
    default MesMdItemTypeDO selectByCode(String code) {
        return selectOne(MesMdItemTypeDO::getName, code);
        return selectOne(MesMdItemTypeDO::getCode, code);
    }
    default Long selectCountByParentId(Long parentId) {
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/feedback/MesProFeedbackMapper.java
@@ -1,10 +1,13 @@
package cn.iocoder.yudao.module.mes.dal.mysql.pro.feedback;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedbackPageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
@@ -19,10 +22,31 @@
public interface MesProFeedbackMapper extends BaseMapperX<MesProFeedbackDO> {
    default PageResult<MesProFeedbackDO> selectPage(MesProFeedbackPageReqVO reqVO) {
        // å½“按任务编码模糊查询时,需要 JOIN ä»»åŠ¡è¡¨
        if (StrUtil.isNotBlank(reqVO.getTaskCode())) {
            MPJLambdaWrapperX<MesProFeedbackDO> query = new MPJLambdaWrapperX<>();
            query.selectAll(MesProFeedbackDO.class);
            query.leftJoin(MesProTaskDO.class, on -> on
                    .eq(MesProTaskDO::getId, MesProFeedbackDO::getTaskId));
            query.like(MesProTaskDO::getCode, reqVO.getTaskCode());
            query.likeIfPresent(MesProFeedbackDO::getCode, reqVO.getCode())
                    .eqIfPresent(MesProFeedbackDO::getType, reqVO.getType())
                    .eqIfPresent(MesProFeedbackDO::getWorkOrderId, reqVO.getWorkOrderId())
                    .eqIfPresent(MesProFeedbackDO::getTaskId, reqVO.getTaskId())
                    .eqIfPresent(MesProFeedbackDO::getStatus, reqVO.getStatus())
                    .betweenIfPresent(MesProFeedbackDO::getFeedbackTime, reqVO.getFeedbackTime())
                    .eqIfPresent(MesProFeedbackDO::getItemId, reqVO.getItemId())
                    .eqIfPresent(MesProFeedbackDO::getFeedbackUserId, reqVO.getFeedbackUserId())
                    .eqIfPresent(MesProFeedbackDO::getCreator, reqVO.getCreator())
                    .orderByDesc(MesProFeedbackDO::getId);
            return selectPage(reqVO, query);
        }
        // ä¸æ¶‰åŠä»»åŠ¡ç¼–ç æŸ¥è¯¢æ—¶ï¼Œèµ°æ™®é€šæŸ¥è¯¢
        return selectPage(reqVO, new LambdaQueryWrapperX<MesProFeedbackDO>()
                .likeIfPresent(MesProFeedbackDO::getCode, reqVO.getCode())
                .eqIfPresent(MesProFeedbackDO::getType, reqVO.getType())
                .eqIfPresent(MesProFeedbackDO::getWorkOrderId, reqVO.getWorkOrderId())
                .eqIfPresent(MesProFeedbackDO::getTaskId, reqVO.getTaskId())
                .eqIfPresent(MesProFeedbackDO::getStatus, reqVO.getStatus())
                .betweenIfPresent(MesProFeedbackDO::getFeedbackTime, reqVO.getFeedbackTime())
                .eqIfPresent(MesProFeedbackDO::getItemId, reqVO.getItemId())
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/task/MesProTaskIssueMapper.java
@@ -37,4 +37,16 @@
                .orderByDesc(MesProTaskIssueDO::getId));
    }
    default List<MesProTaskIssueDO> selectListBySourceDoc(String sourceDocType, Long sourceDocId) {
        return selectList(new LambdaQueryWrapperX<MesProTaskIssueDO>()
                .eq(MesProTaskIssueDO::getSourceDocType, sourceDocType)
                .eq(MesProTaskIssueDO::getSourceDocId, sourceDocId));
    }
    default void deleteBySourceDoc(String sourceDocType, Long sourceDocId) {
        delete(new LambdaQueryWrapperX<MesProTaskIssueDO>()
                .eq(MesProTaskIssueDO::getSourceDocType, sourceDocType)
                .eq(MesProTaskIssueDO::getSourceDocId, sourceDocId));
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/workorder/MesProWorkOrderParamRecordDetailMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
package cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderParamRecordDetailDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
import java.util.List;
/**
 * MES ç”Ÿäº§å·¥å•参数记录明细 Mapper
 *
 * @author èŠ‹é“æºç 
 */
@Mapper
public interface MesProWorkOrderParamRecordDetailMapper extends BaseMapperX<MesProWorkOrderParamRecordDetailDO> {
    default List<MesProWorkOrderParamRecordDetailDO> selectListByRecordId(Long recordId) {
        return selectList(new LambdaQueryWrapperX<MesProWorkOrderParamRecordDetailDO>()
                .eq(MesProWorkOrderParamRecordDetailDO::getRecordId, recordId)
                .orderByAsc(MesProWorkOrderParamRecordDetailDO::getId));
    }
    default List<MesProWorkOrderParamRecordDetailDO> selectListByRecordIds(Collection<Long> recordIds) {
        return selectList(new LambdaQueryWrapperX<MesProWorkOrderParamRecordDetailDO>()
                .in(MesProWorkOrderParamRecordDetailDO::getRecordId, recordIds)
                .orderByAsc(MesProWorkOrderParamRecordDetailDO::getId));
    }
    default void deleteByRecordId(Long recordId) {
        delete(new LambdaQueryWrapperX<MesProWorkOrderParamRecordDetailDO>()
                .eq(MesProWorkOrderParamRecordDetailDO::getRecordId, recordId));
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/workorder/MesProWorkOrderParamRecordMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
package cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.paramrecord.MesProWorkOrderParamRecordPageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderParamRecordDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
 * MES ç”Ÿäº§å·¥å•参数记录 Mapper
 *
 * @author èŠ‹é“æºç 
 */
@Mapper
public interface MesProWorkOrderParamRecordMapper extends BaseMapperX<MesProWorkOrderParamRecordDO> {
    default PageResult<MesProWorkOrderParamRecordDO> selectPageByWorkOrderId(Long taskId,
                                                                             MesProWorkOrderParamRecordPageReqVO pageReqVO) {
        return selectPage(pageReqVO, new LambdaQueryWrapperX<MesProWorkOrderParamRecordDO>()
                .eq(MesProWorkOrderParamRecordDO::getTaskId, taskId)
                .likeIfPresent(MesProWorkOrderParamRecordDO::getRecordNo, pageReqVO.getRecordNo())
                .likeIfPresent(MesProWorkOrderParamRecordDO::getTemplateName, pageReqVO.getTemplateName())
                .betweenIfPresent(MesProWorkOrderParamRecordDO::getCreateTime, pageReqVO.getCreateTime())
                .orderByDesc(MesProWorkOrderParamRecordDO::getId));
    }
    default List<MesProWorkOrderParamRecordDO> selectListByWorkOrderId(Long workOrderId) {
        return selectList(new LambdaQueryWrapperX<MesProWorkOrderParamRecordDO>()
                .eq(MesProWorkOrderParamRecordDO::getWorkOrderId, workOrderId)
                .orderByDesc(MesProWorkOrderParamRecordDO::getId));
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productissue/MesWmProductIssueLineMapper.java
@@ -21,6 +21,10 @@
                .orderByDesc(MesWmProductIssueLineDO::getId));
    }
    default List<MesWmProductIssueLineDO> selectListByTaskId(Long taskId) {
        return selectList(MesWmProductIssueLineDO::getTaskId, taskId);
    }
    default List<MesWmProductIssueLineDO> selectListByIssueId(Long issueId) {
        return selectList(MesWmProductIssueLineDO::getIssueId, issueId);
    }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productissue/MesWmProductIssueMapper.java
@@ -19,6 +19,7 @@
                .likeIfPresent(MesWmProductIssueDO::getName, reqVO.getName())
                .eqIfPresent(MesWmProductIssueDO::getWorkstationId, reqVO.getWorkstationId())
                .eqIfPresent(MesWmProductIssueDO::getWorkOrderId, reqVO.getWorkOrderId())
                .eqIfPresent(MesWmProductIssueDO::getTaskId, reqVO.getTaskId())
                .eqIfPresent(MesWmProductIssueDO::getStatus, reqVO.getStatus())
                .betweenIfPresent(MesWmProductIssueDO::getIssueDate, reqVO.getIssueDate())
                .orderByDesc(MesWmProductIssueDO::getId));
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/returnissue/MesWmReturnIssueMapper.java
@@ -19,6 +19,7 @@
                .likeIfPresent(MesWmReturnIssueDO::getName, reqVO.getName())
                .eqIfPresent(MesWmReturnIssueDO::getWorkstationId, reqVO.getWorkstationId())
                .eqIfPresent(MesWmReturnIssueDO::getWorkOrderId, reqVO.getWorkOrderId())
                .eqIfPresent(MesWmReturnIssueDO::getTaskId, reqVO.getTaskId())
                .eqIfPresent(MesWmReturnIssueDO::getType, reqVO.getType())
                .orderByDesc(MesWmReturnIssueDO::getId));
    }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
@@ -265,6 +265,13 @@
    ErrorCode PRO_WORK_ORDER_PRODUCT_MISMATCH = new ErrorCode(1_040_502_005, "当前产品物料与生产工单产品不一致");
    ErrorCode PRO_WORK_ORDER_BOM_NOT_EXISTS = new ErrorCode(1_040_502_100, "生产工单BOM不存在");
    // ========== MES ç”Ÿäº§ç®¡ç†-生产工单参数记录(1-040-502-200) ==========
    ErrorCode PRO_WORK_ORDER_PARAM_RECORD_NOT_EXISTS = new ErrorCode(1_040_502_200, "工单参数记录不存在");
    ErrorCode PRO_WORK_ORDER_PARAM_RECORD_TEMPLATE_NO_DETAIL = new ErrorCode(1_040_502_201, "参数模板没有明细,无法创建记录");
    ErrorCode PRO_WORK_ORDER_PARAM_RECORD_DETAIL_NOT_IN_TEMPLATE = new ErrorCode(1_040_502_202, "参数明细不属于所选模板");
    ErrorCode PRO_WORK_ORDER_PARAM_RECORD_REQUIRED_PARAM_MISSING = new ErrorCode(1_040_502_203, "必填参数【{}】未填写");
    ErrorCode PRO_WORK_ORDER_PARAM_RECORD_TEMPLATE_NOT_CHANGE = new ErrorCode(1_040_502_204, "不允许更换参数模板,请删除后重新创建");
    // ========== MES ç”Ÿäº§ç®¡ç†-生产任务(1-040-503-000) ==========
    ErrorCode PRO_TASK_NOT_EXISTS = new ErrorCode(1_040_503_000, "生产任务不存在");
    ErrorCode PRO_TASK_ALREADY_FINISHED = new ErrorCode(1_040_503_001, "生产任务已完成或已取消,不能继续操作");
@@ -508,6 +515,7 @@
    ErrorCode WM_PRODUCT_ISSUE_DETAIL_ITEM_MISMATCH = new ErrorCode(1_040_708_201, "拣货明细的物料与领料单行的物料不一致");
    ErrorCode WM_PRODUCT_ISSUE_DETAIL_LINE_NOT_MATCH = new ErrorCode(1_040_708_202, "拣货明细不属于指定的领料出库单");
    ErrorCode WM_PRODUCT_ISSUE_NO_DETAIL = new ErrorCode(1_040_708_203, "领料出库单没有拣货明细,无法执行领出");
    ErrorCode WM_PRODUCT_ISSUE_TASK_NOT_EXISTS = new ErrorCode(1_040_708_204, "生产任务不存在");
    // ========== MES ä»“库管理-生产入库单(1-040-709-000) ==========
    ErrorCode WM_PRODUCT_PRODUCE_NOT_EXISTS = new ErrorCode(1_040_709_000, "生产入库单不存在");
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdItemServiceImpl.java
@@ -396,12 +396,13 @@
     */
    private void syncItemToMdm(MesMdItemDO item, MesMdItemSaveReqVO saveReqVO) {
        // 1. èŽ·å–ç‰©æ–™ç±»åž‹å¯¹åº”çš„ itemType
        Integer itemType = null;
        Long itemType = null;
        if (item.getItemTypeId() != null) {
            MesMdItemTypeDO itemTypeDO = itemTypeService.getItemType(item.getItemTypeId());
            if (itemTypeDO != null) {
                // MES: ITEM=物料, PRODUCT=产品 -> MDM: 1=原料, 2=半成品, 3=成品, 4=辅料
                itemType = convertMesItemTypeToMdm(itemTypeDO.getItemOrProduct(), itemTypeDO.getName());
//                itemType = convertMesItemTypeToMdm(itemTypeDO.getItemOrProduct(), itemTypeDO.getName());
                itemType = itemTypeDO.getId();
            }
        }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/mdm/MesMdmItemService.java
@@ -30,7 +30,7 @@
     * @return ç‰©æ–™
     */
    MesMdmItemDO validateItemExists(Long id);
    MesMdmItemDO mesValidateItemExists(Long id);
    /**
     * æ ¡éªŒç‰©æ–™å­˜åœ¨ä¸”启用
     *
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/mdm/MesMdmItemServiceImpl.java
@@ -1,8 +1,11 @@
package cn.iocoder.yudao.module.mes.service.mdm;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO;
import cn.iocoder.yudao.module.mes.dal.mysql.mdm.MesMdmItemMapper;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
@@ -25,6 +28,8 @@
    @Resource
    private MesMdmItemMapper mesMdmItemMapper;
    @Autowired
    private MesMdItemService mesMdItemService;
    @Override
    public MesMdmItemDO getItem(Long id) {
@@ -41,6 +46,19 @@
    }
    @Override
    public MesMdmItemDO mesValidateItemExists(Long itemId) {
        MesMdItemDO mesItem = mesMdItemService.getItem(itemId);
        if (mesItem == null) {
            throw exception(MD_ITEM_NOT_EXISTS);
        }
        MesMdmItemDO item = mesMdmItemMapper.selectById(mesItem.getMdmItemId());
        if (item == null) {
            throw exception(MD_ITEM_NOT_EXISTS);
        }
        return item;
    }
    @Override
    public MesMdmItemDO validateItemExistsAndEnable(Long id) {
        MesMdmItemDO item = validateItemExists(id);
        if (item.getStatus() != null && item.getStatus() == 1) {
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/feedback/MesProFeedbackServiceImpl.java
@@ -6,7 +6,6 @@
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedbackPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.feedback.vo.MesProFeedbackSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO;
@@ -20,7 +19,6 @@
import cn.iocoder.yudao.module.mes.dal.mysql.pro.feedback.MesProFeedbackMapper;
import cn.iocoder.yudao.module.mes.enums.pro.MesProFeedbackStatusEnum;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum;
import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService;
import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProcessService;
import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductBomService;
import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService;
@@ -60,8 +58,6 @@
    private MesProRouteProcessService routeProcessService;
    @Resource
    private MesProRouteProductBomService routeProductBomService;
    @Resource
    private MesMdWorkstationService workstationService;
    @Resource
    @Lazy // é¿å…å¾ªçŽ¯ä¾èµ–
    private MesProTaskService taskService;
@@ -205,6 +201,12 @@
            MesWmProductProduceDO produce = productProduceService.generateProductProduce(feedback, false);
            productProduceService.finishProductProduce(produce.getId());
            updateTaskAndWorkOrderByFeedback(feedback);
        } else {
            // 4.3 éžå…³é”®å·¥åºï¼šåªæ›´æ–°ä»»åŠ¡çš„å·²ç”Ÿäº§æ•°é‡ï¼ˆå·¥å•ä¸æ›´æ–°ï¼Œå·¥å•åªçœ‹å…³é”®å·¥åºäº§å‡ºï¼‰
            taskService.updateProducedQuantity(feedback.getTaskId(),
                    feedback.getFeedbackQuantity(),
                    ObjectUtil.defaultIfNull(feedback.getQualifiedQuantity(), BigDecimal.ZERO),
                    ObjectUtil.defaultIfNull(feedback.getUnqualifiedQuantity(), BigDecimal.ZERO));
        }
        // 5. éžå…³é”®å·¥åº / å…³é”®éžè´¨æ£€å·¥åºï¼šç›´æŽ¥å®Œæˆï¼ˆæ¸…é›¶ uncheckQuantity é˜²æ­¢ !key+check ç•™è„æ•°æ®ï¼‰
@@ -277,23 +279,30 @@
    /**
     * æ ¡éªŒæŠ¥å·¥å•的业务数据(创建 & ä¿®æ”¹å…±ç”¨ï¼‰
     *
     * <p>以生产任务为核心:前端只需传 taskId,workOrderId、routeId、processId、itemId、workstationId
     * ä»Žä»»åŠ¡è‡ªåŠ¨å¡«å……ï¼Œæ— éœ€å‰ç«¯é‡å¤ä¼ å…¥ã€‚
     *
     * @param reqVO æŠ¥å·¥è¯·æ±‚
     * @return å…³è”的生产任务
     */
    private MesProTaskDO validateFeedbackData(MesProFeedbackSaveReqVO reqVO) {
        // 1. æ ¡éªŒå·¥ä½œç«™å­˜åœ¨
        MesMdWorkstationDO workstation = workstationService.validateWorkstationExists(reqVO.getWorkstationId());
        if (ObjUtil.notEqual(workstation.getProcessId(), reqVO.getProcessId())) {
            throw exception(PRO_WORKSTATION_PROCESS_MISMATCH);
        }
        // 1. æ ¡éªŒä»»åŠ¡å­˜åœ¨ä¸”æœªç»ˆæ€
        MesProTaskDO task = taskService.validateTaskNotFinished(reqVO.getTaskId());
        // 2.1 æ ¡éªŒå·¥è‰ºè·¯çº¿ + å·¥åºé…ç½®æœ‰æ•ˆ
        // 2. ä»Žä»»åŠ¡è‡ªåŠ¨å¡«å……å…³è”å­—æ®µï¼ˆå‰ç«¯æ— éœ€ä¼ å…¥ï¼ŒåŽç«¯ç»Ÿä¸€ä»Žä»»åŠ¡èŽ·å–ï¼‰
        reqVO.setWorkOrderId(task.getWorkOrderId());
        reqVO.setRouteId(task.getRouteId());
        reqVO.setProcessId(task.getProcessId());
        reqVO.setItemId(task.getItemId());
        reqVO.setWorkstationId(task.getWorkstationId());
        // 3. æ ¡éªŒå·¥è‰ºè·¯çº¿ + å·¥åºé…ç½®æœ‰æ•ˆ
        MesProRouteProcessDO routeProcess = routeProcessService.getRouteProcessByRouteIdAndProcessId(
                reqVO.getRouteId(), reqVO.getProcessId());
                task.getRouteId(), task.getProcessId());
        if (routeProcess == null) {
            throw exception(PRO_FEEDBACK_ROUTE_PROCESS_INVALID);
        }
        // 2.2 æ ¡éªŒæ•°é‡
        // 4.1 æ ¡éªŒæ•°é‡
        boolean checkFlag = Boolean.TRUE.equals(routeProcess.getCheckFlag());
        if (checkFlag) {
            // éœ€è¦æ£€éªŒï¼šåªéœ€å¡«æŠ¥å·¥æ•°é‡ï¼Œä¸”å¿…é¡» > 0
@@ -310,33 +319,7 @@
            }
        }
        // 3. æ ¡éªŒå·¥å•已确认
        MesProWorkOrderDO workOrder = workOrderService.validateWorkOrderConfirmed(reqVO.getWorkOrderId());
        if (ObjUtil.notEqual(workOrder.getProductId(), reqVO.getItemId())) {
            throw exception(PRO_WORK_ORDER_PRODUCT_MISMATCH);
        }
        // 4. æ ¡éªŒä»»åŠ¡å­˜åœ¨ä¸”æœªç»ˆæ€ï¼ˆå·²å®Œæˆ/已取消),并返回任务用于冗余 itemId
        MesProTaskDO task = taskService.validateTaskNotFinished(reqVO.getTaskId());
        validateTaskRelation(task, workstation, workOrder, reqVO);
        return task;
    }
    private void validateTaskRelation(MesProTaskDO task, MesMdWorkstationDO workstation,
                                      MesProWorkOrderDO workOrder, MesProFeedbackSaveReqVO reqVO) {
        if (ObjUtil.notEqual(task.getWorkOrderId(), workOrder.getId())) {
            throw exception(PRO_TASK_WORK_ORDER_MISMATCH);
        }
        if (ObjUtil.notEqual(task.getWorkstationId(), workstation.getId())) {
            throw exception(PRO_TASK_WORKSTATION_MISMATCH);
        }
        if (ObjUtil.notEqual(task.getRouteId(), reqVO.getRouteId())
                || ObjUtil.notEqual(task.getProcessId(), reqVO.getProcessId())) {
            throw exception(PRO_TASK_ROUTE_PROCESS_MISMATCH);
        }
        if (ObjUtil.notEqual(task.getItemId(), reqVO.getItemId())) {
            throw exception(PRO_TASK_ITEM_MISMATCH);
        }
    }
    @Override
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskIssueService.java
@@ -69,4 +69,12 @@
     */
    Long getTaskIssueCountByUnitMeasureId(Long unitMeasureId);
    /**
     * æ ¹æ®æ¥æºå•据删除投料记录
     *
     * @param sourceDocType æ¥æºå•据类型
     * @param sourceDocId   æ¥æºå•据编号
     */
    void deleteTaskIssueBySourceDoc(String sourceDocType, Long sourceDocId);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/task/MesProTaskIssueServiceImpl.java
@@ -88,6 +88,11 @@
        return taskIssueMapper.selectCountByUnitMeasureId(unitMeasureId);
    }
    @Override
    public void deleteTaskIssueBySourceDoc(String sourceDocType, Long sourceDocId) {
        taskIssueMapper.deleteBySourceDoc(sourceDocType, sourceDocId);
    }
    private void validateTaskIssueExists(Long id) {
        if (taskIssueMapper.selectById(id) == null) {
            throw exception(PRO_TASK_ISSUE_NOT_EXISTS);
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderParamRecordService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,67 @@
package cn.iocoder.yudao.module.mes.service.pro.workorder;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.paramrecord.MesProWorkOrderParamRecordPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.paramrecord.MesProWorkOrderParamRecordSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderParamRecordDO;
import jakarta.validation.Valid;
import java.util.List;
/**
 * MES ç”Ÿäº§å·¥å•参数记录 Service æŽ¥å£
 *
 * @author èŠ‹é“æºç 
 */
public interface MesProWorkOrderParamRecordService {
    /**
     * åˆ›å»ºå·¥å•参数记录
     *
     * @param workOrderId å·¥å•编号
     * @param createReqVO åˆ›å»ºä¿¡æ¯
     * @return è®°å½•编号
     */
    Long createParamRecord(Long workOrderId, @Valid MesProWorkOrderParamRecordSaveReqVO createReqVO);
    /**
     * æ›´æ–°å·¥å•参数记录(仅更新填写值,不换模板)
     *
     * @param updateReqVO æ›´æ–°ä¿¡æ¯
     */
    void updateParamRecord(@Valid MesProWorkOrderParamRecordSaveReqVO updateReqVO);
    /**
     * åˆ é™¤å·¥å•参数记录
     *
     * @param id è®°å½•编号
     */
    void deleteParamRecord(Long id);
    /**
     * èŽ·å¾—å·¥å•å‚æ•°è®°å½•
     *
     * @param id è®°å½•编号
     * @return å‚数记录
     */
    MesProWorkOrderParamRecordDO getParamRecord(Long id);
    /**
     * èŽ·å¾—å·¥å•å‚æ•°è®°å½•åˆ†é¡µ
     *
     * @param workOrderId å·¥å•编号
     * @param pageReqVO   åˆ†é¡µæŸ¥è¯¢
     * @return å‚数记录分页
     */
    PageResult<MesProWorkOrderParamRecordDO> getParamRecordPageByWorkOrderId(Long taskId,
                                                                             MesProWorkOrderParamRecordPageReqVO pageReqVO);
    /**
     * èŽ·å¾—å·¥å•å‚æ•°è®°å½•åˆ—è¡¨
     *
     * @param workOrderId å·¥å•编号
     * @return å‚数记录列表
     */
    List<MesProWorkOrderParamRecordDO> getParamRecordListByWorkOrderId(Long workOrderId);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderParamRecordServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,204 @@
package cn.iocoder.yudao.module.mes.service.pro.workorder;
import cn.hutool.core.collection.CollUtil;
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.pro.workorder.vo.paramrecord.MesProWorkOrderParamRecordPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.paramrecord.MesProWorkOrderParamRecordSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.processparamtemplate.MesPdProcessParamTemplateDetailDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pd.processparamtemplate.MesPdProcessParamTemplateDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderParamRecordDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderParamRecordDetailDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkOrderParamRecordDetailMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkOrderParamRecordMapper;
import cn.iocoder.yudao.module.mes.service.pd.processparamtemplate.MesPdProcessParamTemplateService;
import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
/**
 * MES ç”Ÿäº§å·¥å•参数记录 Service å®žçŽ°ç±»
 *
 * @author èŠ‹é“æºç 
 */
@Service
@Validated
public class MesProWorkOrderParamRecordServiceImpl implements MesProWorkOrderParamRecordService {
    @Resource
    private MesProWorkOrderParamRecordMapper paramRecordMapper;
    @Resource
    private MesProWorkOrderParamRecordDetailMapper paramRecordDetailMapper;
    @Resource
    private MesProWorkOrderService workOrderService;
    @Resource
    private MesPdProcessParamTemplateService templateService;
    @Autowired
    private MesProTaskService mesProTaskService;
    @Autowired
    private MesProWorkOrderService mesProWorkOrderService;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createParamRecord(Long taskId, MesProWorkOrderParamRecordSaveReqVO createReqVO) {
        // 1. æ ¡éªŒå·¥å•存在
        // 2. æ ¡éªŒæ¨¡æ¿å­˜åœ¨ï¼ŒèŽ·å–æ¨¡æ¿ä¿¡æ¯ + æ˜Žç»†åˆ—表
        MesPdProcessParamTemplateDO template = templateService.validateProcessParamTemplateExists(createReqVO.getTemplateId());
        List<MesPdProcessParamTemplateDetailDO> templateDetails =
                templateService.getProcessParamTemplateDetailListByTemplateId(createReqVO.getTemplateId());
        if (CollUtil.isEmpty(templateDetails)) {
            throw exception(PRO_WORK_ORDER_PARAM_RECORD_TEMPLATE_NO_DETAIL);
        }
        // 3. æž„建明细 templateDetailId â†’ templateDetail çš„æ˜ å°„,用于快照
        Map<Long, MesPdProcessParamTemplateDetailDO> detailMap = templateDetails.stream()
                .collect(Collectors.toMap(MesPdProcessParamTemplateDetailDO::getId, d -> d));
        // 4. æ ¡éªŒå¿…填参数都有值
        for (MesProWorkOrderParamRecordSaveReqVO.DetailItem item : createReqVO.getDetails()) {
            MesPdProcessParamTemplateDetailDO templateDetail = detailMap.get(item.getTemplateDetailId());
            if (templateDetail == null) {
                throw exception(PRO_WORK_ORDER_PARAM_RECORD_DETAIL_NOT_IN_TEMPLATE);
            }
            if (Boolean.TRUE.equals(templateDetail.getRequired())
                    && (item.getParamValue() == null || item.getParamValue().isBlank())) {
                throw exception(PRO_WORK_ORDER_PARAM_RECORD_REQUIRED_PARAM_MISSING, templateDetail.getParamName());
            }
        }
        MesProTaskDO task = mesProTaskService.getTask(taskId);
        MesProWorkOrderDO workOrder = mesProWorkOrderService.getWorkOrder(task.getWorkOrderId());
        // 5. æ’入主表(快照模板编码、名称)
        MesProWorkOrderParamRecordDO record = MesProWorkOrderParamRecordDO.builder()
                .workOrderId(workOrder.getId())
                .taskId(taskId)
                .templateId(template.getId())
                .templateCode(template.getTemplateCode())
                .templateName(template.getTemplateName())
                .recordNo(createReqVO.getRecordNo())
                .remark(createReqVO.getRemark())
                .build();
        paramRecordMapper.insert(record);
        // 6. æ’入明细表(快照参数定义 + ç”¨æˆ·å¡«å†™å€¼ï¼‰
        List<MesProWorkOrderParamRecordDetailDO> detailDOs = createReqVO.getDetails().stream()
                .map(item -> {
                    MesPdProcessParamTemplateDetailDO templateDetail = detailMap.get(item.getTemplateDetailId());
                    return MesProWorkOrderParamRecordDetailDO.builder()
                            .recordId(record.getId())
                            .templateDetailId(templateDetail.getId())
                            .paramCode(templateDetail.getParamCode())
                            .paramName(templateDetail.getParamName())
                            .paramType(templateDetail.getParamType())
                            .unitMeasureId(templateDetail.getUnitMeasureId())
                            .paramValue(item.getParamValue())
                            .build();
                }).toList();
        paramRecordDetailMapper.insertBatch(detailDOs);
        return record.getId();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateParamRecord(MesProWorkOrderParamRecordSaveReqVO updateReqVO) {
        // 1. æ ¡éªŒå­˜åœ¨
        MesProWorkOrderParamRecordDO record = validateParamRecordExists(updateReqVO.getId());
        // 2. æ ¡éªŒæ¨¡æ¿æœªå˜æ›´ï¼ˆä¸å…è®¸æ¢æ¨¡æ¿ï¼Œåªèƒ½ä¿®æ”¹å¡«å†™å€¼ï¼‰
        if (!record.getTemplateId().equals(updateReqVO.getTemplateId())) {
            throw exception(PRO_WORK_ORDER_PARAM_RECORD_TEMPLATE_NOT_CHANGE);
        }
        // 3. èŽ·å–æ¨¡æ¿æ˜Žç»†ï¼Œæ ¡éªŒå¿…å¡«å‚æ•°
        List<MesPdProcessParamTemplateDetailDO> templateDetails =
                templateService.getProcessParamTemplateDetailListByTemplateId(record.getTemplateId());
        Map<Long, MesPdProcessParamTemplateDetailDO> detailMap = templateDetails.stream()
                .collect(Collectors.toMap(MesPdProcessParamTemplateDetailDO::getId, d -> d));
        for (MesProWorkOrderParamRecordSaveReqVO.DetailItem item : updateReqVO.getDetails()) {
            MesPdProcessParamTemplateDetailDO templateDetail = detailMap.get(item.getTemplateDetailId());
            if (templateDetail == null) {
                throw exception(PRO_WORK_ORDER_PARAM_RECORD_DETAIL_NOT_IN_TEMPLATE);
            }
            if (Boolean.TRUE.equals(templateDetail.getRequired())
                    && (item.getParamValue() == null || item.getParamValue().isBlank())) {
                throw exception(PRO_WORK_ORDER_PARAM_RECORD_REQUIRED_PARAM_MISSING, templateDetail.getParamName());
            }
        }
        // 4. æ›´æ–°ä¸»è¡¨
        MesProWorkOrderParamRecordDO updateObj = new MesProWorkOrderParamRecordDO();
        updateObj.setId(updateReqVO.getId());
        updateObj.setRecordNo(updateReqVO.getRecordNo());
        updateObj.setRemark(updateReqVO.getRemark());
        paramRecordMapper.updateById(updateObj);
        // 5. åˆ é™¤æ—§æ˜Žç»† + æ’入新明细
        paramRecordDetailMapper.deleteByRecordId(record.getId());
        List<MesProWorkOrderParamRecordDetailDO> detailDOs = updateReqVO.getDetails().stream()
                .map(item -> {
                    MesPdProcessParamTemplateDetailDO templateDetail = detailMap.get(item.getTemplateDetailId());
                    return MesProWorkOrderParamRecordDetailDO.builder()
                            .recordId(record.getId())
                            .templateDetailId(templateDetail.getId())
                            .paramCode(templateDetail.getParamCode())
                            .paramName(templateDetail.getParamName())
                            .paramType(templateDetail.getParamType())
                            .unitMeasureId(templateDetail.getUnitMeasureId())
                            .paramValue(item.getParamValue())
                            .build();
                }).toList();
        paramRecordDetailMapper.insertBatch(detailDOs);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteParamRecord(Long id) {
        // 1. æ ¡éªŒå­˜åœ¨
        validateParamRecordExists(id);
        // 2. åˆ é™¤ä¸»è¡¨ + æ˜Žç»†
        paramRecordMapper.deleteById(id);
        paramRecordDetailMapper.deleteByRecordId(id);
    }
    @Override
    public MesProWorkOrderParamRecordDO getParamRecord(Long id) {
        return paramRecordMapper.selectById(id);
    }
    @Override
    public PageResult<MesProWorkOrderParamRecordDO> getParamRecordPageByWorkOrderId(Long taskId,
                                                                                    MesProWorkOrderParamRecordPageReqVO pageReqVO) {
        return paramRecordMapper.selectPageByWorkOrderId(taskId, pageReqVO);
    }
    @Override
    public List<MesProWorkOrderParamRecordDO> getParamRecordListByWorkOrderId(Long workOrderId) {
        return paramRecordMapper.selectListByWorkOrderId(workOrderId);
    }
    // ==================== æ ¡éªŒæ–¹æ³• ====================
    private MesProWorkOrderParamRecordDO validateParamRecordExists(Long id) {
        MesProWorkOrderParamRecordDO record = paramRecordMapper.selectById(id);
        if (record == null) {
            throw exception(PRO_WORK_ORDER_PARAM_RECORD_NOT_EXISTS);
        }
        return record;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueLineServiceImpl.java
@@ -114,7 +114,7 @@
        // æ ¡éªŒç‰©æ–™åœ¨å·¥å• BOM ä¸­
        validateItemInWorkOrderBom(reqVO.getIssueId(), reqVO.getItemId());
        // æ ¡éªŒç‰©æ–™å­˜åœ¨
        mdmItemService.validateItemExistsAndEnable(reqVO.getItemId());
        mdmItemService.mesValidateItemExists(reqVO.getItemId());
    }
    private void validateItemInWorkOrderBom(Long issueId, Long itemId) {
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productissue/MesWmProductIssueServiceImpl.java
@@ -8,15 +8,22 @@
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.module.mes.controller.admin.wm.productissue.vo.MesWmProductIssuePageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.productissue.vo.MesWmProductIssueSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskIssueDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productissue.MesWmProductIssueDetailDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productissue.MesWmProductIssueDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productissue.MesWmProductIssueLineDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pro.task.MesProTaskIssueMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.wm.productissue.MesWmProductIssueMapper;
import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductIssueStatusEnum;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmStockReserveBizTypeEnum;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService;
import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskIssueService;
import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
import cn.iocoder.yudao.module.mes.service.wm.stockreserve.MesWmStockReserveService;
import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
@@ -35,6 +42,7 @@
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
@@ -58,6 +66,14 @@
    @Resource
    private MesProWorkOrderService workOrderService;
    @Resource
    private MesProTaskService taskService;
    @Resource
    private MesProTaskIssueService taskIssueService;
    @Resource
    private MesProTaskIssueMapper taskIssueMapper;
    @Resource
    private MesMdItemService mdmItemService;
    @Resource
    private MesWmTransactionService wmTransactionService;
    @Resource
    private MesWmWarehouseService warehouseService;
@@ -74,7 +90,16 @@
        // 1. æ ¡éªŒå…³è”数据
        validateProductIssueSaveData(createReqVO);
        // 2. æ’入主表
        // 2. ä»Ž task è‡ªåЍ另入 workOrderId å’Œ workstationId
        MesProTaskDO task = taskService.validateTaskExists(createReqVO.getTaskId());
        if (createReqVO.getWorkOrderId() == null) {
            createReqVO.setWorkOrderId(task.getWorkOrderId());
        }
        if (createReqVO.getWorkstationId() == null) {
            createReqVO.setWorkstationId(task.getWorkstationId());
        }
        // 3. æ’入主表
        MesWmProductIssueDO issue = BeanUtils.toBean(createReqVO, MesWmProductIssueDO.class);
        issue.setStatus(MesWmProductIssueStatusEnum.PREPARE.getStatus());
        issueMapper.insert(issue);
@@ -89,7 +114,16 @@
        // 1.2 æ ¡éªŒå…³è”数据
        validateProductIssueSaveData(updateReqVO);
        // 2. æ›´æ–°ä¸»è¡¨
        // 2. ä»Ž task è‡ªåЍ另入 workOrderId å’Œ workstationId
        MesProTaskDO task = taskService.validateTaskExists(updateReqVO.getTaskId());
        if (updateReqVO.getWorkOrderId() == null) {
            updateReqVO.setWorkOrderId(task.getWorkOrderId());
        }
        if (updateReqVO.getWorkstationId() == null) {
            updateReqVO.setWorkstationId(task.getWorkstationId());
        }
        // 3. æ›´æ–°ä¸»è¡¨
        MesWmProductIssueDO updateObj = BeanUtils.toBean(updateReqVO, MesWmProductIssueDO.class);
        issueMapper.updateById(updateObj);
    }
@@ -192,7 +226,10 @@
        // 3. éåŽ†æ‰€æœ‰æ˜Žç»†ï¼Œåˆ›å»ºåº“å­˜äº‹åŠ¡ï¼ˆæ‰£å‡åº“å­˜ + è®°å½•流水)
        createTransactionList(issue);
        // 4. æ›´æ–°å‡ºåº“单状态 + é¢†æ–™æ—¥æœŸ
        // 4. è‡ªåŠ¨åˆ›å»ºä»»åŠ¡æŠ•æ–™è®°å½•
        createTaskIssueRecords(issue);
        // 5. æ›´æ–°å‡ºåº“单状态 + é¢†æ–™æ—¥æœŸ
        issueMapper.updateById(new MesWmProductIssueDO()
                .setId(id).setStatus(MesWmProductIssueStatusEnum.FINISHED.getStatus())
                .setIssueDate(LocalDateTime.now()));
@@ -227,6 +264,64 @@
        }
    }
    /**
     * é¢†æ–™å‡ºåº“完成时,自动创建任务投料记录
     * æŒ‰è¡ŒéåŽ†ï¼Œæ¯è¡Œåˆ›å»ºä¸€æ¡ task_issue è®°å½•
     */
    private void createTaskIssueRecords(MesWmProductIssueDO issue) {
        List<MesWmProductIssueLineDO> lines = issueLineService.getProductIssueLineListByIssueId(issue.getId());
        if (CollUtil.isEmpty(lines)) {
            return;
        }
        // æ‰¹é‡æŸ¥è¯¢ç‰©æ–™ä¿¡æ¯ï¼ˆèŽ·å– unitMeasureId)
        Map<Long, MesMdItemDO> itemMap = mdmItemService.getItemMap(
                CollectionUtils.convertSet(lines, MesWmProductIssueLineDO::getItemId));
        // èŽ·å–æ˜Žç»†åˆ—è¡¨ï¼Œç”¨äºŽæå– batchCode
        List<MesWmProductIssueDetailDO> details = issueDetailService.getProductIssueDetailListByIssueId(issue.getId());
        Map<Long, List<MesWmProductIssueDetailDO>> detailMapByLineId = CollectionUtils.convertMultiMap(
                details, MesWmProductIssueDetailDO::getLineId);
        for (MesWmProductIssueLineDO line : lines) {
            // è¡Œçš„ taskId ä¼˜å…ˆï¼Œå¦åˆ™å–主表的 taskId
            Long taskId = line.getTaskId() != null ? line.getTaskId() : issue.getTaskId();
            if (taskId == null) {
                continue;
            }
            // èŽ·å–ç‰©æ–™å•ä½
            MesMdItemDO item = itemMap.get(line.getItemId());
            Long unitMeasureId = item != null ? item.getUnitMeasureId() : null;
            // èŽ·å–æ‰¹æ¬¡å·ï¼šå–è¯¥è¡Œç¬¬ä¸€ä¸ªæ˜Žç»†çš„ batchCode
            String batchCode = null;
            List<MesWmProductIssueDetailDO> lineDetails = detailMapByLineId.get(line.getId());
            if (CollUtil.isNotEmpty(lineDetails)) {
                MesWmProductIssueDetailDO firstDetail = lineDetails.get(0);
                batchCode = firstDetail.getBatchCode();
            }
            // åˆ›å»ºä»»åŠ¡æŠ•æ–™è®°å½•
            MesProTaskIssueDO taskIssue = MesProTaskIssueDO.builder()
                    .taskId(taskId)
                    .workOrderId(issue.getWorkOrderId())
                    .workstationId(issue.getWorkstationId())
                    .sourceDocType("PRODUCT_ISSUE")
                    .sourceDocId(issue.getId())
                    .sourceLineId(line.getId())
                    .sourceDocCode(issue.getCode())
                    .batchCode(batchCode)
                    .itemId(line.getItemId())
                    .unitMeasureId(unitMeasureId)
                    .issuedQuantity(line.getQuantity())
                    .availableQuantity(line.getQuantity()) // åˆå§‹å…¨å¯ç”¨
                    .usedQuantity(BigDecimal.ZERO)
                    .build();
            taskIssueMapper.insert(taskIssue);
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelProductIssue(Long id) {
@@ -241,6 +336,9 @@
        // é‡Šæ”¾åº“存占用
        stockReserveService.releaseReserve(MesWmStockReserveBizTypeEnum.PRODUCT_ISSUE.getType(), id);
        // åˆ é™¤é€šè¿‡è¯¥é¢†æ–™å•创建的任务投料记录
        taskIssueService.deleteTaskIssueBySourceDoc("PRODUCT_ISSUE", id);
        // å–消
        issueMapper.updateById(new MesWmProductIssueDO()
@@ -300,7 +398,13 @@
     */
    private void validateProductIssueSaveData(MesWmProductIssueSaveReqVO reqVO) {
        validateCodeUnique(reqVO.getId(), reqVO.getCode());
        workOrderService.validateWorkOrderConfirmed(reqVO.getWorkOrderId());
        // æ ¡éªŒ task å­˜åœ¨
        MesProTaskDO task = taskService.validateTaskExists(reqVO.getTaskId());
        // æ ¡éªŒ task å…³è”的工单已确认
        Long workOrderId = reqVO.getWorkOrderId() != null ? reqVO.getWorkOrderId() : task.getWorkOrderId();
        if (workOrderId != null) {
            workOrderService.validateWorkOrderConfirmed(workOrderId);
        }
        if (reqVO.getWorkstationId() != null) {
            workstationService.validateWorkstationExistsAndEnable(reqVO.getWorkstationId());
        }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnissue/MesWmReturnIssueServiceImpl.java
@@ -10,9 +10,12 @@
import cn.iocoder.yudao.module.mes.controller.admin.wm.returnissue.vo.MesWmReturnIssuePageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.returnissue.vo.MesWmReturnIssueSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskIssueDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnissue.MesWmReturnIssueDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnissue.MesWmReturnIssueDetailDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnissue.MesWmReturnIssueLineDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pro.task.MesProTaskIssueMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.wm.returnissue.MesWmReturnIssueMapper;
import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum;
@@ -20,6 +23,7 @@
import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService;
import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService;
import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
@@ -61,6 +65,10 @@
    @Resource
    private MesProWorkOrderService workOrderService;
    @Resource
    private MesProTaskService taskService;
    @Resource
    private MesProTaskIssueMapper taskIssueMapper;
    @Resource
    private MesMdmItemService mdmItemService;
    @Resource
    private MesWmTransactionService wmTransactionService;
@@ -77,7 +85,16 @@
        // 1. æ ¡éªŒå…³è”数据
        validateReturnIssueSaveData(createReqVO);
        // 2. æ’入主表
        // 2. ä»Ž task è‡ªåЍ另入 workOrderId å’Œ workstationId
        MesProTaskDO task = taskService.validateTaskExists(createReqVO.getTaskId());
        if (createReqVO.getWorkOrderId() == null) {
            createReqVO.setWorkOrderId(task.getWorkOrderId());
        }
        if (createReqVO.getWorkstationId() == null) {
            createReqVO.setWorkstationId(task.getWorkstationId());
        }
        // 3. æ’入主表
        MesWmReturnIssueDO issue = BeanUtils.toBean(createReqVO, MesWmReturnIssueDO.class);
        issue.setStatus(MesWmReturnIssueStatusEnum.PREPARE.getStatus());
        issueMapper.insert(issue);
@@ -92,11 +109,20 @@
        // 1.2 æ ¡éªŒå…³è”数据
        validateReturnIssueSaveData(updateReqVO);
        // 2. æ›´æ–°ä¸»è¡¨
        // 2. ä»Ž task è‡ªåЍ另入 workOrderId å’Œ workstationId
        MesProTaskDO task = taskService.validateTaskExists(updateReqVO.getTaskId());
        if (updateReqVO.getWorkOrderId() == null) {
            updateReqVO.setWorkOrderId(task.getWorkOrderId());
        }
        if (updateReqVO.getWorkstationId() == null) {
            updateReqVO.setWorkstationId(task.getWorkstationId());
        }
        // 3. æ›´æ–°ä¸»è¡¨
        MesWmReturnIssueDO updateObj = BeanUtils.toBean(updateReqVO, MesWmReturnIssueDO.class);
        issueMapper.updateById(updateObj);
        // 3. é€€æ–™ç±»åž‹å˜æ›´æ—¶ï¼Œåˆ·æ–°æ‰€æœ‰è¡Œçš„质量状态
        // 4. é€€æ–™ç±»åž‹å˜æ›´æ—¶ï¼Œåˆ·æ–°æ‰€æœ‰è¡Œçš„质量状态
        if (ObjUtil.notEqual(oldIssue.getType(), updateReqVO.getType())) {
            issueLineService.updateReturnIssueQualityStatusByIssueId(updateReqVO.getId(), updateReqVO.getType());
        }
@@ -192,7 +218,10 @@
        // 2. éåŽ†æ‰€æœ‰æ˜Žç»†ï¼Œåˆ›å»ºåº“å­˜äº‹åŠ¡ï¼ˆå¢žåŠ åº“å­˜ + è®°å½•流水)
        createTransactionList(issue);
        // 3. æ›´æ–°é€€æ–™å•状态
        // 3. æ‰£å‡ä»»åŠ¡æŠ•æ–™è®°å½•çš„å¯ç”¨é‡
        updateTaskIssueOnReturn(issue);
        // 4. æ›´æ–°é€€æ–™å•状态
        issueMapper.updateById(new MesWmReturnIssueDO()
                .setId(id).setStatus(MesWmReturnIssueStatusEnum.FINISHED.getStatus()));
    }
@@ -224,6 +253,36 @@
                    .setBizType(MesBizTypeConstants.WM_RETURN_ISSUE).setBizId(issue.getId())
                    .setBizCode(issue.getCode()).setBizLineId(detail.getLineId())
                    .setRelatedTransactionId(outTransactionId)); // å…³è”出库事务
        }
    }
    /**
     * é€€æ–™å®Œæˆæ—¶ï¼Œæ‰£å‡ä»»åŠ¡æŠ•æ–™è®°å½•çš„å¯ç”¨é‡
     * æŒ‰è¡ŒéåŽ†ï¼Œæ‰¾åˆ°å¯¹åº”çš„ task_issue è®°å½•,扣减 availableQuantity、issuedQuantity
     */
    private void updateTaskIssueOnReturn(MesWmReturnIssueDO issue) {
        List<MesWmReturnIssueLineDO> lines = issueLineService.getReturnIssueLineListByIssueId(issue.getId());
        if (CollUtil.isEmpty(lines)) {
            return;
        }
        for (MesWmReturnIssueLineDO line : lines) {
            Long taskId = line.getTaskId() != null ? line.getTaskId() : issue.getTaskId();
            if (taskId == null) {
                continue;
            }
            // æŸ¥æ‰¾è¯¥ task + item + æ¥æºä¸ºé¢†æ–™å‡ºåº“单 çš„æŠ•料记录
            List<MesProTaskIssueDO> taskIssues = taskIssueMapper.selectListByTaskId(taskId);
            MesProTaskIssueDO matchedIssue = CollUtil.findOne(taskIssues,
                    ti -> ti.getItemId().equals(line.getItemId())
                            && "PRODUCT_ISSUE".equals(ti.getSourceDocType()));
            if (matchedIssue != null) {
                // æ‰£å‡æŠ•料量和可用量
                BigDecimal newIssuedQty = matchedIssue.getIssuedQuantity().subtract(line.getQuantity());
                BigDecimal newAvailableQty = matchedIssue.getAvailableQuantity().subtract(line.getQuantity());
                matchedIssue.setIssuedQuantity(newIssuedQty.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : newIssuedQty);
                matchedIssue.setAvailableQuantity(newAvailableQty.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : newAvailableQty);
                taskIssueMapper.updateById(matchedIssue);
            }
        }
    }
@@ -289,7 +348,13 @@
     */
    private void validateReturnIssueSaveData(MesWmReturnIssueSaveReqVO reqVO) {
        validateCodeUnique(reqVO.getId(), reqVO.getCode());
        workOrderService.validateWorkOrderConfirmed(reqVO.getWorkOrderId());
        // æ ¡éªŒ task å­˜åœ¨
        MesProTaskDO task = taskService.validateTaskExists(reqVO.getTaskId());
        // æ ¡éªŒ task å…³è”的工单已确认
        Long workOrderId = reqVO.getWorkOrderId() != null ? reqVO.getWorkOrderId() : task.getWorkOrderId();
        if (workOrderId != null) {
            workOrderService.validateWorkOrderConfirmed(workOrderId);
        }
        if (reqVO.getWorkstationId() != null) {
            workstationService.validateWorkstationExistsAndEnable(reqVO.getWorkstationId());
        }