wanshenmean
7 小时以前 96adc295cb04fd135d63d3a907f2732274f90965
feat: 添加MES异步上传辅助服务并重构相关代码

refactor(WCS): 优化堆垛机任务检查逻辑和线程安全
fix(WCS): 修复调度中心服务状态判断错误
perf(WCS): 降低设备通信超时时间
refactor(WCS): 使用ConcurrentBag替换List实现线程安全存储
refactor(WMS): 重构MES上传逻辑统一使用辅助服务
docs: 补充和完善代码注释
已添加2个文件
已修改19个文件
1523 ■■■■■ 文件已修改
Code/.omc/state/last-tool-error.json 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/.omc/state/mission-state.json 388 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/.omc/state/subagent-tracking.json 276 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/QuartzNet/JobFactory.cs 48 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/QuartzNet/QuartzNetExtension.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/QuartzNet/SchedulerCenterServer.cs 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/StackerCrane/Common/CommonStackerCrane.cs 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/Storage.cs 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/appsettings.json 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineJob/CommonConveyorLineJob.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/CommonStackerCraneJob.cs 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneCommandBuilder.cs 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_IBasicService/IMesUploadHelper.cs 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_StockService/MesUploadHelper.cs 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_StockService/StockInfoService.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_StockService/StockSerivce.cs 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Inbound.cs 76 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Outbound.cs 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockInfoController.cs 116 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockInfoDetailController.cs 178 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/.omc/state/last-tool-error.json
@@ -1,7 +1,7 @@
{
  "tool_name": "Read",
  "tool_input_preview": "{\"file_path\":\"D:\\\\Git\\\\ShanMeiXinNengYuan\\\\Code\\\\WMS\\\\WIDESEA_WMSClient_Vben_v2\\\\apps\\\\web-ele\\\\src\\\\views\\\\dashboard\\\\index.vue\"}",
  "error": "File does not exist. Note: your current working directory is D:\\Git\\ShanMeiXinNengYuan\\Code.",
  "timestamp": "2026-04-20T01:28:36.836Z",
  "tool_name": "Bash",
  "tool_input_preview": "{\"command\":\"cd \\\"D:/Git/ShanMeiXinNengYuan/Code\\\" && dotnet build WMS/WIDESEA_WMSServer/WIDESEA_WMSServer.sln 2>&1\",\"timeout\":120000,\"description\":\"Build WMS solution to verify changes\"}",
  "error": "Exit code 1\n  æ­£åœ¨ç¡®å®šè¦è¿˜åŽŸçš„é¡¹ç›®â€¦\r\n  æ‰€æœ‰é¡¹ç›®å‡æ˜¯æœ€æ–°çš„,无法还原。\r\nD:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_Core\\AOP\\LogAOP.cs(169,123): warning CS8625: æ— æ³•å°† null å­—面量转换为非 null çš„引用类型。 [D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_Core\\WIDESEA_Core.csproj]\r\nD:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_Core\\Authorization\\AuthorizationResponse.cs(21,30): warning CS8625: æ— æ³•å°† null å­—面量转换为非 null çš„引用类型。 [D:\\Git\\ShanMeiXinNengYuan\\Code\\WMS\\WIDESEA_WMSServer\\WIDESEA_Core\\WIDES...",
  "timestamp": "2026-04-20T16:56:32.862Z",
  "retry_count": 1
}
Code/.omc/state/mission-state.json
@@ -1,5 +1,5 @@
{
  "updatedAt": "2026-04-20T02:13:36.765Z",
  "updatedAt": "2026-04-20T17:02:18.624Z",
  "missions": [
    {
      "id": "session:9007b9ea-1eb6-4d24-8fe7-2c3a949eac88:none",
@@ -1964,6 +1964,392 @@
          "sourceKey": "session-stop:a6eae12446c31bdfe"
        }
      ]
    },
    {
      "id": "session:bce34684-82ff-42dd-b951-6194cfe1e77c:none",
      "source": "session",
      "name": "none",
      "objective": "Session mission",
      "createdAt": "2026-04-20T15:49:37.351Z",
      "updatedAt": "2026-04-20T17:02:18.624Z",
      "status": "done",
      "workerCount": 30,
      "taskCounts": {
        "total": 30,
        "pending": 0,
        "blocked": 0,
        "inProgress": 0,
        "completed": 30,
        "failed": 0
      },
      "agents": [
        {
          "name": "general-purpose:af087f2",
          "role": "general-purpose",
          "ownership": "af087f2e64d433e39",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T17:02:18.624Z"
        },
        {
          "name": "general-purpose:a151e5f",
          "role": "general-purpose",
          "ownership": "a151e5f87f0cbdac6",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T15:50:53.499Z"
        },
        {
          "name": "general-purpose:a9435dc",
          "role": "general-purpose",
          "ownership": "a9435dc250bc7482e",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T15:50:38.393Z"
        },
        {
          "name": "general-purpose:a32b075",
          "role": "general-purpose",
          "ownership": "a32b0751a039672ef",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T15:51:30.118Z"
        },
        {
          "name": "general-purpose:aaa2a0f",
          "role": "general-purpose",
          "ownership": "aaa2a0f6b2a25bec0",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T15:51:23.092Z"
        },
        {
          "name": "general-purpose:a86b879",
          "role": "general-purpose",
          "ownership": "a86b879fb7a0801be",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T15:52:07.543Z"
        },
        {
          "name": "general-purpose:a12823e",
          "role": "general-purpose",
          "ownership": "a12823e675f358745",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T15:52:29.208Z"
        },
        {
          "name": "general-purpose:ae97a4d",
          "role": "general-purpose",
          "ownership": "ae97a4de553f0b4c3",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T15:52:16.206Z"
        },
        {
          "name": "general-purpose:a4a2d7d",
          "role": "general-purpose",
          "ownership": "a4a2d7dfcc130e3c8",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T15:53:19.832Z"
        },
        {
          "name": "general-purpose:afef0a2",
          "role": "general-purpose",
          "ownership": "afef0a2e4255227bd",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T15:53:08.591Z"
        },
        {
          "name": "general-purpose:ae1e2c3",
          "role": "general-purpose",
          "ownership": "ae1e2c3f798ee872a",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T15:53:36.457Z"
        },
        {
          "name": "general-purpose:a66e87c",
          "role": "general-purpose",
          "ownership": "a66e87c9223005128",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T15:56:26.414Z"
        },
        {
          "name": "general-purpose:a113f70",
          "role": "general-purpose",
          "ownership": "a113f704503b35113",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T15:54:23.336Z"
        },
        {
          "name": "general-purpose:a7cc134",
          "role": "general-purpose",
          "ownership": "a7cc1344397da9324",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T15:58:27.436Z"
        },
        {
          "name": "general-purpose:aeac471",
          "role": "general-purpose",
          "ownership": "aeac471bcaec36af3",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T16:00:25.655Z"
        },
        {
          "name": "general-purpose:a6c7e45",
          "role": "general-purpose",
          "ownership": "a6c7e458af6948696",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T15:57:35.457Z"
        },
        {
          "name": "general-purpose:a5144b6",
          "role": "general-purpose",
          "ownership": "a5144b6e94595b89f",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T16:01:24.405Z"
        },
        {
          "name": "general-purpose:ac4bbba",
          "role": "general-purpose",
          "ownership": "ac4bbba9b05c57c6a",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T16:03:43.062Z"
        },
        {
          "name": "general-purpose:ad3b6fa",
          "role": "general-purpose",
          "ownership": "ad3b6fad5f339a4e5",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T16:02:27.528Z"
        },
        {
          "name": "general-purpose:a72a84d",
          "role": "general-purpose",
          "ownership": "a72a84d56ec83e520",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T16:03:48.315Z"
        },
        {
          "name": "general-purpose:a62f850",
          "role": "general-purpose",
          "ownership": "a62f85025e6fd117b",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T16:04:24.590Z"
        },
        {
          "name": "general-purpose:a3265e6",
          "role": "general-purpose",
          "ownership": "a3265e63c320b7fb7",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T16:04:17.712Z"
        },
        {
          "name": "general-purpose:a75fe8e",
          "role": "general-purpose",
          "ownership": "a75fe8e3716ab2841",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T16:05:07.601Z"
        },
        {
          "name": "general-purpose:ab70218",
          "role": "general-purpose",
          "ownership": "ab7021856484bc64e",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T16:18:18.639Z"
        },
        {
          "name": "general-purpose:a9c7d87",
          "role": "general-purpose",
          "ownership": "a9c7d87d13a26caa1",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T16:18:25.585Z"
        },
        {
          "name": "general-purpose:ac4481c",
          "role": "general-purpose",
          "ownership": "ac4481cc8f2fd18cf",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T16:18:05.225Z"
        },
        {
          "name": "general-purpose:ad8e51b",
          "role": "general-purpose",
          "ownership": "ad8e51b3e49b68c5b",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T16:19:04.231Z"
        },
        {
          "name": "general-purpose:a8d6992",
          "role": "general-purpose",
          "ownership": "a8d69927a9c24e9b5",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T16:36:32.273Z"
        },
        {
          "name": "general-purpose:a9f7c77",
          "role": "general-purpose",
          "ownership": "a9f7c771d702937f2",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T16:35:27.503Z"
        },
        {
          "name": "general-purpose:aaecb7b",
          "role": "general-purpose",
          "ownership": "aaecb7b0d7ead5188",
          "status": "done",
          "currentStep": null,
          "latestUpdate": "completed",
          "completedSummary": null,
          "updatedAt": "2026-04-20T16:32:32.486Z"
        }
      ],
      "timeline": [
        {
          "id": "session-start:a8d69927a9c24e9b5:2026-04-20T16:31:25.163Z",
          "at": "2026-04-20T16:31:25.163Z",
          "kind": "update",
          "agent": "general-purpose:a8d6992",
          "detail": "started general-purpose:a8d6992",
          "sourceKey": "session-start:a8d69927a9c24e9b5"
        },
        {
          "id": "session-start:a9f7c771d702937f2:2026-04-20T16:31:25.222Z",
          "at": "2026-04-20T16:31:25.222Z",
          "kind": "update",
          "agent": "general-purpose:a9f7c77",
          "detail": "started general-purpose:a9f7c77",
          "sourceKey": "session-start:a9f7c771d702937f2"
        },
        {
          "id": "session-start:aaecb7b0d7ead5188:2026-04-20T16:31:25.289Z",
          "at": "2026-04-20T16:31:25.289Z",
          "kind": "update",
          "agent": "general-purpose:aaecb7b",
          "detail": "started general-purpose:aaecb7b",
          "sourceKey": "session-start:aaecb7b0d7ead5188"
        },
        {
          "id": "session-stop:aaecb7b0d7ead5188:2026-04-20T16:32:32.486Z",
          "at": "2026-04-20T16:32:32.486Z",
          "kind": "completion",
          "agent": "general-purpose:aaecb7b",
          "detail": "completed",
          "sourceKey": "session-stop:aaecb7b0d7ead5188"
        },
        {
          "id": "session-stop:a9f7c771d702937f2:2026-04-20T16:35:27.503Z",
          "at": "2026-04-20T16:35:27.503Z",
          "kind": "completion",
          "agent": "general-purpose:a9f7c77",
          "detail": "completed",
          "sourceKey": "session-stop:a9f7c771d702937f2"
        },
        {
          "id": "session-stop:a8d69927a9c24e9b5:2026-04-20T16:36:32.273Z",
          "at": "2026-04-20T16:36:32.273Z",
          "kind": "completion",
          "agent": "general-purpose:a8d6992",
          "detail": "completed",
          "sourceKey": "session-stop:a8d69927a9c24e9b5"
        },
        {
          "id": "session-stop:a02e13efa2a4d4a7d:2026-04-20T16:43:22.723Z",
          "at": "2026-04-20T16:43:22.723Z",
          "kind": "completion",
          "agent": "general-purpose:af087f2",
          "detail": "completed",
          "sourceKey": "session-stop:a02e13efa2a4d4a7d"
        },
        {
          "id": "session-stop:a82856d42f4ef095b:2026-04-20T17:02:18.624Z",
          "at": "2026-04-20T17:02:18.624Z",
          "kind": "completion",
          "agent": "general-purpose:af087f2",
          "detail": "completed",
          "sourceKey": "session-stop:a82856d42f4ef095b"
        }
      ]
    }
  ]
}
Code/.omc/state/subagent-tracking.json
@@ -1184,10 +1184,280 @@
      "status": "completed",
      "completed_at": "2026-04-20T02:08:23.744Z",
      "duration_ms": 81044
    },
    {
      "agent_id": "af087f2e64d433e39",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T15:49:37.351Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T15:50:06.600Z",
      "duration_ms": 29249
    },
    {
      "agent_id": "a151e5f87f0cbdac6",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T15:50:24.517Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T15:50:53.498Z",
      "duration_ms": 28981
    },
    {
      "agent_id": "a9435dc250bc7482e",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T15:50:24.578Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T15:50:38.390Z",
      "duration_ms": 13812
    },
    {
      "agent_id": "a32b0751a039672ef",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T15:51:08.683Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T15:51:30.117Z",
      "duration_ms": 21434
    },
    {
      "agent_id": "aaa2a0f6b2a25bec0",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T15:51:08.748Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T15:51:23.091Z",
      "duration_ms": 14343
    },
    {
      "agent_id": "a86b879fb7a0801be",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T15:51:49.188Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T15:52:07.541Z",
      "duration_ms": 18353
    },
    {
      "agent_id": "a12823e675f358745",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T15:51:49.202Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T15:52:29.206Z",
      "duration_ms": 40004
    },
    {
      "agent_id": "ae97a4de553f0b4c3",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T15:51:49.272Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T15:52:16.205Z",
      "duration_ms": 26933
    },
    {
      "agent_id": "a4a2d7dfcc130e3c8",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T15:52:54.548Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T15:53:19.831Z",
      "duration_ms": 25283
    },
    {
      "agent_id": "afef0a2e4255227bd",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T15:52:54.560Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T15:53:08.589Z",
      "duration_ms": 14029
    },
    {
      "agent_id": "ae1e2c3f798ee872a",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T15:52:54.621Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T15:53:36.456Z",
      "duration_ms": 41835
    },
    {
      "agent_id": "a66e87c9223005128",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T15:53:53.740Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T15:56:26.413Z",
      "duration_ms": 152673
    },
    {
      "agent_id": "a113f704503b35113",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T15:53:53.756Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T15:54:23.335Z",
      "duration_ms": 29579
    },
    {
      "agent_id": "a7cc1344397da9324",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T15:56:59.419Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T15:58:27.434Z",
      "duration_ms": 88015
    },
    {
      "agent_id": "aeac471bcaec36af3",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T15:56:59.484Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T16:00:25.654Z",
      "duration_ms": 206170
    },
    {
      "agent_id": "a6c7e458af6948696",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T15:56:59.548Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T15:57:35.456Z",
      "duration_ms": 35908
    },
    {
      "agent_id": "a5144b6e94595b89f",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T16:00:50.066Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T16:01:24.403Z",
      "duration_ms": 34337
    },
    {
      "agent_id": "ad3b6fad5f339a4e5",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T16:01:46.364Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T16:02:27.526Z",
      "duration_ms": 41162
    },
    {
      "agent_id": "ac4bbba9b05c57c6a",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T16:01:46.304Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T16:03:43.060Z",
      "duration_ms": 116756
    },
    {
      "agent_id": "a72a84d56ec83e520",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T16:01:46.423Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T16:03:48.313Z",
      "duration_ms": 121890
    },
    {
      "agent_id": "a62f85025e6fd117b",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T16:04:06.835Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T16:04:24.588Z",
      "duration_ms": 17753
    },
    {
      "agent_id": "a3265e63c320b7fb7",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T16:04:06.894Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T16:04:17.711Z",
      "duration_ms": 10817
    },
    {
      "agent_id": "a75fe8e3716ab2841",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T16:04:35.938Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T16:05:07.599Z",
      "duration_ms": 31661
    },
    {
      "agent_id": "ab7021856484bc64e",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T16:15:18.619Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T16:18:18.637Z",
      "duration_ms": 180018
    },
    {
      "agent_id": "a9c7d87d13a26caa1",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T16:15:18.684Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T16:18:25.584Z",
      "duration_ms": 186900
    },
    {
      "agent_id": "ac4481cc8f2fd18cf",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T16:15:18.748Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T16:18:05.224Z",
      "duration_ms": 166476
    },
    {
      "agent_id": "ad8e51b3e49b68c5b",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T16:18:33.453Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T16:19:04.229Z",
      "duration_ms": 30776
    },
    {
      "agent_id": "a8d69927a9c24e9b5",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T16:31:25.163Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T16:36:32.272Z",
      "duration_ms": 307109
    },
    {
      "agent_id": "a9f7c771d702937f2",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T16:31:25.222Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T16:35:27.501Z",
      "duration_ms": 242279
    },
    {
      "agent_id": "aaecb7b0d7ead5188",
      "agent_type": "general-purpose",
      "started_at": "2026-04-20T16:31:25.289Z",
      "parent_mode": "none",
      "status": "completed",
      "completed_at": "2026-04-20T16:32:32.484Z",
      "duration_ms": 67195
    }
  ],
  "total_spawned": 118,
  "total_completed": 127,
  "total_spawned": 135,
  "total_completed": 157,
  "total_failed": 0,
  "last_updated": "2026-04-20T02:13:36.867Z"
  "last_updated": "2026-04-20T17:02:18.739Z"
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/QuartzNet/JobFactory.cs
@@ -19,6 +19,7 @@
using Quartz.Spi;
using Quartz;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -28,7 +29,7 @@
namespace WIDESEAWCS_QuartzJob
{
    /// <summary>
    /// Job注入
    /// Job注入工厂,管理 Job å®žä¾‹çš„ DI ä½œç”¨åŸŸç”Ÿå‘½å‘¨æœŸ
    /// </summary>
    public class JobFactory : IJobFactory
    {
@@ -38,48 +39,71 @@
        private readonly IServiceProvider _serviceProvider;
        /// <summary>
        /// Job å®žä¾‹ä¸Žå¯¹åº”çš„ DI ä½œç”¨åŸŸæ˜ å°„,用于在 ReturnJob æ—¶æ­£ç¡®é‡Šæ”¾èµ„源
        /// </summary>
        private readonly ConcurrentDictionary<IJob, IServiceScope> _scopes = new();
        /// <summary>
        /// Job注入
        /// </summary>
        /// <param name="serviceProvider"></param>
        /// <param name="serviceProvider">服务提供者</param>
        public JobFactory(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }
        /// <summary>
        /// å®žçŽ°æŽ¥å£Job
        /// åˆ›å»º Job å®žä¾‹ï¼Œå¹¶ä¸ºæ¯ä¸ª Job åˆ›å»ºç‹¬ç«‹çš„ DI ä½œç”¨åŸŸ
        /// </summary>
        /// <param name="bundle"></param>
        /// <param name="scheduler"></param>
        /// <returns></returns>
        /// <param name="bundle">触发器触发上下文</param>
        /// <param name="scheduler">调度器实例</param>
        /// <returns>Job å®žä¾‹</returns>
        public IJob NewJob(TriggerFiredBundle bundle, IScheduler scheduler)
        {
            try
            {
                if (App.ExpDateTime != null && (DateTime.Now - App.ExpDateTime.GetValueOrDefault()).Seconds > 0)
                // éªŒè¯æœ‰æ•ˆæœŸï¼Œä½¿ç”¨ TotalSeconds é¿å… .Seconds å–模导致间歇性判断错误
                if (App.ExpDateTime != null && (DateTime.Now - App.ExpDateTime.GetValueOrDefault()).TotalSeconds > 0)
                {
                    throw new InvalidOperationException($"验证错误");
                }
                // ä¸ºæ¯ä¸ª Job åˆ›å»ºç‹¬ç«‹çš„ DI ä½œç”¨åŸŸï¼Œç¡®ä¿ Scoped æœåŠ¡æ­£ç¡®é‡Šæ”¾
                IServiceScope serviceScope = _serviceProvider.CreateScope();
                IJob? job = serviceScope.ServiceProvider.GetService(bundle.JobDetail.JobType) as IJob;
                if (job == null)
                {
                    // Job è§£æžå¤±è´¥æ—¶ç«‹å³é‡Šæ”¾ä½œç”¨åŸŸï¼Œé¿å…æ³„漏
                    serviceScope.Dispose();
                    throw new InvalidOperationException($"无法解析 Job ç±»åž‹: {bundle.JobDetail.JobType.Name}");
                }
                // ä¿å­˜ scope å¼•用,以便 ReturnJob æ—¶é‡Šæ”¾
                _scopes[job] = serviceScope;
                return job;
            }
            catch (Exception ex)
            {
                Console.Out.WriteLine(ex.ToString());
                throw new Exception(ex.Message);
                throw;
            }
        }
        /// <summary>
        /// Job注入
        /// é‡Šæ”¾ Job å®žä¾‹åŠå…¶å…³è”çš„ DI ä½œç”¨åŸŸ
        /// </summary>
        /// <param name="job"></param>
        /// <param name="job">待释放的 Job å®žä¾‹</param>
        public void ReturnJob(IJob job)
        {
            IDisposable? disposable = job as IDisposable;
            disposable?.Dispose();
            // å…ˆé‡Šæ”¾å…³è”çš„ DI ä½œç”¨åŸŸï¼ˆåŒ…括其中所有 Scoped æœåŠ¡ï¼‰
            if (_scopes.TryRemove(job, out IServiceScope? scope))
            {
                scope.Dispose();
            }
            // å†é‡Šæ”¾ Job æœ¬èº«
            (job as IDisposable)?.Dispose();
        }
    }
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/QuartzNet/QuartzNetExtension.cs
@@ -50,7 +50,7 @@
                deviceInfos.ForEach(x =>
                {
                    if (!Storage.Devices.Exists(d => d.DeviceCode == x.DeviceCode))
                    if (!Storage.Devices.Any(d => d.DeviceCode == x.DeviceCode))
                    {
                        try
                        {
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/QuartzNet/SchedulerCenterServer.cs
@@ -86,7 +86,7 @@
            WebResponseContent result = new WebResponseContent();
            try
            {
                if (_scheduler.IsShutdown && _scheduler.IsStarted)
                if (_scheduler.IsShutdown || !_scheduler.IsStarted)
                {
                    // ä»ŽFactory中获取Scheduler实例
                    NameValueCollection collection = new NameValueCollection
@@ -110,7 +110,7 @@
                }
                else
                {
                    await _scheduler.Shutdown();
                    // è°ƒåº¦å™¨å·²åœ¨è¿è¡Œï¼Œç›´æŽ¥è¿”回提示
                    result = WebResponseContent.Instance.Error(QuartzJobInfoMessage.JobHasStart);
                    return result;
                }
@@ -135,16 +135,13 @@
                    //等待任务运行完成
                    await _scheduler.Shutdown(false);
                    await Console.Out.WriteLineAsync(QuartzJobInfoMessage.StopJobSuccess);
                    QuartzLogger.Info(QuartzJobInfoMessage.StopJobSuccess);
                    result = WebResponseContent.Instance.OK(QuartzJobInfoMessage.StopJobSuccess);
                    return result;
                }
                else
                {
                    IReadOnlyCollection<string> jobGroupNames = await _scheduler.GetJobGroupNames();
                    await _scheduler.PauseAll();
                    // è°ƒåº¦å™¨å·²åœæ­¢ï¼Œç›´æŽ¥è¿”回提示(不再对已 shutdown çš„ scheduler è°ƒç”¨ PauseAll)
                    result = WebResponseContent.Instance.Error(QuartzJobInfoMessage.JobHasStop);
                    return result;
                }
@@ -169,7 +166,7 @@
            {
                try
                {
                    if (_scheduler.IsShutdown && _scheduler.IsStarted)
                    if (_scheduler.IsShutdown || !_scheduler.IsStarted)
                    {
                        // ä»ŽFactory中获取Scheduler实例
                        NameValueCollection collection = new NameValueCollection
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/StackerCrane/Common/CommonStackerCrane.cs
@@ -20,12 +20,14 @@
using HslCommunication;
using System.ComponentModel;
using System.Reflection;
using System.Threading;
using WIDESEAWCS_Communicator;
using WIDESEAWCS_QuartzJob.DeviceBase;
using WIDESEAWCS_QuartzJob.DTO;
using WIDESEAWCS_QuartzJob.StackerCrane;
using WIDESEAWCS_QuartzJob.StackerCrane.Common;
using WIDESEAWCS_QuartzJob.StackerCrane.Enum;
using WIDESEAWCS_Core.LogHelper;
namespace WIDESEAWCS_QuartzJob
{
@@ -67,7 +69,10 @@
        /// </summary>
        private int _lastTaskNum;
        private bool _isChecked = false;
        /// <summary>
        /// æ ‡è®°æ˜¯å¦æ­£åœ¨æ£€æŸ¥ä»»åŠ¡å®ŒæˆçŠ¶æ€ï¼ˆvolatile ä¿è¯å¤šçº¿ç¨‹å¯è§æ€§ï¼‰
        /// </summary>
        private volatile bool _isChecked = false;
        private bool _heartStatr = true;
@@ -399,30 +404,33 @@
                        {
                            OperateResult<TimeSpan> operateResult = new OperateResult<TimeSpan>();
                            TypeCode typeCode = SiemensDBDataType.GetTypeCode(devicePro.DeviceDataType);
                            // è¶…时从 10*6000ms (60s) é™ä½Žåˆ° 10*1000ms (10s),防止断连时长时间阻塞
                            int timeout = 10 * 1000;
                            switch (typeCode)
                            {
                                case TypeCode.Boolean:
                                    operateResult = Communicator.Wait(devicePro.DeviceProAddress, 500, 10 * 6000, Convert.ToBoolean(deviceProtocolDetail.ProtocalDetailValue));
                                    operateResult = Communicator.Wait(devicePro.DeviceProAddress, 500, timeout, Convert.ToBoolean(deviceProtocolDetail.ProtocalDetailValue));
                                    break;
                                case TypeCode.Byte:
                                    operateResult = Communicator.Wait(devicePro.DeviceProAddress, 500, 10 * 6000, Convert.ToByte(deviceProtocolDetail.ProtocalDetailValue));
                                    operateResult = Communicator.Wait(devicePro.DeviceProAddress, 500, timeout, Convert.ToByte(deviceProtocolDetail.ProtocalDetailValue));
                                    break;
                                case TypeCode.Int16:
                                    operateResult = Communicator.Wait(devicePro.DeviceProAddress, 500, 10 * 6000, Convert.ToInt16(deviceProtocolDetail.ProtocalDetailValue));
                                    operateResult = Communicator.Wait(devicePro.DeviceProAddress, 500, timeout, Convert.ToInt16(deviceProtocolDetail.ProtocalDetailValue));
                                    break;
                                case TypeCode.Int32:
                                    operateResult = Communicator.Wait(devicePro.DeviceProAddress, 500, 10 * 6000, Convert.ToInt32(deviceProtocolDetail.ProtocalDetailValue));
                                    operateResult = Communicator.Wait(devicePro.DeviceProAddress, 500, timeout, Convert.ToInt32(deviceProtocolDetail.ProtocalDetailValue));
                                    break;
                                case TypeCode.UInt16:
                                    operateResult = Communicator.Wait(devicePro.DeviceProAddress, 500, 10 * 6000, Convert.ToUInt16(deviceProtocolDetail.ProtocalDetailValue));
                                    operateResult = Communicator.Wait(devicePro.DeviceProAddress, 500, timeout, Convert.ToUInt16(deviceProtocolDetail.ProtocalDetailValue));
                                    break;
                                case TypeCode.UInt32:
                                    operateResult = Communicator.Wait(devicePro.DeviceProAddress, 500, 10 * 6000, Convert.ToUInt32(deviceProtocolDetail.ProtocalDetailValue));
                                    operateResult = Communicator.Wait(devicePro.DeviceProAddress, 500, timeout, Convert.ToUInt32(deviceProtocolDetail.ProtocalDetailValue));
                                    break;
                                default:
@@ -440,6 +448,8 @@
                }
                catch (Exception ex)
                {
                    // è®°å½•异常,避免错误被静默吞掉
                    QuartzLogger.Error($"CheckStackerCraneTaskCompleted: è®¾å¤‡ {_deviceCode} æ£€æŸ¥å¼‚常", "CommonStackerCrane", ex);
                }
                finally
                {
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/Storage.cs
@@ -3,6 +3,7 @@
using Quartz;
using SqlSugar;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
@@ -19,14 +20,14 @@
namespace WIDESEAWCS_QuartzJob
{
    /// <summary>
    /// é™æ€å˜è„¸å­˜å‚¨åŒºï¼Œå¯ä½¿ç”¨é™æ€å˜é‡ï¼Œä¹Ÿå¯æ³¨å…¥ä½¿ç”¨
    /// é™æ€å˜é‡å­˜å‚¨åŒºï¼Œå¯ä½¿ç”¨é™æ€å˜é‡ï¼Œä¹Ÿå¯æ³¨å…¥ä½¿ç”¨
    /// </summary>
    public class Storage
    {
        /// <summary>
        /// å·²è¿žæŽ¥è®¾å¤‡å¯¹è±¡é›†åˆ
        /// å·²è¿žæŽ¥è®¾å¤‡å¯¹è±¡é›†åˆï¼ˆçº¿ç¨‹å®‰å…¨ï¼‰
        /// </summary>
        public static List<IDevice> Devices = new List<IDevice>();
        public static ConcurrentBag<IDevice> Devices = new ConcurrentBag<IDevice>();
        /// <summary>
        /// è®¾å¤‡å¯¹è±¡
@@ -44,8 +45,8 @@
        /// <summary>
        /// èŽ·å–è®¾å¤‡
        /// </summary>
        /// <param name="deviceCode"></param>
        /// <returns></returns>
        /// <param name="deviceCode">设备编码</param>
        /// <returns>设备实例,未找到返回 null</returns>
        public IDevice? GetDevice(string deviceCode)
        {
            return Pro_Devices.FirstOrDefault(x => x.DeviceCode == deviceCode);
@@ -54,8 +55,8 @@
        /// <summary>
        /// èŽ·å–è®¾å¤‡
        /// </summary>
        /// <param name="deviceCodes"></param>
        /// <returns></returns>
        /// <param name="deviceCodes">设备编码列表</param>
        /// <returns>匹配的设备列表</returns>
        public List<IDevice> GetDevices(List<string> deviceCodes)
        {
            return Pro_Devices.Where(x => deviceCodes.Contains(x.DeviceCode)).ToList();
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/appsettings.json
@@ -18,7 +18,8 @@
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
      "Microsoft.AspNetCore": "Warning",
      "Quartz": "Debug"
    }
  },
  "dics": "deviceType,devicePlcType,jobAssembly,jobClassName,deviceStatus,taskType,taskState,inOutType,dispatchId",
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineJob/CommonConveyorLineJob.cs
@@ -28,6 +28,7 @@
using WIDESEAWCS_Communicator;
using WIDESEAWCS_Core;
using WIDESEAWCS_Core.Helper;
using WIDESEAWCS_Core.LogHelper;
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
using WIDESEAWCS_QuartzJob;
@@ -87,6 +88,8 @@
            }
            catch (Exception ex)
            {
                // è®°å½•异常,避免错误被静默吞掉
                QuartzLogger.Error("CommonConveyorLineJob Execute å¼‚常", "CommonConveyorLineJob", ex);
            }
            return Task.CompletedTask;
        }
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/CommonStackerCraneJob.cs
@@ -111,13 +111,13 @@
            _logger = logger;
            // åŠ è½½é…ç½®æ–‡ä»¶
            _config = LoadConfig();
            //_config = LoadConfig();
            // åˆå§‹åŒ–任务选择器
            _taskSelector = new StackerCraneTaskSelector(taskService, routerService, httpClientHelper, _logger);
            // åˆå§‹åŒ–命令构建器
            _commandBuilder = new StackerCraneCommandBuilder(taskService, routerService, _config, _logger);
            _commandBuilder = new StackerCraneCommandBuilder(taskService, routerService, _logger);
        }
        /// <summary>
@@ -222,7 +222,7 @@
                bool sendFlag = SendStackerCraneCommand(commonStackerCrane, stackerCraneTaskCommand);
                if (sendFlag)
                {
                    Task.Delay(1000).Wait();
                    Thread.Sleep(1000);
                    commonStackerCrane.SetValue(StackerCraneDBName.WorkAction, (short)StackerCraneWorkActionEnum.StartTask);
                    // å‘送成功,更新状态
                    commonStackerCrane.LastTaskType = task.TaskType;
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneCommandBuilder.cs
@@ -37,11 +37,6 @@
        private readonly IRouterService _routerService;
        /// <summary>
        /// å †åž›æœºå‘½ä»¤é…ç½®
        /// </summary>
        private readonly StackerCraneCommandConfig _config;
        /// <summary>
        /// æ—¥å¿—记录器
        /// </summary>
        private readonly ILogger _logger;
@@ -56,12 +51,10 @@
        public StackerCraneCommandBuilder(
            ITaskService taskService,
            IRouterService routerService,
            StackerCraneCommandConfig config,
            ILogger logger)
        {
            _taskService = taskService;
            _routerService = routerService;
            _config = config;
            _logger = logger;
        }
@@ -99,20 +92,20 @@
        /// </remarks>
        /// <param name="roadway">巷道编码</param>
        /// <returns>命令类型(Standard æˆ– Formation)</returns>
        private string GetCommandType(string roadway)
        {
            foreach (var mapping in _config.RoadwayCommandMapping)
            {
                if (roadway.Contains(mapping.Key))
                {
                    QuartzLogHelper.LogDebug(_logger, "GetCommandType:匹配巷道 {Roadway},命令类型: {CommandType}", $"GetCommandType:匹配巷道 {roadway},命令类型: {mapping.Value}", roadway, roadway, mapping.Value);
                    return mapping.Value;
                }
            }
        //private string GetCommandType(string roadway)
        //{
        //    foreach (var mapping in _config.RoadwayCommandMapping)
        //    {
        //        if (roadway.Contains(mapping.Key))
        //        {
        //            QuartzLogHelper.LogDebug(_logger, "GetCommandType:匹配巷道 {Roadway},命令类型: {CommandType}", $"GetCommandType:匹配巷道 {roadway},命令类型: {mapping.Value}", roadway, roadway, mapping.Value);
        //            return mapping.Value;
        //        }
        //    }
            QuartzLogHelper.LogDebug(_logger, "GetCommandType:巷道 {Roadway} æœªåŒ¹é…ï¼Œä½¿ç”¨é»˜è®¤å‘½ä»¤ç±»åž‹: {DefaultType}", $"GetCommandType:巷道 {roadway} æœªåŒ¹é…ï¼Œä½¿ç”¨é»˜è®¤å‘½ä»¤ç±»åž‹: {_config.DefaultCommandType}", roadway, roadway, _config.DefaultCommandType);
            return _config.DefaultCommandType;
        }
        //    QuartzLogHelper.LogDebug(_logger, "GetCommandType:巷道 {Roadway} æœªåŒ¹é…ï¼Œä½¿ç”¨é»˜è®¤å‘½ä»¤ç±»åž‹: {DefaultType}", $"GetCommandType:巷道 {roadway} æœªåŒ¹é…ï¼Œä½¿ç”¨é»˜è®¤å‘½ä»¤ç±»åž‹: {_config.DefaultCommandType}", roadway, roadway, _config.DefaultCommandType);
        //    return _config.DefaultCommandType;
        //}
        /// <summary>
        /// åˆ›å»ºæ ‡å‡†å‘½ä»¤
Code/WMS/WIDESEA_WMSServer/WIDESEA_IBasicService/IMesUploadHelper.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
using WIDESEA_Common.StockEnum;
using WIDESEA_Core;
namespace WIDESEA_IBasicService
{
    /// <summary>
    /// MES异步上传辅助服务 - å°è£…Task.Run + çŠ¶æ€æ›´æ–° + æ—¥å¿—记录的统一模式
    /// </summary>
    public interface IMesUploadHelper : IDependency
    {
        /// <summary>
        /// ä»¥fire-and-forget方式异步执行MES调用,自动更新上传状态并记录日志
        /// </summary>
        /// <param name="palletCode">托盘号</param>
        /// <param name="successStatus">成功时的状态枚举值(奇数=成功,偶数=失败)</param>
        /// <param name="apiType">MES接口类型名称</param>
        /// <param name="requestJson">请求JSON(用于日志记录)</param>
        /// <param name="mesCall">MES调用委托,返回(是否成功, å“åº”JSON, é”™è¯¯æ¶ˆæ¯)</param>
        /// <param name="creator">操作人</param>
        void FireAndForget(
            string palletCode,
            MesUploadStatusEnum successStatus,
            string apiType,
            string requestJson,
            Func<(bool isSuccess, string responseJson, string errorMessage)> mesCall,
            string creator = "System");
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_StockService/MesUploadHelper.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,93 @@
using System.Diagnostics;
using WIDESEA_Common.StockEnum;
using WIDESEA_Core;
using WIDESEA_DTO.MES;
using WIDESEA_IStockService;
using WIDESEA_IBasicService;
namespace WIDESEA_StockService
{
    /// <summary>
    /// MES异步上传辅助服务实现
    /// </summary>
    public class MesUploadHelper : IMesUploadHelper
    {
        private readonly IStockInfoService _stockInfoService;
        private readonly IMesLogService _mesLogService;
        /// <summary>
        /// æž„造函数
        /// </summary>
        /// <param name="stockInfoService">库存信息服务</param>
        /// <param name="mesLogService">MES日志服务</param>
        public MesUploadHelper(IStockInfoService stockInfoService, IMesLogService mesLogService)
        {
            _stockInfoService = stockInfoService;
            _mesLogService = mesLogService;
        }
        /// <summary>
        /// ä»¥fire-and-forget方式异步执行MES调用,自动更新上传状态并记录日志
        /// </summary>
        public void FireAndForget(
            string palletCode,
            MesUploadStatusEnum successStatus,
            string apiType,
            string requestJson,
            Func<(bool isSuccess, string responseJson, string errorMessage)> mesCall,
            string creator = "System")
        {
            _ = Task.Run(async () =>
            {
                var stopwatch = Stopwatch.StartNew();
                try
                {
                    var (isSuccess, responseJson, errorMessage) = mesCall();
                    stopwatch.Stop();
                    // å¥‡æ•°=成功,偶数=失败
                    int status = isSuccess ? (int)successStatus : (int)successStatus + 1;
                    await _stockInfoService.UpdateMesUploadStatusAsync(palletCode, status);
                    await LogAsync(palletCode, apiType, requestJson, responseJson,
                        stopwatch.ElapsedMilliseconds, isSuccess, errorMessage, creator);
                }
                catch (Exception ex)
                {
                    stopwatch.Stop();
                    int status = (int)successStatus + 1;
                    await _stockInfoService.UpdateMesUploadStatusAsync(palletCode, status);
                    await LogAsync(palletCode, apiType, requestJson, "",
                        stopwatch.ElapsedMilliseconds, false, ex.Message, creator);
                }
            });
        }
        /// <summary>
        /// è®°å½•MES接口调用日志
        /// </summary>
        private async Task LogAsync(string palletCode, string apiType, string requestJson,
            string responseJson, long elapsedMs, bool isSuccess, string errorMessage, string creator)
        {
            try
            {
                await _mesLogService.LogAsync(new MesApiLogDto
                {
                    PalletCode = palletCode,
                    ApiType = apiType,
                    RequestJson = requestJson,
                    ResponseJson = responseJson,
                    IsSuccess = isSuccess,
                    ErrorMessage = errorMessage,
                    ElapsedMs = (int)elapsedMs,
                    Creator = creator
                });
            }
            catch
            {
                // æ—¥å¿—记录失败不影响主流程
            }
        }
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_StockService/StockInfoService.cs
@@ -391,7 +391,7 @@
        {
            try
            {
                var stockInfo = await BaseDal.QueryDataFirstAsync(x => x.PalletCode == palletCode);
                var stockInfo = await BaseDal.QueryFirstAsync(x => x.PalletCode == palletCode);
                if (stockInfo == null)
                    return false;
Code/WMS/WIDESEA_WMSServer/WIDESEA_StockService/StockSerivce.cs
@@ -1,6 +1,5 @@
using Newtonsoft.Json;
using SqlSugar;
using System.Diagnostics;
using WIDESEA_Common.Constants;
using WIDESEA_Common.StockEnum;
using WIDESEA_Core;
@@ -54,6 +53,7 @@
        public IMesService _mesService { get; }
        private readonly IMesLogService _mesLogService;
        private readonly IMesUploadHelper _mesUploadHelper;
        /// <summary>
        /// æž„造函数
@@ -62,6 +62,7 @@
        /// <param name="stockInfoService">库存信息服务</param>
        /// <param name="stockInfoDetail_HtyService">库存明细历史服务</param>
        /// <param name="stockInfo_HtyService">库存历史服务</param>
        /// <param name="mesUploadHelper">MES异步上传辅助服务</param>
        public StockService(
            IStockInfoDetailService stockInfoDetailService,
            IStockInfoService stockInfoService,
@@ -70,7 +71,8 @@
            IMesService mesService,
            IWarehouseService warehouseService,
            ISqlSugarClient sqlSugarClient,
            IMesLogService mesLogService)
            IMesLogService mesLogService,
            IMesUploadHelper mesUploadHelper)
        {
            StockInfoDetailService = stockInfoDetailService;
            StockInfoService = stockInfoService;
@@ -80,6 +82,7 @@
            _warehouseService = warehouseService;
            SqlSugarClient = sqlSugarClient;
            _mesLogService = mesLogService;
            _mesUploadHelper = mesUploadHelper;
        }
        /// <summary>
@@ -444,42 +447,24 @@
                    ContainCode = palletCode,
                    SfcList = sfcList
                };
                _ = Task.Run(() =>
                string requestJson = unbindRequest.ToJson();
                var localToken = token;
                _mesUploadHelper.FireAndForget(
                    palletCode,
                    MesUploadStatusEnum.拆盘上传成功,
                    "UnBindContainer",
                    requestJson,
                    () =>
                {
                    var stopwatch = Stopwatch.StartNew();
                    try
                    {
                        var unbindResult = string.IsNullOrWhiteSpace(token)
                        var result = string.IsNullOrWhiteSpace(localToken)
                            ? _mesService.UnBindContainer(unbindRequest)
                            : _mesService.UnBindContainer(unbindRequest, token);
                        stopwatch.Stop();
                        bool isSuccess = unbindResult?.Data?.IsSuccess ?? false;
                        int status = isSuccess
                            ? (int)MesUploadStatusEnum.拆盘上传成功
                            : (int)MesUploadStatusEnum.拆盘上传失败;
                        // æ›´æ–°MES上传状态
                        StockInfoService.UpdateMesUploadStatusAsync(palletCode, status).ConfigureAwait(false);
                        // è®°å½•MES日志
                        _mesLogService.LogAsync(new MesApiLogDto
                        {
                            PalletCode = palletCode,
                            ApiType = "UnBindContainer",
                            RequestJson = unbindRequest.ToJson(),
                            ResponseJson = System.Text.Json.JsonSerializer.Serialize(unbindResult),
                            IsSuccess = isSuccess,
                            ErrorMessage = unbindResult?.Data?.Msg ?? unbindResult?.ErrorMessage ?? "未知错误",
                            ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
                            Creator = "System"
                        }).ConfigureAwait(false);
                    }
                    catch (Exception ex)
                    {
                        // è°ƒç”¨å¤±è´¥
                        StockInfoService.UpdateMesUploadStatusAsync(palletCode, (int)MesUploadStatusEnum.拆盘上传失败).ConfigureAwait(false);
                    }
                            : _mesService.UnBindContainer(unbindRequest, localToken);
                        return (
                            result?.Data?.IsSuccess ?? false,
                            System.Text.Json.JsonSerializer.Serialize(result),
                            result?.Data?.Msg ?? result?.ErrorMessage ?? "未知错误"
                        );
                });
                // 4. åˆ é™¤ä¸´æ—¶è¡¨è®°å½•
@@ -502,7 +487,6 @@
        public async Task<WebResponseContent> GroupPalletConfirmAsync(string palletCode, string deviceName)
        {
            WebResponseContent content = new WebResponseContent();
            var stopwatch = Stopwatch.StartNew();
            try
            {
                if (string.IsNullOrWhiteSpace(palletCode))
@@ -548,43 +532,24 @@
                    }).ToList()
                };
                string requestJson = bindRequest.ToJson();
                var localToken = token;
                // 3. Fire-and-forget异步调用MES绑定
                _ = Task.Run(() =>
                _mesUploadHelper.FireAndForget(
                    palletCode,
                    MesUploadStatusEnum.组盘上传成功,
                    "BindContainer",
                    requestJson,
                    () =>
                {
                    var stopwatch = Stopwatch.StartNew();
                    try
                    {
                        var bindResult = string.IsNullOrWhiteSpace(token)
                        var result = string.IsNullOrWhiteSpace(localToken)
                            ? _mesService.BindContainer(bindRequest)
                            : _mesService.BindContainer(bindRequest, token);
                        stopwatch.Stop();
                        bool isSuccess = bindResult?.Data?.IsSuccess ?? false;
                        int status = isSuccess
                            ? (int)MesUploadStatusEnum.组盘上传成功
                            : (int)MesUploadStatusEnum.组盘上传失败;
                        // æ›´æ–°MES上传状态
                        StockInfoService.UpdateMesUploadStatusAsync(palletCode, status).ConfigureAwait(false);
                        // è®°å½•MES日志
                        _mesLogService.LogAsync(new MesApiLogDto
                        {
                            PalletCode = palletCode,
                            ApiType = "BindContainer",
                            RequestJson = requestJson,
                            ResponseJson = System.Text.Json.JsonSerializer.Serialize(bindResult),
                            IsSuccess = isSuccess,
                            ErrorMessage = bindResult?.ErrorMessage ?? "未知错误",
                            ElapsedMs = (int)stopwatch.ElapsedMilliseconds,
                            Creator = "System"
                        }).ConfigureAwait(false);
                    }
                    catch (Exception ex)
                    {
                        // è°ƒç”¨å¤±è´¥
                        StockInfoService.UpdateMesUploadStatusAsync(palletCode, (int)MesUploadStatusEnum.组盘上传失败).ConfigureAwait(false);
                    }
                            : _mesService.BindContainer(bindRequest, localToken);
                        return (
                            result?.Data?.IsSuccess ?? false,
                            System.Text.Json.JsonSerializer.Serialize(result),
                            result?.Data?.Msg ?? result?.ErrorMessage ?? "未知错误"
                        );
                });
                return content.OK("批量组盘确认成功");
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs
@@ -17,10 +17,7 @@
using WIDESEA_Core.Helper;
using WIDESEA_DTO.GradingMachine;
using WIDESEA_DTO.MES;
using WIDESEA_DTO.Stock;
using WIDESEA_DTO.Task;
using Newtonsoft.Json;
using System.Diagnostics;
using WIDESEA_IBasicService;
using WIDESEA_IRecordService;
using WIDESEA_IStockService;
@@ -44,6 +41,7 @@
        private readonly IRecordService _recordService;
        private readonly IMESDeviceConfigService _mesDeviceConfigService;
        private readonly IMesLogService _mesLogService;
        private readonly IMesUploadHelper _mesUploadHelper;
        public IRepository<Dt_Task> Repository => BaseDal;
@@ -70,7 +68,8 @@
            IUnitOfWorkManage unitOfWorkManage,
            IRecordService recordService,
            IMESDeviceConfigService mesDeviceConfigService,
            IMesLogService mesLogService) : base(BaseDal)
            IMesLogService mesLogService,
            IMesUploadHelper mesUploadHelper) : base(BaseDal)
        {
            _mapper = mapper;
            _stockInfoService = stockInfoService;
@@ -85,6 +84,7 @@
            _recordService = recordService;
            _mesDeviceConfigService = mesDeviceConfigService;
            _mesLogService = mesLogService;
            _mesUploadHelper = mesUploadHelper;
        }
        /// <summary>
@@ -200,80 +200,5 @@
            return DetermineTargetAddress(roadway, addressMap);
        }
        /// <summary>
        /// å¼‚步执行MES上传 - ä¸é˜»å¡žä¸»ä¸šåŠ¡é€»è¾‘
        /// </summary>
        /// <param name="palletCode">托盘号</param>
        /// <param name="successStatus">成功时的状态枚举值(奇数)</param>
        /// <param name="uploadFunc">具体的MES调用函数</param>
        private async Task MesUploadAsync(string palletCode, MesUploadStatusEnum successStatus, Func<Task<HttpResponseResult<MesResponse>>> uploadFunc)
        {
            var stopwatch = Stopwatch.StartNew();
            string requestJson = "";
            string responseJson = "";
            bool isSuccess = false;
            string errorMessage = "";
            try
            {
                // è°ƒç”¨MES
                var result = await uploadFunc();
                stopwatch.Stop();
                isSuccess = result?.Data?.IsSuccess ?? false;
                errorMessage = result?.Data?.Msg ?? result?.ErrorMessage ?? "未知错误";
                responseJson = JsonConvert.SerializeObject(result);
                // æ ¹æ®æˆåŠŸ/失败决定状态值:奇数=成功,偶数=失败
                var uploadStatus = isSuccess ? (int)successStatus : (int)successStatus + 1;
                // æ›´æ–°åº“存表状态(不等待)
                _ = _stockInfoService.UpdateMesUploadStatusAsync(palletCode, uploadStatus);
                // è®°å½•MES日志
                await LogMesCallAsync(palletCode, successStatus.ToString(), requestJson, responseJson,
                    stopwatch.ElapsedMilliseconds, isSuccess ? "成功" : "失败", errorMessage);
            }
            catch (Exception ex)
            {
                stopwatch.Stop();
                errorMessage = ex.Message;
                // æ›´æ–°çŠ¶æ€ä¸ºå¤±è´¥ï¼ˆsuccessStatus+1 å³ä¸ºå¤±è´¥çŠ¶æ€ï¼‰
                var uploadStatus = (int)successStatus + 1;
                _ = _stockInfoService.UpdateMesUploadStatusAsync(palletCode, uploadStatus);
                // è®°å½•异常日志
                await LogMesCallAsync(palletCode, successStatus.ToString(), requestJson, responseJson,
                    stopwatch.ElapsedMilliseconds, "失败", errorMessage);
            }
        }
        /// <summary>
        /// è®°å½•MES接口调用日志
        /// </summary>
        private async Task LogMesCallAsync(string palletCode, string apiType, string requestJson,
            string responseJson, long durationMs, string status, string errorMessage)
        {
            try
            {
                var mesLog = new MesApiLogDto
                {
                    PalletCode = palletCode,
                    ApiType = apiType,
                    RequestJson = requestJson,
                    ResponseJson = responseJson,
                    IsSuccess = status == "成功",
                    ErrorMessage = errorMessage,
                    ElapsedMs = (int)durationMs,
                    Creator = "System"
                };
                await _mesLogService.LogAsync(mesLog);
            }
            catch
            {
                // æ—¥å¿—记录失败不影响主流程
            }
        }
    }
}
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Inbound.cs
@@ -1,6 +1,4 @@
using Microsoft.AspNetCore.Http.HttpResults;
using Newtonsoft.Json;
using System.Diagnostics;
using WIDESEA_Common.Constants;
using WIDESEA_Common.LocationEnum;
using WIDESEA_Common.StockEnum;
@@ -122,7 +120,6 @@
        /// </summary>
        public async Task<WebResponseContent> InboundFinishTaskAsync(CreateTaskDto taskDto)
        {
            var stopwatch = Stopwatch.StartNew();
            try
            {
                var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode);
@@ -153,8 +150,6 @@
                }
                else
                {
                    // åˆ¤æ–­æ˜¯ä¸æ˜¯æžå·åº“任务
                    if (taskDto.WarehouseId == (int)WarehouseEnum.FJ1 || taskDto.WarehouseId == (int)WarehouseEnum.ZJ1)
                    {
@@ -188,62 +183,33 @@
                        string token = mesConfig?.Token;
                        // å¼‚步调用MES托盘进站,不阻塞主逻辑
                        var palletCode = taskDto.PalletCode;
                        var localEquipmentCode = equipmentCode;
                        var localResourceCode = resourceCode;
                        var localToken = token;
                        _ = Task.Run(async () =>
                        {
                            var localStopwatch = Stopwatch.StartNew();
                            try
                            {
                                var inboundRequest = new InboundInContainerRequest
                                {
                                    EquipmentCode = localEquipmentCode,
                                    ResourceCode = localResourceCode,
                            EquipmentCode = equipmentCode,
                            ResourceCode = resourceCode,
                                    LocalTime = DateTime.Now,
                                    ContainerCode = palletCode
                            ContainerCode = taskDto.PalletCode
                                };
                                string localRequestJson = inboundRequest.ToJson();
                                var inboundResult = string.IsNullOrWhiteSpace(localToken)
                        string requestJson = inboundRequest.ToJson();
                        var palletCode = taskDto.PalletCode;
                        _mesUploadHelper.FireAndForget(
                            palletCode,
                            MesUploadStatusEnum.进站上传成功,
                            "InboundInContainer",
                            requestJson,
                            () =>
                            {
                                var result = string.IsNullOrWhiteSpace(token)
                                    ? _mesService.InboundInContainer(inboundRequest)
                                    : _mesService.InboundInContainer(inboundRequest, localToken);
                                localStopwatch.Stop();
                                bool isSuccess = inboundResult?.Data?.IsSuccess ?? false;
                                int status = isSuccess
                                    ? (int)MesUploadStatusEnum.进站上传成功
                                    : (int)MesUploadStatusEnum.进站上传失败;
                                await _stockInfoService.UpdateMesUploadStatusAsync(palletCode, status);
                                await _mesLogService.LogAsync(new MesApiLogDto
                                {
                                    PalletCode = palletCode,
                                    ApiType = "InboundInContainer",
                                    RequestJson = localRequestJson,
                                    ResponseJson = JsonConvert.SerializeObject(inboundResult),
                                    IsSuccess = isSuccess,
                                    ErrorMessage = inboundResult?.Data?.Msg ?? inboundResult?.ErrorMessage ?? "未知错误",
                                    ElapsedMs = (int)localStopwatch.ElapsedMilliseconds,
                                    Creator = "systeam"
                                    : _mesService.InboundInContainer(inboundRequest, token);
                                return (
                                    result?.Data?.IsSuccess ?? false,
                                    JsonConvert.SerializeObject(result),
                                    result?.Data?.Msg ?? result?.ErrorMessage ?? "未知错误"
                                );
                                });
                            }
                            catch (Exception ex)
                            {
                                localStopwatch.Stop();
                                await _stockInfoService.UpdateMesUploadStatusAsync(palletCode, (int)MesUploadStatusEnum.进站上传失败);
                                await _mesLogService.LogAsync(new MesApiLogDto
                                {
                                    PalletCode = palletCode,
                                    ApiType = "InboundInContainer",
                                    IsSuccess = false,
                                    ErrorMessage = ex.Message,
                                    ElapsedMs = (int)localStopwatch.ElapsedMilliseconds,
                                    Creator = "systeam"
                                });
                            }
                        });
                        return await CompleteTaskAsync(task, "入库完成");
                    });
                }
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Outbound.cs
@@ -1,4 +1,3 @@
using System.Diagnostics;
using WIDESEA_Common.Constants;
using WIDESEA_Common.LocationEnum;
using WIDESEA_Common.StockEnum;
@@ -60,7 +59,6 @@
        /// </summary>
        public async Task<WebResponseContent> OutboundFinishTaskAsync(CreateTaskDto taskDto)
        {
            var stopwatch = Stopwatch.StartNew();
            try
            {
                var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode);
@@ -139,14 +137,25 @@
                        ContainerCode = taskDto.PalletCode
                    };
                    string palletCode = taskDto.PalletCode;
                    string requestJson = outboundRequest.ToJson();
                    // Fire-and-forget: å¼‚步执行MES出站,不阻塞主业务逻辑
                    _ = Task.Run(() => MesUploadAsync(palletCode, MesUploadStatusEnum.出站上传成功, async () =>
                    _mesUploadHelper.FireAndForget(
                        palletCode,
                        MesUploadStatusEnum.出站上传成功,
                        "OutboundInContainer",
                        requestJson,
                        () =>
                    {
                        return await Task.FromResult(string.IsNullOrWhiteSpace(token)
                            var result = string.IsNullOrWhiteSpace(token)
                            ? _mesService.OutboundInContainer(outboundRequest)
                            : _mesService.OutboundInContainer(outboundRequest, token));
                    }));
                                : _mesService.OutboundInContainer(outboundRequest, token);
                            return (
                                result?.Data?.IsSuccess ?? false,
                                Newtonsoft.Json.JsonConvert.SerializeObject(result),
                                result?.Data?.Msg ?? result?.ErrorMessage ?? "未知错误"
                            );
                        });
                    var completeResult = await CompleteTaskAsync(task, "出库完成");
                    if (!completeResult.Status)
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockInfoController.cs
@@ -11,7 +11,6 @@
using WIDESEA_Model.Models;
using WIDESEA_Common.Constants;
using WIDESEA_Common.StockEnum;
using System.Diagnostics;
namespace WIDESEA_WMSServer.Controllers.Stock
{
@@ -22,25 +21,22 @@
    [ApiController]
    public class StockInfoController : ApiBaseController<IStockInfoService, Dt_StockInfo>
    {
        private readonly IMesLogService _mesLogService;
        private readonly IMesService _mesService;
        private readonly IMESDeviceConfigService _mesDeviceConfigService;
        private readonly ISys_DictionaryService _sysDictionaryService;
        private readonly IStockInfoService _stockInfoService;
        private readonly IMesUploadHelper _mesUploadHelper;
        public StockInfoController(
            IStockInfoService service,
            IMesLogService mesLogService,
            IMesService mesService,
            IMESDeviceConfigService mesDeviceConfigService,
            ISys_DictionaryService sysDictionaryService,
            IStockInfoService stockInfoService) : base(service)
            IMesUploadHelper mesUploadHelper) : base(service)
        {
            _mesLogService = mesLogService;
            _mesService = mesService;
            _mesDeviceConfigService = mesDeviceConfigService;
            _sysDictionaryService = sysDictionaryService;
            _stockInfoService = stockInfoService;
            _mesUploadHelper = mesUploadHelper;
        }
        /// <summary>
@@ -106,50 +102,23 @@
                string palletCode = stockInfo.PalletCode;
                // 5. å¼‚步执行MES调用(fire-and-forget)
                _ = Task.Run(async () =>
                {
                    var localStopwatch = Stopwatch.StartNew();
                    try
                _mesUploadHelper.FireAndForget(
                    palletCode,
                    MesUploadStatusEnum.进站上传成功,
                    "InboundInContainer",
                    requestJson,
                    () =>
                    {
                        var result = string.IsNullOrWhiteSpace(token)
                            ? _mesService.InboundInContainer(mesRequest)
                            : _mesService.InboundInContainer(mesRequest, token);
                        localStopwatch.Stop();
                        bool isSuccess = result?.IsSuccess ?? false;
                        int status = isSuccess
                            ? (int)MesUploadStatusEnum.进站上传成功
                            : (int)MesUploadStatusEnum.进站上传失败;
                        await _stockInfoService.UpdateMesUploadStatusAsync(palletCode, status);
                        await _mesLogService.LogAsync(new MesApiLogDto
                        {
                            PalletCode = palletCode,
                            ApiType = "InboundInContainer",
                            RequestJson = requestJson,
                            ResponseJson = System.Text.Json.JsonSerializer.Serialize(result),
                            IsSuccess = isSuccess,
                            ErrorMessage = result?.ErrorMessage ?? "未知错误",
                            ElapsedMs = (int)localStopwatch.ElapsedMilliseconds,
                            Creator = App.User.UserName
                        });
                    }
                    catch (Exception ex)
                    {
                        localStopwatch.Stop();
                        await _stockInfoService.UpdateMesUploadStatusAsync(palletCode, (int)MesUploadStatusEnum.进站上传失败);
                        await _mesLogService.LogAsync(new MesApiLogDto
                        {
                            PalletCode = palletCode,
                            ApiType = "InboundInContainer",
                            IsSuccess = false,
                            ErrorMessage = ex.Message,
                            ElapsedMs = (int)localStopwatch.ElapsedMilliseconds,
                            Creator = App.User.UserName
                        });
                    }
                });
                        return (
                            result?.IsSuccess ?? false,
                            System.Text.Json.JsonSerializer.Serialize(result),
                            result?.ErrorMessage ?? "未知错误"
                        );
                    },
                    App.User.UserName);
                // 6. ç«‹å³è¿”回成功
                return response.OK("托盘进站成功");
@@ -223,50 +192,23 @@
                string palletCode = stockInfo.PalletCode;
                // 5. å¼‚步执行MES调用(fire-and-forget)
                _ = Task.Run(async () =>
                {
                    var localStopwatch = Stopwatch.StartNew();
                    try
                _mesUploadHelper.FireAndForget(
                    palletCode,
                    MesUploadStatusEnum.出站上传成功,
                    "OutboundInContainer",
                    requestJson,
                    () =>
                    {
                        var result = string.IsNullOrWhiteSpace(token)
                            ? _mesService.OutboundInContainer(mesRequest)
                            : _mesService.OutboundInContainer(mesRequest, token);
                        localStopwatch.Stop();
                        bool isSuccess = result?.IsSuccess ?? false;
                        int status = isSuccess
                            ? (int)MesUploadStatusEnum.出站上传成功
                            : (int)MesUploadStatusEnum.出站上传失败;
                        await _stockInfoService.UpdateMesUploadStatusAsync(palletCode, status);
                        await _mesLogService.LogAsync(new MesApiLogDto
                        {
                            PalletCode = palletCode,
                            ApiType = "OutboundInContainer",
                            RequestJson = requestJson,
                            ResponseJson = System.Text.Json.JsonSerializer.Serialize(result),
                            IsSuccess = isSuccess,
                            ErrorMessage = result?.ErrorMessage ?? "未知错误",
                            ElapsedMs = (int)localStopwatch.ElapsedMilliseconds,
                            Creator = App.User.UserName
                        });
                    }
                    catch (Exception ex)
                    {
                        localStopwatch.Stop();
                        await _stockInfoService.UpdateMesUploadStatusAsync(palletCode, (int)MesUploadStatusEnum.出站上传失败);
                        await _mesLogService.LogAsync(new MesApiLogDto
                        {
                            PalletCode = palletCode,
                            ApiType = "OutboundInContainer",
                            IsSuccess = false,
                            ErrorMessage = ex.Message,
                            ElapsedMs = (int)localStopwatch.ElapsedMilliseconds,
                            Creator = App.User.UserName
                        });
                    }
                });
                        return (
                            result?.IsSuccess ?? false,
                            System.Text.Json.JsonSerializer.Serialize(result),
                            result?.ErrorMessage ?? "未知错误"
                        );
                    },
                    App.User.UserName);
                // 6. ç«‹å³è¿”回成功
                return response.OK("托盘出站成功");
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockInfoDetailController.cs
@@ -9,7 +9,6 @@
using WIDESEA_Model.Models;
using WIDESEA_Common.Constants;
using WIDESEA_Common.StockEnum;
using System.Diagnostics;
namespace WIDESEA_WMSServer.Controllers.Stock
{
@@ -20,25 +19,25 @@
    [ApiController]
    public class StockInfoDetailController : ApiBaseController<IStockInfoDetailService, Dt_StockInfoDetail>
    {
        private readonly IMesLogService _mesLogService;
        private readonly IMesService _mesService;
        private readonly ISys_DictionaryService _sysDictionaryService;
        private readonly IStockInfoService _stockInfoService;
        private readonly IMESDeviceConfigService _mesDeviceConfigService;
        private readonly IMesUploadHelper _mesUploadHelper;
        public StockInfoDetailController(
            IStockInfoDetailService service,
            IMesLogService mesLogService,
            IMesService mesService,
            ISys_DictionaryService sysDictionaryService,
            IStockInfoService stockInfoService,
            IMESDeviceConfigService mesDeviceConfigService) : base(service)
            IMESDeviceConfigService mesDeviceConfigService,
            IMesUploadHelper mesUploadHelper) : base(service)
        {
            _mesLogService = mesLogService;
            _mesService = mesService;
            _sysDictionaryService = sysDictionaryService;
            _stockInfoService = stockInfoService;
            _mesDeviceConfigService = mesDeviceConfigService;
            _mesUploadHelper = mesUploadHelper;
        }
        /// <summary>
@@ -91,51 +90,23 @@
                string requestJson = System.Text.Json.JsonSerializer.Serialize(mesRequest);
                // 5. å¼‚步调用MES接口(fire-and-forget)
                var localToken = token;
                _ = Task.Run(async () =>
                _mesUploadHelper.FireAndForget(
                    stockInfo.PalletCode,
                    MesUploadStatusEnum.组盘上传成功,
                    "BindContainer",
                    requestJson,
                    () =>
                {
                    var localStopwatch = Stopwatch.StartNew();
                    try
                    {
                        var result = string.IsNullOrWhiteSpace(localToken)
                        var result = string.IsNullOrWhiteSpace(token)
                            ? _mesService.BindContainer(mesRequest)
                            : _mesService.BindContainer(mesRequest, localToken);
                        localStopwatch.Stop();
                        bool isSuccess = result?.IsSuccess ?? false;
                        int status = isSuccess
                            ? (int)MesUploadStatusEnum.组盘上传成功
                            : (int)MesUploadStatusEnum.组盘上传失败;
                        await _stockInfoService.UpdateMesUploadStatusAsync(stockInfo.PalletCode, status);
                        await _mesLogService.LogAsync(new MesApiLogDto
                        {
                            PalletCode = stockInfo.PalletCode,
                            ApiType = "BindContainer",
                            RequestJson = requestJson,
                            ResponseJson = System.Text.Json.JsonSerializer.Serialize(result),
                            IsSuccess = isSuccess,
                            ErrorMessage = result?.ErrorMessage ?? "未知错误",
                            ElapsedMs = (int)localStopwatch.ElapsedMilliseconds,
                            Creator = App.User.UserName
                        });
                    }
                    catch (Exception ex)
                    {
                        localStopwatch.Stop();
                        await _stockInfoService.UpdateMesUploadStatusAsync(stockInfo.PalletCode, (int)MesUploadStatusEnum.组盘上传失败);
                        await _mesLogService.LogAsync(new MesApiLogDto
                        {
                            PalletCode = stockInfo.PalletCode,
                            ApiType = "BindContainer",
                            IsSuccess = false,
                            ErrorMessage = ex.Message,
                            ElapsedMs = (int)localStopwatch.ElapsedMilliseconds,
                            Creator = App.User.UserName
                        });
                    }
                });
                            : _mesService.BindContainer(mesRequest, token);
                        return (
                            result?.IsSuccess ?? false,
                            System.Text.Json.JsonSerializer.Serialize(result),
                            result?.ErrorMessage ?? "未知错误"
                        );
                    },
                    App.User.UserName);
                // 6. ç«‹å³è¿”回成功响应
                return response.OK("托盘电芯绑定成功");
@@ -191,51 +162,23 @@
                string requestJson = System.Text.Json.JsonSerializer.Serialize(mesRequest);
                // 5. å¼‚步调用MES接口(fire-and-forget)
                var localToken = token;
                _ = Task.Run(async () =>
                _mesUploadHelper.FireAndForget(
                    stockInfo.PalletCode,
                    MesUploadStatusEnum.拆盘上传成功,
                    "UnbindContainer",
                    requestJson,
                    () =>
                {
                    var localStopwatch = Stopwatch.StartNew();
                    try
                    {
                        var result = string.IsNullOrWhiteSpace(localToken)
                        var result = string.IsNullOrWhiteSpace(token)
                            ? _mesService.UnBindContainer(mesRequest)
                            : _mesService.UnBindContainer(mesRequest, localToken);
                        localStopwatch.Stop();
                        bool isSuccess = result?.IsSuccess ?? false;
                        int status = isSuccess
                            ? (int)MesUploadStatusEnum.拆盘上传成功
                            : (int)MesUploadStatusEnum.拆盘上传失败;
                        await _stockInfoService.UpdateMesUploadStatusAsync(stockInfo.PalletCode, status);
                        await _mesLogService.LogAsync(new MesApiLogDto
                        {
                            PalletCode = stockInfo.PalletCode,
                            ApiType = "UnbindContainer",
                            RequestJson = requestJson,
                            ResponseJson = System.Text.Json.JsonSerializer.Serialize(result),
                            IsSuccess = isSuccess,
                            ErrorMessage = result?.ErrorMessage ?? "未知错误",
                            ElapsedMs = (int)localStopwatch.ElapsedMilliseconds,
                            Creator = App.User.UserName
                        });
                    }
                    catch (Exception ex)
                    {
                        localStopwatch.Stop();
                        await _stockInfoService.UpdateMesUploadStatusAsync(stockInfo.PalletCode, (int)MesUploadStatusEnum.拆盘上传失败);
                        await _mesLogService.LogAsync(new MesApiLogDto
                        {
                            PalletCode = stockInfo.PalletCode,
                            ApiType = "UnbindContainer",
                            IsSuccess = false,
                            ErrorMessage = ex.Message,
                            ElapsedMs = (int)localStopwatch.ElapsedMilliseconds,
                            Creator = App.User.UserName
                        });
                    }
                });
                            : _mesService.UnBindContainer(mesRequest, token);
                        return (
                            result?.IsSuccess ?? false,
                            System.Text.Json.JsonSerializer.Serialize(result),
                            result?.ErrorMessage ?? "未知错误"
                        );
                    },
                    App.User.UserName);
                // 6. ç«‹å³è¿”回成功响应
                return response.OK("托盘电芯解绑成功");
@@ -302,48 +245,21 @@
                string requestJson = System.Text.Json.JsonSerializer.Serialize(mesRequest);
                // 5. å¼‚步调用MES接口(fire-and-forget)
                _ = Task.Run(async () =>
                {
                    var localStopwatch = Stopwatch.StartNew();
                    try
                _mesUploadHelper.FireAndForget(
                    stockInfo.PalletCode,
                    MesUploadStatusEnum.NG上报成功,
                    "ContainerNgReport",
                    requestJson,
                    () =>
                    {
                        var result = _mesService.ContainerNgReport(mesRequest);
                        localStopwatch.Stop();
                        bool isSuccess = result?.IsSuccess ?? false;
                        int status = isSuccess
                            ? (int)MesUploadStatusEnum.NG上报成功
                            : (int)MesUploadStatusEnum.NG上报失败;
                        await _stockInfoService.UpdateMesUploadStatusAsync(stockInfo.PalletCode, status);
                        await _mesLogService.LogAsync(new MesApiLogDto
                        {
                            PalletCode = stockInfo.PalletCode,
                            ApiType = "ContainerNgReport",
                            RequestJson = requestJson,
                            ResponseJson = System.Text.Json.JsonSerializer.Serialize(result),
                            IsSuccess = isSuccess,
                            ErrorMessage = result?.ErrorMessage ?? "未知错误",
                            ElapsedMs = (int)localStopwatch.ElapsedMilliseconds,
                            Creator = App.User.UserName
                        });
                    }
                    catch (Exception ex)
                    {
                        localStopwatch.Stop();
                        await _stockInfoService.UpdateMesUploadStatusAsync(stockInfo.PalletCode, (int)MesUploadStatusEnum.NG上报失败);
                        await _mesLogService.LogAsync(new MesApiLogDto
                        {
                            PalletCode = stockInfo.PalletCode,
                            ApiType = "ContainerNgReport",
                            IsSuccess = false,
                            ErrorMessage = ex.Message,
                            ElapsedMs = (int)localStopwatch.ElapsedMilliseconds,
                            Creator = App.User.UserName
                        });
                    }
                });
                        return (
                            result?.IsSuccess ?? false,
                            System.Text.Json.JsonSerializer.Serialize(result),
                            result?.ErrorMessage ?? "未知错误"
                        );
                    },
                    App.User.UserName);
                // 6. ç«‹å³è¿”回成功响应
                return response.OK("NG电芯上报成功");