From 0b2869539598059704e1d208e2bcb18603b0fe0f Mon Sep 17 00:00:00 2001
From: wanshenmean <cathay_xy@163.com>
Date: 星期五, 01 五月 2026 00:00:30 +0800
Subject: [PATCH] feat(出库时效): 添加出库时效配置功能

---
 Code/WMS/WIDESEA_WMSServer/WIDESEA_Core/Core/OutboundTimeConfigOptions.cs                       |   28 ++++
 Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/Controllers/Task/RobotTaskController.cs            |    9 
 Code/WMS/WIDESEA_WMSClient/src/router/viewGird.js                                               |    5 
 Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs                               |    6 
 Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/appsettings.json                                   |    2 
 Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Inbound.cs                   |    9 
 Code/WMS/WIDESEA_WMSClient/src/views/system/outboundTimeConfig.vue                              |  109 ++++++++++++++++++
 Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotJob.cs                                |   10 +
 Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotWorkflowOrchestrator.cs      |   13 +
 Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/WIDESEA_WMSServer.csproj                           |    6 +
 Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs                      |   23 +++
 Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/System/OutboundTimeConfigController.cs |   94 +++++++++++++++
 Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Program.cs                                         |    3 
 Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/outbound_time_config.json                          |    7 +
 Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotSimpleCommandHandler.cs      |    8 +
 Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Model/Models/RobotState/RobotSocketState.cs               |   23 +++
 16 files changed, 336 insertions(+), 19 deletions(-)

diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Model/Models/RobotState/RobotSocketState.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Model/Models/RobotState/RobotSocketState.cs
index de75b1d..4fcd16a 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Model/Models/RobotState/RobotSocketState.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Model/Models/RobotState/RobotSocketState.cs
@@ -127,13 +127,23 @@
         public int[]? LastPutPositions { get; set; }
 
         /// <summary>
-        /// 鐢垫睜/璐т綅鏉$爜鍒楄〃
+        /// 鐢垫睜/璐т綅鏉$爜鍒楄〃锛堢疮绉墍鏈夋壒娆★紝涓嶉噸澶嶏級
         /// </summary>
         /// <remarks>
-        /// 鍦ㄧ粍鐩樻搷浣滄椂鐢ㄤ簬璁板綍鐢熸垚鐨勬墭鐩樻潯鐮併��
+        /// 鍦ㄧ粍鐩樻搷浣滄椂鐢ㄤ簬绱Н璁板綍鎵�鏈夌數鑺潯鐮侊紝姣忔璇诲彇鏂版潯鐮佹椂杩藉姞锛堝幓閲嶏級銆�
+        /// 浠呭湪鏀跺埌 allputfinished锛堝叏閮ㄦ斁璐у畬鎴愶級鏃舵竻绌恒��
         /// 姣忎釜鏉$爜鏍煎紡涓� "TRAY" + 鏃ユ湡 + 鏃堕棿 + 闅忔満鏁般��
         /// </remarks>
         public List<string> CellBarcode { get; set; } = new List<string>();
+
+        /// <summary>
+        /// 褰撳墠鎵规鐨勭數鑺潯鐮佸垪琛�
+        /// </summary>
+        /// <remarks>
+        /// 姣忔璇诲彇鏂版潯鐮佹椂璁剧疆涓烘湰鎵规鐨勬潯鐮侊紝浠呯敤浜� WMS 鎻愪氦鏃舵寜鎵规鎻愪氦銆�
+        /// 姣忔鏂版壒娆¤鍙栨椂瑕嗙洊锛屽湪 allputfinished 鏃舵竻绌恒��
+        /// </remarks>
+        public List<string> CurrentBatchBarcodes { get; set; } = new List<string>();
 
         /// <summary>
         /// 鏈烘鎵嬪綋鍓嶆鍦ㄦ墽琛岀殑浠诲姟
@@ -217,5 +227,14 @@
         /// 鎷夊甫绾夸笂鐢佃姱鏄惁鍒颁綅銆�
         /// </remarks>
         public bool BatteryArrived { get; set; } = false;
+
+        /// <summary>
+        /// 褰撳墠鎵ц涓殑鏈哄櫒浜轰换鍔$紪鍙�
+        /// </summary>
+        /// <remarks>
+        /// 涓嬪彂浠诲姟鏃剁紦瀛樹换鍔$紪鍙凤紝鐢ㄤ簬 RobotJob 蹇�熸煡鎵炬墽琛屼腑鐨勪换鍔★紝
+        /// 閬垮厤姣忔杞鍏ㄨ〃鎵弿銆備换鍔″畬鎴愭椂娓呯┖涓� null銆�
+        /// </remarks>
+        public int? CurrentTaskNum { get; set; }
     }
 }
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/Controllers/Task/RobotTaskController.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/Controllers/Task/RobotTaskController.cs
index af053f2..e43311b 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/Controllers/Task/RobotTaskController.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/Controllers/Task/RobotTaskController.cs
@@ -1,9 +1,8 @@
-using Autofac.Core;
-using Masuit.Tools;
 using Microsoft.AspNetCore.Authorization;
 using Microsoft.AspNetCore.Mvc;
 using WIDESEAWCS_Core;
 using WIDESEAWCS_Core.BaseController;
+using WIDESEAWCS_DTO.TaskInfo;
 using WIDESEAWCS_ITaskInfoService;
 using WIDESEAWCS_Model.Models;
 
@@ -27,15 +26,15 @@
             return WebResponseContent.Instance.Error();
         }
 
-
         [HttpGet, HttpPost, Route("GetRobotTaskTotalNum"), AllowAnonymous]
-        public int GetRobotTaskTotalNum( int taskType, string? palletCode)
+        public int GetRobotTaskTotalNum(int taskType, string? palletCode)
         {
             return Service.GetRobotTaskTotalNum(taskType, palletCode);
         }
+
         // 鎵嬪姩鏈烘鎵嬩换鍔�
         [HttpGet, HttpPost, Route("CreateRobotTaskManually"), AllowAnonymous]
-        public WebResponseContent CreateRobotTaskManually([FromBody] ManualRobotTaskDto request) 
+        public WebResponseContent CreateRobotTaskManually([FromBody] ManualRobotTaskDto request)
         {
             return Service.CreateRobotTaskManually(request);
         }
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotJob.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotJob.cs
index c97e7ed..ecb24ae 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotJob.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotJob.cs
@@ -209,7 +209,15 @@
                 }
 
                 // 杞鑾峰彇璇ヨ澶囩殑寰呭鐞嗕换鍔�
-                var task = _taskProcessor.GetTask(robotCrane);
+                // 浼樺厛閫氳繃鐘舵�佷腑缂撳瓨鐨勪换鍔$紪鍙锋煡鎵炬墽琛屼腑鐨勪换鍔�
+                Dt_RobotTask? task = null;
+                if (state.CurrentTaskNum.HasValue)
+                {
+                    task = _taskProcessor.GetTaskByNum(state.CurrentTaskNum.Value);
+                }
+
+                // 缂撳瓨鐨勪换鍔″彿鏈壘鍒板搴斾换鍔℃椂锛屾寜璁惧缂栫爜鑾峰彇鏂颁换鍔�
+                task ??= _taskProcessor.GetTask(robotCrane);
 
                 // 濡傛灉娌℃湁鑾峰彇鍒板緟澶勭悊浠诲姟锛屼笖RobotArmObject涓�1锛堟湁鐗╂枡锛夛紝鍒欒幏鍙栬璁惧鎵ц涓殑浠诲姟
                 //if (task == null && state.RobotArmObject == 1)
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs
index 06c1c36..93fc738 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/RobotTaskProcessor.cs
@@ -124,6 +124,20 @@
         }
 
         /// <summary>
+        /// 鎸変换鍔$紪鍙疯幏鍙栨満鍣ㄤ汉浠诲姟
+        /// </summary>
+        /// <remarks>
+        /// 鐢ㄤ簬 RobotJob 蹇�熸煡鎵炬墽琛屼腑鐨勪换鍔★紝閬垮厤鍏ㄨ〃鎵弿銆�
+        /// 浼樺厛閫氳繃鐘舵�佷腑缂撳瓨鐨� CurrentTaskNum 瀹氫綅浠诲姟銆�
+        /// </remarks>
+        /// <param name="taskNum">鏈哄櫒浜轰换鍔$紪鍙�</param>
+        /// <returns>鍖归厤鐨勪换鍔″璞★紝濡傛灉娌℃湁鍒欒繑鍥� null</returns>
+        public Dt_RobotTask? GetTaskByNum(int taskNum)
+        {
+            return _robotTaskService.Repository.QueryFirst(x => x.RobotTaskNum == taskNum);
+        }
+
+        /// <summary>
         /// 鎸夎澶囩紪鐮佽幏鍙栧綋鍓嶆満鍣ㄤ汉鐨勬墽琛屼腑浠诲姟
         /// </summary>
         /// <remarks>
@@ -186,6 +200,7 @@
 
             // 灏嗕换鍔″叧鑱斿埌鐘舵�佸璞�
             state.CurrentTask = task;
+            state.CurrentTaskNum = task.RobotTaskNum;
 
             if (isScanNG)
             {
@@ -276,6 +291,7 @@
 
             // 灏嗕换鍔″叧鑱斿埌鐘舵�佸璞�
             state.CurrentTask = task;
+            state.CurrentTaskNum = task.RobotTaskNum;
 
             if (_stateManager.TryUpdateStateSafely(state.IPAddress, state))
             {
@@ -346,6 +362,7 @@
 
             task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode();
             state.CurrentTask = task;
+            state.CurrentTaskNum = task.RobotTaskNum;
 
             if (_stateManager.TryUpdateStateSafely(state.IPAddress, state))
             {
@@ -389,6 +406,7 @@
         {
             task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode();
             state.CurrentTask = task;
+            state.CurrentTaskNum = task.RobotTaskNum;
 
             if (_stateManager.TryUpdateStateSafely(state.IPAddress, state))
             {
@@ -434,6 +452,7 @@
         {
             task.RobotTaskState = TaskRobotStatusEnum.RobotExecuting.GetHashCode();
             state.CurrentTask = task;
+            state.CurrentTaskNum = task.RobotTaskNum;
 
             if (_stateManager.TryUpdateStateSafely(state.IPAddress, state))
             {
@@ -688,9 +707,9 @@
                         // 閫氶亾/浣嶇疆缂栧彿
                         Channel = x,
 
-                        // 鐢垫睜鏉$爜锛氬鏋滅姸鎬佷腑鏈夋潯鐮佸垪琛紝鍙栧搴斾綅缃殑鏉$爜锛涘惁鍒欎负绌�
+                        // 鐢垫睜鏉$爜锛氫娇鐢ㄥ綋鍓嶆壒娆℃潯鐮佸垪琛紝鍙栧搴斾綅缃殑鏉$爜锛涘惁鍒欎负绌�
                         //CellBarcode = state.CellBarcode?.Count > 0 ? state.CellBarcode[x - 1] : ""
-                        CellBarcode = !state.CellBarcode.IsNullOrEmpty() ? state.CellBarcode[idx].ToString() ?? string.Empty : string.Empty
+                        CellBarcode = !state.CurrentBatchBarcodes.IsNullOrEmpty() ? state.CurrentBatchBarcodes[idx].ToString() ?? string.Empty : string.Empty
                     })
                     .ToList()
             };
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotSimpleCommandHandler.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotSimpleCommandHandler.cs
index 2c7f640..eb8c01e 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotSimpleCommandHandler.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotSimpleCommandHandler.cs
@@ -221,8 +221,10 @@
                                 QuartzLogHelper.LogInfo(_logger, $"鍙戦�佹秷鎭細銆怗roup,diskFinished銆�", state.RobotCrane.DeviceName);
 
                                 state.CurrentTask = null;
+                                state.CurrentTaskNum = null;
                                 state.RobotTaskTotalNum = 0;
                                 state.CellBarcode = new List<string>();
+                                state.CurrentBatchBarcodes = new List<string>();
                                 state.ChangePalletPhase = 0;
                                 state.CurrentBatchIndex = 1;
                                 state.IsInFakeBatteryMode = false;
@@ -335,8 +337,10 @@
                                 }
 
                                 state.CurrentTask = null;
+                                state.CurrentTaskNum = null;
                                 state.RobotTaskTotalNum = 0;
                                 state.CellBarcode = new List<string>();
+                                state.CurrentBatchBarcodes = new List<string>();
 
                                 await _socketClientGateway.SendToClientAsync(state.IPAddress, $"Group,diskFinished");
                                 QuartzLogHelper.LogInfo(_logger, $"鍙戦�佹秷鎭細銆怗roup,diskFinished銆�", state.RobotCrane.DeviceName);
@@ -371,8 +375,10 @@
                             }
 
                             state.CurrentTask = null;
+                            state.CurrentTaskNum = null;
                             state.RobotTaskTotalNum = 0;
                             state.CellBarcode = new List<string>();
+                            state.CurrentBatchBarcodes = new List<string>();
 
                             await _socketClientGateway.SendToClientAsync(state.IPAddress, $"Group,diskFinished");
                             QuartzLogHelper.LogInfo(_logger, $"鍙戦�佹秷鎭細銆怗roup,diskFinished銆�", state.RobotCrane.DeviceName);
@@ -411,8 +417,10 @@
 
                             // 娓呯悊鐘舵�侊紝涓轰笅涓�涓换鍔″仛鍑嗗
                             state.CurrentTask = null;           // 娓呴櫎褰撳墠浠诲姟
+                            state.CurrentTaskNum = null;        // 娓呴櫎褰撳墠浠诲姟缂栧彿
                             state.RobotTaskTotalNum = 0;        // 閲嶇疆浠诲姟璁℃暟
                             state.CellBarcode = new List<string>();  // 娓呯┖鏉$爜鍒楄〃
+                            state.CurrentBatchBarcodes = new List<string>();  // 娓呯┖褰撳墠鎵规鏉$爜
 
                             await _socketClientGateway.SendToClientAsync(state.IPAddress, $"Group,diskFinished");
                             QuartzLogHelper.LogInfo(_logger, $"鍙戦�佹秷鎭細銆怗roup,diskFinished銆�", state.RobotCrane.DeviceName);
diff --git a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotWorkflowOrchestrator.cs b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotWorkflowOrchestrator.cs
index 8cba5b3..6d8f9a7 100644
--- a/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotWorkflowOrchestrator.cs
+++ b/Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/RobotJob/Workflow/RobotWorkflowOrchestrator.cs
@@ -271,6 +271,7 @@
                 if (stateToUpdate != null)
                 {
                     stateToUpdate.CurrentTask = task;
+                    stateToUpdate.CurrentTaskNum = task.RobotTaskNum;
 
                     if (_stateManager.TryUpdateStateSafely(ipAddress, stateToUpdate))
                     {
@@ -356,10 +357,16 @@
 
                         QuartzLogHelper.LogInfo(_logger, $"HandlePutFinishedStateAsync锛氳鍙栫殑鐢佃姱鏉$爜鍞竴锛岀户缁墽琛岋紝浠诲姟鍙�: {task.RobotTaskNum}", stateForUpdate?.RobotCrane?.DeviceName ?? ipAddress);
 
-                        // 灏嗘潯鐮佹坊鍔犲埌鐘舵�佷腑锛屼緵鍚庣画鏀捐揣鏃朵娇鐢�
-                        stateForUpdate.CellBarcode = new List<string>()
+                        // 灏嗘潯鐮佺疮绉埌 CellBarcode锛堝幓閲嶏級锛屽苟璁剧疆褰撳墠鎵规鏉$爜
+                        if (!stateForUpdate.CellBarcode.Contains(trayBarcode1))
+                            stateForUpdate.CellBarcode.Add(trayBarcode1);
+                        if (!stateForUpdate.CellBarcode.Contains(trayBarcode2))
+                            stateForUpdate.CellBarcode.Add(trayBarcode2);
+
+                        // 璁剧疆褰撳墠鎵规鏉$爜锛岀敤浜� WMS 鎻愪氦
+                        stateForUpdate.CurrentBatchBarcodes = new List<string>()
                         {
-                            trayBarcode1,trayBarcode2
+                            trayBarcode1, trayBarcode2
                         };
                     }
 
diff --git a/Code/WMS/WIDESEA_WMSClient/src/router/viewGird.js b/Code/WMS/WIDESEA_WMSClient/src/router/viewGird.js
index e2bc776..50c2798 100644
--- a/Code/WMS/WIDESEA_WMSClient/src/router/viewGird.js
+++ b/Code/WMS/WIDESEA_WMSClient/src/router/viewGird.js
@@ -236,6 +236,11 @@
     name: 'PDA',
     component: () => import('@/views/system/PDA.vue')
   }
+  , {
+    path: '/outboundTimeConfig',
+    name: 'outboundTimeConfig',
+    component: () => import('@/views/system/outboundTimeConfig.vue')
+  }
 ]
 
 export default viewgird
diff --git a/Code/WMS/WIDESEA_WMSClient/src/views/system/outboundTimeConfig.vue b/Code/WMS/WIDESEA_WMSClient/src/views/system/outboundTimeConfig.vue
new file mode 100644
index 0000000..e44fcc4
--- /dev/null
+++ b/Code/WMS/WIDESEA_WMSClient/src/views/system/outboundTimeConfig.vue
@@ -0,0 +1,109 @@
+<template>
+  <div class="config-container">
+    <el-card class="config-card">
+      <template #header>
+        <span>鍑哄簱鏃舵晥閰嶇疆</span>
+      </template>
+      <el-form :model="form" label-width="180px" v-loading="loading">
+        <el-form-item label="GW棣栨斁鍏ュ簱鏃舵晥锛堝皬鏃讹級">
+          <el-input-number
+            v-model="form.gw1FirstHours"
+            :min="0.01"
+            :max="999"
+            :precision="2"
+            :step="1"
+          />
+        </el-form-item>
+        <el-form-item label="GW浜屾斁鍏ュ簱鏃舵晥锛堝皬鏃讹級">
+          <el-input-number
+            v-model="form.gw1SecondHours"
+            :min="0.01"
+            :max="999"
+            :precision="2"
+            :step="0.01"
+          />
+        </el-form-item>
+        <el-form-item label="CW鍑哄簱鏃舵晥锛堝皬鏃讹級">
+          <el-input-number
+            v-model="form.cw1Hours"
+            :min="0.01"
+            :max="999"
+            :precision="2"
+            :step="1"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="handleSave" :loading="saving">
+            淇濆瓨
+          </el-button>
+        </el-form-item>
+      </el-form>
+    </el-card>
+  </div>
+</template>
+
+<script>
+import { ref, onMounted } from "vue";
+import { ElMessage } from "element-plus";
+import http from "@/api/http";
+
+export default {
+  name: "outboundTimeConfig",
+  setup() {
+    const loading = ref(false);
+    const saving = ref(false);
+    const form = ref({
+      gw1FirstHours: 24,
+      gw1SecondHours: 0.05,
+      cw1Hours: 3,
+    });
+
+    /// 鍔犺浇褰撳墠閰嶇疆
+    const loadConfig = async () => {
+      loading.value = true;
+      try {
+        const res = await http.post("/api/OutboundTimeConfig/get", {}, false);
+        if (res.status) {
+          form.value = {
+            gw1FirstHours: res.data.gw1FirstHours,
+            gw1SecondHours: res.data.gw1SecondHours,
+            cw1Hours: res.data.cw1Hours,
+          };
+        }
+      } finally {
+        loading.value = false;
+      }
+    };
+
+    /// 淇濆瓨閰嶇疆
+    const handleSave = async () => {
+      saving.value = true;
+      try {
+        const res = await http.post("/api/OutboundTimeConfig/update", form.value, false);
+        if (res.status) {
+          ElMessage.success("淇濆瓨鎴愬姛");
+        } else {
+          ElMessage.error(res.message || "淇濆瓨澶辫触");
+        }
+      } finally {
+        saving.value = false;
+      }
+    };
+
+    onMounted(() => {
+      loadConfig();
+    });
+
+    return { form, loading, saving, handleSave };
+  },
+};
+</script>
+
+<style scoped>
+.config-container {
+  padding: 20px;
+}
+.config-card {
+  max-width: 600px;
+}
+</style>
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_Core/Core/OutboundTimeConfigOptions.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_Core/Core/OutboundTimeConfigOptions.cs
new file mode 100644
index 0000000..2b3c643
--- /dev/null
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_Core/Core/OutboundTimeConfigOptions.cs
@@ -0,0 +1,28 @@
+namespace WIDESEA_Core.Core
+{
+    /// <summary>
+    /// 鍑哄簱鏃舵晥閰嶇疆閫夐」锛岀粦瀹� appsettings.json 涓殑 OutboundTimeConfig 鑺�
+    /// </summary>
+    public class OutboundTimeConfigOptions
+    {
+        /// <summary>
+        /// 閰嶇疆鑺傚悕绉�
+        /// </summary>
+        public const string SectionName = "OutboundTimeConfig";
+
+        /// <summary>
+        /// GW棣栨斁鍏ュ簱鏃舵晥锛堝皬鏃讹級锛岄粯璁�24灏忔椂
+        /// </summary>
+        public double Gw1FirstHours { get; set; } = 24;
+
+        /// <summary>
+        /// GW浜屾斁鍏ュ簱鏃舵晥锛堝皬鏃讹級锛岄粯璁�0.05灏忔椂锛堢害3鍒嗛挓锛�
+        /// </summary>
+        public double Gw1SecondHours { get; set; } = 0.05;
+
+        /// <summary>
+        /// CW鍑哄簱鏃舵晥锛堝皬鏃讹級锛岄粯璁�3灏忔椂
+        /// </summary>
+        public double Cw1Hours { get; set; } = 3;
+    }
+}
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs
index 77f62a8..2367be1 100644
--- a/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs
@@ -9,6 +9,7 @@
 using WIDESEA_Common.StockEnum;
 using WIDESEA_Common.TaskEnum;
 using WIDESEA_Common.WareHouseEnum;
+using Microsoft.Extensions.Options;
 using WIDESEA_Core;
 using WIDESEA_Core.BaseRepository;
 using WIDESEA_Core.BaseServices;
@@ -43,6 +44,7 @@
         private readonly IMesLogService _mesLogService;
         private readonly IMesUploadHelper _mesUploadHelper;
         private readonly ISqlSugarClient _sqlSugarClient;
+        private readonly IOptionsMonitor<OutboundTimeConfigOptions> _outboundTimeOptions;
 
         public IRepository<Dt_Task> Repository => BaseDal;
 
@@ -71,7 +73,8 @@
             IMESDeviceConfigService mesDeviceConfigService,
             IMesLogService mesLogService,
             IMesUploadHelper mesUploadHelper,
-            ISqlSugarClient sqlSugarClient) : base(BaseDal)
+            ISqlSugarClient sqlSugarClient,
+            IOptionsMonitor<OutboundTimeConfigOptions> outboundTimeOptions) : base(BaseDal)
         {
             _mapper = mapper;
             _stockInfoService = stockInfoService;
@@ -88,6 +91,7 @@
             _mesLogService = mesLogService;
             _mesUploadHelper = mesUploadHelper;
             _sqlSugarClient = sqlSugarClient;
+            _outboundTimeOptions = outboundTimeOptions;
         }
 
         /// <summary>
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Inbound.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Inbound.cs
index 2d22bbb..53cd86a 100644
--- a/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Inbound.cs
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/WCS/TaskService_Inbound.cs
@@ -233,14 +233,15 @@
         /// <param name="stockInfo">搴撳瓨淇℃伅</param>
         private void SetOutboundDateByRoadway(Dt_Task task, Dt_StockInfo stockInfo)
         {
+            var config = _outboundTimeOptions.CurrentValue;
             var now = DateTime.Now;
             if (task.Roadway.Contains("GW"))
             {
                 stockInfo.OutboundDate = string.IsNullOrEmpty(stockInfo.Remark)
-                    ? now.AddHours(OutboundTimeConstants.OUTBOUND_HOURS_GW1_FIRST)
+                    ? now.AddHours(config.Gw1FirstHours)
                     : stockInfo.Remark == StockRemarkConstants.GW1
-                        ? now.AddHours(OutboundTimeConstants.OUTBOUND_HOURS_GW1_SECOND)
-                        : now.AddHours(OutboundTimeConstants.OUTBOUND_HOURS_GW1_FIRST);
+                        ? now.AddHours(config.Gw1SecondHours)
+                        : now.AddHours(config.Gw1FirstHours);
 
                 stockInfo.Remark = string.IsNullOrEmpty(stockInfo.Remark)
                     ? StockRemarkConstants.GW1
@@ -250,7 +251,7 @@
             }
             else if (task.Roadway.Contains("CW"))
             {
-                stockInfo.OutboundDate = now.AddHours(OutboundTimeConstants.OUTBOUND_HOURS_CW1);
+                stockInfo.OutboundDate = now.AddHours(config.Cw1Hours);
                 if (stockInfo.Remark == StockRemarkConstants.GW2)
                     stockInfo.Remark = StockRemarkConstants.CW1;
             }
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/System/OutboundTimeConfigController.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/System/OutboundTimeConfigController.cs
new file mode 100644
index 0000000..6b1804b
--- /dev/null
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/System/OutboundTimeConfigController.cs
@@ -0,0 +1,94 @@
+using Microsoft.AspNetCore.Authorization;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Options;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using WIDESEA_Core;
+using WIDESEA_Core.Core;
+
+namespace WIDESEA_WMSServer.Controllers
+{
+    /// <summary>
+    /// 鍑哄簱鏃舵晥閰嶇疆鎺у埗鍣紝璇诲啓鐙珛鐨� outbound_time_config.json 閰嶇疆鏂囦欢
+    /// </summary>
+    [Route("api/[controller]")]
+    [ApiController]
+    [Authorize]
+    public class OutboundTimeConfigController : ControllerBase
+    {
+        private readonly IOptionsMonitor<OutboundTimeConfigOptions> _optionsMonitor;
+        private readonly IWebHostEnvironment _env;
+
+        /// <summary>
+        /// 閰嶇疆鏂囦欢鍚�
+        /// </summary>
+        private const string ConfigFileName = "outbound_time_config.json";
+
+        /// <summary>
+        /// 鏋勯�犲嚱鏁�
+        /// </summary>
+        /// <param name="optionsMonitor">鍑哄簱鏃舵晥閰嶇疆 Options 鐩戞帶鍣�</param>
+        /// <param name="env">Web 瀹夸富鐜锛岀敤浜庡畾浣嶉厤缃枃浠惰矾寰�</param>
+        public OutboundTimeConfigController(IOptionsMonitor<OutboundTimeConfigOptions> optionsMonitor, IWebHostEnvironment env)
+        {
+            _optionsMonitor = optionsMonitor;
+            _env = env;
+        }
+
+        /// <summary>
+        /// 鑾峰彇褰撳墠鍑哄簱鏃舵晥閰嶇疆
+        /// </summary>
+        /// <returns>褰撳墠閰嶇疆鍊�</returns>
+        [HttpPost("get")]
+        public IActionResult Get()
+        {
+            var config = _optionsMonitor.CurrentValue;
+            return Ok(WebResponseContent.Instance.OK("鑾峰彇鎴愬姛", new
+            {
+                config.Gw1FirstHours,
+                config.Gw1SecondHours,
+                config.Cw1Hours
+            }));
+        }
+
+        /// <summary>
+        /// 鏇存柊鍑哄簱鏃舵晥閰嶇疆锛屽啓鍏ョ嫭绔嬬殑 outbound_time_config.json 鏂囦欢
+        /// </summary>
+        /// <param name="config">鏂扮殑閰嶇疆鍊�</param>
+        /// <returns>鎿嶄綔缁撴灉</returns>
+        [HttpPost("update")]
+        public IActionResult Update([FromBody] OutboundTimeConfigOptions config)
+        {
+            if (config.Gw1FirstHours <= 0 || config.Gw1SecondHours <= 0 || config.Cw1Hours <= 0)
+            {
+                return Ok(WebResponseContent.Instance.Error("閰嶇疆鍊煎繀椤诲ぇ浜�0"));
+            }
+
+            try
+            {
+                var filePath = Path.Combine(_env.ContentRootPath, ConfigFileName);
+
+                // 鏋勫缓閰嶇疆 JSON锛屽寘瑁瑰湪 OutboundTimeConfig 鑺備笅浠ュ尮閰� Options 缁戝畾
+                var configSection = new JObject
+                {
+                    ["Gw1FirstHours"] = config.Gw1FirstHours,
+                    ["Gw1SecondHours"] = config.Gw1SecondHours,
+                    ["Cw1Hours"] = config.Cw1Hours
+                };
+                var jsonObj = new JObject
+                {
+                    [OutboundTimeConfigOptions.SectionName] = configSection
+                };
+
+                // 鍐欏叆鏂囦欢锛屾牸寮忓寲杈撳嚭
+                System.IO.File.WriteAllText(filePath, jsonObj.ToString(Formatting.Indented));
+
+                return Ok(WebResponseContent.Instance.OK("閰嶇疆鏇存柊鎴愬姛"));
+            }
+            catch (Exception ex)
+            {
+                return Ok(WebResponseContent.Instance.Error($"閰嶇疆鏇存柊澶辫触: {ex.Message}"));
+            }
+        }
+    }
+}
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Program.cs b/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Program.cs
index a3e0f16..43e60a1 100644
--- a/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Program.cs
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Program.cs
@@ -36,6 +36,7 @@
     hostingContext.Configuration.ConfigureApplication();
     config.Sources.Clear();
     config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
+    config.AddJsonFile("outbound_time_config.json", optional: true, reloadOnChange: true);
 });
 
 builder.Host.UseSerilog((context, services, loggerConfiguration) =>
@@ -75,6 +76,8 @@
 builder.Services.AddSingleton<RoundRobinService>();
 builder.Services.Configure<AutoOutboundTaskOptions>(
     builder.Configuration.GetSection("AutoOutboundTask"));
+builder.Services.Configure<OutboundTimeConfigOptions>(
+    builder.Configuration.GetSection(OutboundTimeConfigOptions.SectionName));
 builder.Services.AddMemoryCacheSetup(); // 缂撳瓨鏈嶅姟
 builder.Services.AddWebSocketSetup();
 builder.Services.AddSqlsugarSetup(); // SqlSugar 鏁版嵁搴撻厤缃�
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/WIDESEA_WMSServer.csproj b/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/WIDESEA_WMSServer.csproj
index 234f1e2..e3f712c 100644
--- a/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/WIDESEA_WMSServer.csproj
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/WIDESEA_WMSServer.csproj
@@ -24,6 +24,12 @@
   </ItemGroup>
 
   <ItemGroup>
+    <Content Update="outbound_time_config.json">
+      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+    </Content>
+  </ItemGroup>
+
+  <ItemGroup>
 	  <Content Update="wwwroot\swg-login.html">
 		  <CopyToOutputDirectory>Never</CopyToOutputDirectory>
 	  </Content>
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/appsettings.json b/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/appsettings.json
index 56185d5..fc7e7ae 100644
--- a/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/appsettings.json
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/appsettings.json
@@ -34,7 +34,7 @@
   "MainDB": "DB_WIDESEA", //褰撳墠椤圭洰鐨勪富搴擄紝鎵�瀵瑰簲鐨勮繛鎺ュ瓧绗︿覆鐨凟nabled蹇呴』涓簍rue
   //杩炴帴瀛楃涓�
   //"ConnectionString": "HTI6FB1H05Krd07mNm9yBCNhofW6edA5zLs9TY~MNthRYW3kn0qKbMIsGp~3yyPDF1YZUCPBQx8U0Jfk4PH~ajNFXVIwlH85M3F~v_qKYQ3CeAz3q1mLVDn8O5uWt1~3Ut2V3KRkEwYHvW2oMDN~QIDXPxDgXN0R2oTIhc9dNu7QNaLEknblqmHhjaNSSpERdDVZIgHnMKejU_SL49tralBkZmDNi0hmkbL~837j1NWe37u9fJKmv91QPb~16JsuI9uu0EvNZ06g6PuZfOSAeFH9GMMIZiketdcJG3tHelo=",
-  "ConnectionString": "Data Source=192.168.60.30;Initial Catalog=WIDESEAWMS_ShanMei;User ID=sa;Password=P@ssw0rd;Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
+  "ConnectionString": "Data Source=.;Initial Catalog=WIDESEAWMS_ShanMei;User ID=sa;Password=P@ssw0rd;Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
   //"ConnectionString": "Data Source=.;Initial Catalog=WIDESEAWMS_ShanMei;User ID=sa;Password=123456;Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
   //"ConnectionString": "Data Source=10.30.4.92;Initial Catalog=WMS_TC;User ID=sa;Password=duo123456;Integrated Security=False;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False",
   //鏃MS鏁版嵁搴撹繛鎺�
diff --git a/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/outbound_time_config.json b/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/outbound_time_config.json
new file mode 100644
index 0000000..bd086c3
--- /dev/null
+++ b/Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/outbound_time_config.json
@@ -0,0 +1,7 @@
+{
+  "OutboundTimeConfig": {
+    "Gw1FirstHours": 24.0,
+    "Gw1SecondHours": 0.05,
+    "Cw1Hours": 3.0
+  }
+}
\ No newline at end of file

--
Gitblit v1.9.3