wanshenmean
8 小时以前 75ef0dfecf2331c0828a1a182b1d3243d3041c51
refactor: 统一日志组件为Serilog并优化相关功能

feat(StockInfoDetail): 新增托盘绑定和解绑功能

fix: 修复跨域配置和HTTP头设置问题

perf(ConveyorLineNewJob): 添加托盘检查间隔限制优化性能

style: 清理无用引用和格式化代码
已修改18个文件
278 ■■■■■ 文件已修改
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/Program.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/appsettings.json 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/CommonConveyorLineNewJob.cs 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineDispatchHandler.cs 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineTargetAddressSelector.cs 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineTaskFilter.cs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/QuartzLogHelper.cs 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/CommonStackerCraneJob.cs 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneCommandBuilder.cs 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneTaskSelector.cs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/api/http.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/stock/stockInfo.jsx 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/extension/system/Mes_Log.jsx 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSClient/src/views/system/Mes_Log.vue 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_Core/Filter/ApiAuthorizeFilter.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_Core/Middlewares/HttpRequestMiddleware.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockInfoController.cs 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockInfoDetailController.cs 71 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/Program.cs
@@ -49,7 +49,7 @@
        .Enrich.WithProperty("Application", "WCS")
        // 设置Microsoft命名空间的日志级别为Information
        // 这样可以减少Microsoft框架本身的详细日志,避免过多的Debug日志
        .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
        .MinimumLevel.Override("Microsoft", LogEventLevel.Debug)
        .WriteTo.Console()  // 添加控制台输出接收器,日志将显示在控制台窗口中
                            // 添加文件输出接收器,将日志写入文件系统
        .WriteTo.File(
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Server/appsettings.json
@@ -39,7 +39,7 @@
  //跨域
  "Cors": {
    "PolicyName": "CorsIpAccess", //策略名称
    "EnableAllIPs": false, //当为true时,开放所有IP均可访问。
    "EnableAllIPs": true, //当为true时,开放所有IP均可访问。
    // 支持多个域名端口,注意端口号后不要带/斜杆:比如localhost:8000/,是错的
    // 注意,http://127.0.0.1:1818 和 http://localhost:1818 是不一样的
    "IPs": "http://127.0.0.1:8080,http://localhost:8080,http://localhost:8081"
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/CommonConveyorLineNewJob.cs
@@ -1,15 +1,14 @@
using MapsterMapper;
using Masuit.Tools;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Quartz;
using Serilog;
using SqlSugar;
using WIDESEA_Core;
using WIDESEAWCS_Common.TaskEnum;
using WIDESEAWCS_Core;
using WIDESEAWCS_Core.Helper;
using WIDESEAWCS_Core.LogHelper;
using WIDESEAWCS_DTO.TaskInfo;
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
@@ -41,7 +40,6 @@
        /// 任务服务
        /// </summary>
        private readonly ITaskService _taskService;
        /// <summary>
        /// 机器人任务服务
@@ -82,7 +80,7 @@
        /// <summary>
        /// 日志记录器
        /// </summary>
        private readonly ILogger<CommonConveyorLineNewJob> _logger;
        private readonly ILogger _logger;
        /// <summary>
        /// 目标地址到设备类型的映射
@@ -90,6 +88,11 @@
        /// <remarks>
        /// </remarks>
        private static List<string> AddressToDeviceType = new List<string> { "11020", "11028" };
        /// <summary>
        /// 托盘检查位置的最近执行时间(用于30秒间隔限制)
        /// </summary>
        private static readonly Dictionary<string, DateTime> _lastPalletCheckTime = new();
        /// <summary>
        /// 构造函数
@@ -100,7 +103,7 @@
        /// <param name="mapper">对象映射器</param>
        /// <param name="httpClientHelper">HTTP 客户端帮助类</param>
        /// <param name="logger">日志记录器</param>
        public CommonConveyorLineNewJob(ITaskService taskService, ITaskExecuteDetailService taskExecuteDetailService, IRouterService routerService, IMapper mapper, HttpClientHelper httpClientHelper, ILogger<CommonConveyorLineNewJob> logger, IRobotTaskService robotTaskService)
        public CommonConveyorLineNewJob(ITaskService taskService, ITaskExecuteDetailService taskExecuteDetailService, IRouterService routerService, IMapper mapper, HttpClientHelper httpClientHelper, ILogger logger, IRobotTaskService robotTaskService)
        {
            _taskService = taskService;
            _taskExecuteDetailService = taskExecuteDetailService;
@@ -179,6 +182,13 @@
                            // 如果当前设备在检查列表中
                            if (checkPalletPositions.Any(x => x.Code == childDeviceCode))
                            {
                                // 30秒间隔限制
                                if (_lastPalletCheckTime.TryGetValue(childDeviceCode, out var lastTime) &&
                                    (DateTime.Now - lastTime).TotalSeconds < 30)
                                {
                                    continue;
                                }
                                // 检查输送线状态(是否有托盘)
                                if (command.CV_State == 2)
                                {
@@ -196,6 +206,8 @@
                                            TargetAddress = childDeviceCode
                                        }.Serialize());
                                        _lastPalletCheckTime[childDeviceCode] = DateTime.Now;
                                        // 如果请求成功,接收 WMS 返回的任务
                                        if (responseResult.IsSuccess && responseResult.Data.Status)
                                        {
@@ -208,7 +220,7 @@
                                }
                            }
                            #endregion
                            #endregion 检测是否需要空托盘
                            // ========== 检查 PLC_STB 标志 ==========
                            // 只有当 PLC_STB 为 1 时才处理任务
@@ -278,7 +290,6 @@
                                        RobotTargetAddressLineCode = childDeviceCode,
                                        RobotTaskNum = num, // 生成任务号
                                        RobotDispatchertime = DateTime.Now,
                                    };
                                    if (_robotTaskService.AddData(robotTask).Status)
                                    {
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineDispatchHandler.cs
@@ -1,9 +1,7 @@
using MapsterMapper;
using Microsoft.Extensions.Logging;
using Serilog;
using WIDESEAWCS_Common.TaskEnum;
using WIDESEAWCS_Core;
using WIDESEAWCS_Core.Helper;
using WIDESEAWCS_Core.LogHelper;
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
using WIDESEAWCS_QuartzJob;
@@ -201,7 +199,6 @@
            Dt_Task? task = _taskFilter.QueryExecutingTask(command.TaskNo, childDeviceCode);
            if (task != null)
            {
                // 更新任务状态到下一阶段(通常是完成)
                if (_taskService.UpdateTaskStatusToNext(task).Status)
                {
@@ -209,8 +206,6 @@
                    conveyorLine.SetValue(ConveyorLineDBNameNew.WCS_ACK, (short)1, childDeviceCode);
                    QuartzLogHelper.LogInfo(_logger, "ConveyorLineInFinish:入库完成,任务号: {TaskNum},子设备: {ChildDeviceCode}", $"入库完成,任务号: {task.TaskNum}", conveyorLine.DeviceCode, task.TaskNum, childDeviceCode);
                }
            }
        }
@@ -316,4 +311,4 @@
            }
        }
    }
}
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineTargetAddressSelector.cs
@@ -1,5 +1,4 @@
using Microsoft.Extensions.Logging;
using WIDESEAWCS_Core.LogHelper;
using Serilog;
using WIDESEAWCS_QuartzJob;
namespace WIDESEAWCS_Tasks
@@ -276,7 +275,7 @@
            if (device == null)
            {
                // 设备未找到时记录调试日志,方便排查配置问题
                _logger.LogDebug("FindDevice:未找到 {DeviceName}", deviceName);
                _logger.Debug("FindDevice:未找到 {DeviceName}", deviceName);
            }
            return device; // 可能为 null,由调用方负责 null 检查
        }
@@ -456,4 +455,4 @@
            QuartzLogHelper.LogDebug(_logger, "Handle{Scenario}:子设备: {ChildDeviceCode},目标地址: {NextAddress}", $"Handle{scenario}:子设备: {childDeviceCode},目标地址: {nextAddress}", conveyorLine.DeviceCode, scenario, childDeviceCode, nextAddress);
        }
    }
}
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/ConveyorLineNewJob/ConveyorLineTaskFilter.cs
@@ -1,5 +1,4 @@
using Microsoft.Extensions.Logging;
using WIDESEAWCS_Core.LogHelper;
using Serilog;
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
@@ -94,4 +93,4 @@
            return result.Status;
        }
    }
}
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/QuartzLogHelper.cs
@@ -1,4 +1,4 @@
using Microsoft.Extensions.Logging;
using Serilog;
using WIDESEAWCS_Core.LogHelper;
namespace WIDESEAWCS_Tasks;
@@ -23,7 +23,7 @@
    /// <param name="args">ILogger 结构化日志的参数</param>
    public static void LogError(ILogger logger, Exception ex, string loggerMessage, string quartzMessage, string deviceCode, params object[] args)
    {
        logger.LogError(ex, loggerMessage, args);
        logger.Error(ex, loggerMessage, args);
        QuartzLogger.Error(quartzMessage, deviceCode, ex);
    }
@@ -37,7 +37,7 @@
    /// <param name="args">ILogger 结构化日志的参数</param>
    public static void LogError(ILogger logger, string loggerMessage, string quartzMessage, string deviceCode, params object[] args)
    {
        logger.LogError(loggerMessage, args);
        logger.Error(loggerMessage, args);
        QuartzLogger.Error(quartzMessage, deviceCode);
    }
@@ -51,8 +51,21 @@
    /// <param name="args">ILogger 结构化日志的参数</param>
    public static void LogInfo(ILogger logger, string loggerMessage, string quartzMessage, string deviceCode, params object[] args)
    {
        logger.LogInformation(loggerMessage, args);
        logger.Information(loggerMessage, args);
        QuartzLogger.Info(quartzMessage, deviceCode);
    }
    /// <summary>
    /// 记录信息日志
    /// </summary>
    /// <param name="logger">ILogger 实例</param>
    /// <param name="loggerMessage">ILogger 的结构化日志模板</param>
    /// <param name="quartzMessage">QuartzLogger 的日志消息</param>
    /// <param name="deviceCode">设备编码</param>
    public static void LogInfo(ILogger logger, string loggerMessage, string deviceCode)
    {
        logger.Information(loggerMessage);
        QuartzLogger.Info(loggerMessage, deviceCode);
    }
    /// <summary>
@@ -65,7 +78,7 @@
    /// <param name="args">ILogger 结构化日志的参数</param>
    public static void LogWarn(ILogger logger, string loggerMessage, string quartzMessage, string deviceCode, params object[] args)
    {
        logger.LogWarning(loggerMessage, args);
        logger.Warning(loggerMessage, args);
        QuartzLogger.Warn(quartzMessage, deviceCode);
    }
@@ -79,7 +92,7 @@
    /// <param name="args">ILogger 结构化日志的参数</param>
    public static void LogDebug(ILogger logger, string loggerMessage, string quartzMessage, string deviceCode, params object[] args)
    {
        logger.LogDebug(loggerMessage, args);
        logger.Debug(loggerMessage, args);
        QuartzLogger.Debug(quartzMessage, deviceCode);
    }
}
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/CommonStackerCraneJob.cs
@@ -1,7 +1,8 @@
using Microsoft.Extensions.Logging;
//using Microsoft.Extensions.Logging;
using Quartz;
using Serilog;
using WIDESEA_Core;
using WIDESEAWCS_Core;
using WIDESEAWCS_Common.Constants;
using WIDESEAWCS_Core.LogHelper;
using WIDESEAWCS_ITaskInfoRepository;
using WIDESEAWCS_ITaskInfoService;
@@ -10,7 +11,6 @@
using WIDESEAWCS_QuartzJob.Service;
using WIDESEAWCS_QuartzJob.StackerCrane;
using WIDESEAWCS_QuartzJob.StackerCrane.Enum;
using WIDESEAWCS_Common.Constants;
using WIDESEAWCS_Tasks.StackerCraneJob;
namespace WIDESEAWCS_Tasks
@@ -81,7 +81,7 @@
        /// <summary>
        /// 日志记录器
        /// </summary>
        private readonly ILogger<CommonStackerCraneJob> _logger;
        private readonly ILogger  _logger;
        /// <summary>
        /// 堆垛机设备编码
@@ -103,7 +103,7 @@
            ITaskRepository taskRepository,
            IRouterService routerService,
            HttpClientHelper httpClientHelper,
            ILogger<CommonStackerCraneJob> logger)
            ILogger logger)
        {
            _taskService = taskService;
            _taskExecuteDetailService = taskExecuteDetailService;
@@ -169,10 +169,12 @@
        {
            try
            {
                //QuartzLogger.Info($"CommonStackerCraneJob Execute:开始执行堆垛机任务调度 【{DateTime.Now.ToString("F")}】", "CommonStackerCraneJob Execute ");
                // 从 JobDataMap 获取堆垛机设备参数
                bool flag = context.JobDetail.JobDataMap.TryGetValue("JobParams", out object? value);
                if (!flag || value is not CommonStackerCrane commonStackerCrane)
                {
                    _logger.Information("Execute:参数无效,未找到 JobParams 或类型不匹配");
                    // 参数无效,直接返回
                    QuartzLogHelper.LogWarn(_logger, "Execute:参数无效", "Execute:参数无效", "CommonStackerCraneJob");
                    return Task.CompletedTask;
@@ -193,7 +195,7 @@
                // ========== 检查是否可以发送新任务 ==========
                //if (!commonStackerCrane.IsCanSendTask(commonStackerCrane.Communicator, commonStackerCrane.DeviceProDTOs, commonStackerCrane.DeviceProtocolDetailDTOs))
                if (commonStackerCrane.StackerCraneStatusValue != StackerCraneStatus.Normal )
                if (commonStackerCrane.StackerCraneStatusValue != StackerCraneStatus.Normal)
                {
                    // 堆垛机不可用(如正在执行上一任务),直接返回
                    return Task.CompletedTask;
@@ -240,6 +242,10 @@
                // 记录异常
                QuartzLogHelper.LogError(_logger, ex, "Execute:执行异常,设备: {DeviceCode}", $"执行异常: {ex.Message}", _deviceCode, _deviceCode);
            }
            finally
            {
                QuartzLogHelper.LogInfo(_logger, $"CommonStackerCraneJob Execute:堆垛机任务调度执行完成 【{DateTime.Now.ToString("F")}】", _deviceCode);
            }
            return Task.CompletedTask;
        }
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneCommandBuilder.cs
@@ -1,9 +1,7 @@
using Microsoft.Extensions.Logging;
using System;
using Serilog;
using System.Diagnostics.CodeAnalysis;
using WIDESEAWCS_Common.Constants;
using WIDESEAWCS_Common.TaskEnum;
using WIDESEAWCS_Core.LogHelper;
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
using WIDESEAWCS_QuartzJob.Models;
@@ -68,7 +66,7 @@
        /// <returns>堆垛机命令对象,转换失败返回 null</returns>
        public object? ConvertToStackerCraneTaskCommand([NotNull] Dt_Task task)
        {
            return  BuildCommand(task, CreateStandardCommand(task));
            return BuildCommand(task, CreateStandardCommand(task));
            // 根据巷道获取命令类型
            //string commandType = GetCommandType(task.Roadway);
@@ -198,7 +196,7 @@
            {
                taskType = StackerCraneConst.EmptyPalletTaskType;
            }
            else if(task.TaskType == (int)TaskInboundTypeEnum.InEmpty)
            else if (task.TaskType == (int)TaskInboundTypeEnum.InEmpty)
            {
                taskType = StackerCraneConst.EmptyInPalletTaskType;
            }
@@ -388,4 +386,4 @@
                && short.TryParse(parts[2], out layer);
        }
    }
}
}
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_Tasks/StackerCraneJob/StackerCraneTaskSelector.cs
@@ -1,12 +1,11 @@
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Serilog;
using System.Diagnostics.CodeAnalysis;
using WIDESEA_Core;
using WIDESEAWCS_Common.Constants;
using WIDESEAWCS_Common.HttpEnum;
using WIDESEAWCS_Common.TaskEnum;
using WIDESEAWCS_Core;
using WIDESEAWCS_Core.LogHelper;
using WIDESEAWCS_ITaskInfoService;
using WIDESEAWCS_Model.Models;
using WIDESEAWCS_QuartzJob;
@@ -316,4 +315,4 @@
            return isOccupied;
        }
    }
}
}
Code/WMS/WIDESEA_WMSClient/src/api/http.js
@@ -12,7 +12,7 @@
let loadingInstance;
let loadingStatus = false;
if (process.env.NODE_ENV == 'development') {
    axios.defaults.baseURL = 'http://127.0.0.1:9291/';
    axios.defaults.baseURL = window.webConfig.webApiProduction;
}
else if (process.env.NODE_ENV == 'debug') {
    axios.defaults.baseURL = window.webConfig.webApiBaseUrl;
Code/WMS/WIDESEA_WMSClient/src/extension/stock/stockInfo.jsx
@@ -28,6 +28,11 @@
              <el-button
                type="primary"
                size="small"
                onClick={($e) => { this.handleBind(row); }}
              >绑定</el-button>
              <el-button
                type="primary"
                size="small"
                onClick={($e) => { this.handleInbound(row); }}
              >进站</el-button>
              <el-button
@@ -36,10 +41,42 @@
                style="margin-left: 8px"
                onClick={($e) => { this.handleOutbound(row); }}
              >出站</el-button>
              <el-button
                type="success"
                size="small"
                style="margin-left: 8px"
                onClick={($e) => { this.handleUnbind(row); }}
              >解绑</el-button>
            </div>
          );
        }
      });
    },
    // 托盘组盘操作
    async handleBind(row) {
      try {
        await this.$confirm(`确认执行托盘组盘操作?\n托盘编号:${row.palletCode}`, "组盘确认", {
          confirmButtonText: "确认",
          cancelButtonText: "取消",
          type: "warning"
        });
        const result = await this.http.post("/api/StockInfoDetail/BindContainer", {
          palletCode: row.palletCode
        }, "正在调用MES接口...");
        if (result.status) {
          this.$Message.success(result.message || "托盘组盘成功");
          this.$refs.table.load();
        } else {
          this.$error(result.message || "托盘组盘失败");
        }
      } catch (error) {
        if (error !== "cancel") {
          this.$error(error.message || "网络错误,请稍后重试");
        }
      }
    },
    // 托盘进站操作
@@ -96,6 +133,32 @@
      }
    },
    // 托盘拆盘操作
    async handleUnbind(row) {
      try {
        await this.$confirm(`确认执行托盘拆盘操作?\n托盘编号:${row.palletCode}`, "拆盘确认", {
          confirmButtonText: "确认",
          cancelButtonText: "取消",
          type: "warning"
        });
        const result = await this.http.post("/api/StockInfoDetail/UnbindContainer", {
          palletCode: row.palletCode,
        }, "正在调用MES接口...");
        if (result.status) {
          this.$Message.success(result.message || "托盘拆盘成功");
          this.$refs.table.load();
        } else {
          this.$error(result.message || "托盘拆盘失败");
        }
      } catch (error) {
        if (error !== "cancel") {
          this.$error(error.message || "网络错误,请稍后重试");
        }
      }
    },
    onInited() {
      // 框架初始化配置后
    },
Code/WMS/WIDESEA_WMSClient/src/extension/system/Mes_Log.jsx
@@ -48,13 +48,15 @@
        this.showJsonDetail(row, 'request');
      } else if (column.property === 'responseJson' && row.responseJson) {
        this.showJsonDetail(row, 'response');
      } else if (column.property === 'errorMessage' && row.errorMessage) {
        this.showJsonDetail(row, 'errorMessage');
      }
    },
    // 显示 JSON 详情抽屉
    showJsonDetail(row, type = 'request') {
      const jsonContent = type === 'request' ? row.requestJson : row.responseJson;
      const title = type === 'request' ? '📋 请求 JSON' : '📥 响应 JSON';
      const jsonContent = type === 'request' ? row.requestJson :  type === 'response' ? row.responseJson : row.errorMessage;
      const title = type === 'request' ? '📋 请求 JSON' : type === 'response' ? '📋 请求 JSON': '📥 错误消息';
      // 解析 JSON 对象,解析失败则保留原始字符串
      let jsonData;
Code/WMS/WIDESEA_WMSClient/src/views/system/Mes_Log.vue
@@ -81,7 +81,19 @@
          return `<span style="color: #409EFF; cursor: pointer;">${preview}</span>`;
        }
      },
      { field: "errorMessage", title: "错误信息", width: 250 },
      {
        field: "errorMessage",
        title: "错误信息",
        width: 250,
        formatter: (row) => {
          if (!row.responseJson) return '-';
          const preview = row.responseJson.length > 50
            ? row.responseJson.substring(0, 50) + '...'
            : row.responseJson;
          return `<span style="color: #409EFF; cursor: pointer;">${preview}</span>`;
        }
      },
      { field: "elapsedMs", title: "耗时(ms)", width: 100, sortable: true },
      { field: "createDate", title: "调用时间", width: 160, sortable: true },
      { field: "creator", title: "操作人", width: 100 }
Code/WMS/WIDESEA_WMSServer/WIDESEA_Core/Filter/ApiAuthorizeFilter.cs
@@ -84,7 +84,7 @@
                int ExpMinutes = AppSettings.Get("ExpMinutes").ObjToInt();
                if ((expDate.GetValueOrDefault() - DateTime.Now).TotalMinutes < ExpMinutes / 3 && context.HttpContext.Request.Path != ReplaceTokenPath)
                {
                    context.HttpContext.Response.Headers.Add("widesea_exp", "1");
                    context.HttpContext.Response.Headers.Append("widesea_exp", "1");
                }
            }
Code/WMS/WIDESEA_WMSServer/WIDESEA_Core/Middlewares/HttpRequestMiddleware.cs
@@ -18,7 +18,7 @@
        public async Task InvokeAsync(HttpContext context)
        {
            context.Response.Headers.Add("Access-Control-Expose-Headers", "widesea_exp");
            context.Response.Headers.Append("Access-Control-Expose-Headers", "widesea_exp");
            await _next(context);
        }
    }
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockInfoController.cs
@@ -113,9 +113,9 @@
                            ? _mesService.InboundInContainer(mesRequest)
                            : _mesService.InboundInContainer(mesRequest, token);
                        return (
                            result?.IsSuccess ?? false,
                            result.Data?.IsSuccess ?? false,
                            System.Text.Json.JsonSerializer.Serialize(result),
                            result?.ErrorMessage ?? "未知错误"
                            result?.Data?.Msg ?? result?.ErrorMessage ?? "未知错误"
                        );
                    },
                    App.User.UserName);
@@ -203,9 +203,9 @@
                            ? _mesService.OutboundInContainer(mesRequest)
                            : _mesService.OutboundInContainer(mesRequest, token);
                        return (
                            result?.IsSuccess ?? false,
                            result?.Data?.IsSuccess ?? false,
                            System.Text.Json.JsonSerializer.Serialize(result),
                            result?.ErrorMessage ?? "未知错误"
                            result?.Data?.Msg ?? result?.ErrorMessage ?? "未知错误"
                        );
                    },
                    App.User.UserName);
Code/WMS/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Stock/StockInfoDetailController.cs
@@ -1,14 +1,13 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc;
using WIDESEA_Common.Constants;
using WIDESEA_Common.StockEnum;
using WIDESEA_Core;
using WIDESEA_Core.BaseController;
using WIDESEA_DTO.MES;
using WIDESEA_IStockService;
using WIDESEA_IBasicService;
using WIDESEA_IStockService;
using WIDESEA_ISystemService;
using WIDESEA_Model.Models;
using WIDESEA_Common.Constants;
using WIDESEA_Common.StockEnum;
namespace WIDESEA_WMSServer.Controllers.Stock
{
@@ -52,19 +51,19 @@
            try
            {
                // 1. 参数验证
                if (dto.SfcList == null || !dto.SfcList.Any())
                {
                    return response.Error("电芯码列表不能为空");
                }
                //// 1. 参数验证
                //if (dto.SfcList == null || !dto.SfcList.Any())
                //{
                //    return response.Error("电芯码列表不能为空");
                //}
                // 2. 验证电芯状态(非'已锁定'状态允许绑定)
                var stockDetail = await Service.Repository.QueryFirstAsync(x => dto.SfcList.Contains(x.SerialNumber));
                if (stockDetail != null && stockDetail.Status == 99)
                {
                    return response.Error("当前库存明细包含已锁定状态,不允许执行绑定操作");
                }
                var stockInfo = await _stockInfoService.Repository.QueryFirstAsync(x => stockDetail.StockId == x.Id);
                //var stockDetail = await Service.Repository.QueryFirstAsync(x => dto.SfcList.Contains(x.SerialNumber));
                //if (stockDetail != null && stockDetail.Status == 99)
                //{
                //    return response.Error("当前库存明细包含已锁定状态,不允许执行绑定操作");
                //}
                var stockInfo = await _stockInfoService.Repository.QueryDataNavFirstAsync(x => x.PalletCode == dto.PalletCode);
                // 3. 动态获取MES凭证
                var mesConfig = _mesDeviceConfigService.GetByDeviceName("组盘机械手");
@@ -79,10 +78,10 @@
                    ResourceCode = resourceCode,
                    LocalTime = DateTime.Now,
                    ContainerCode = stockInfo.PalletCode,
                    ContainerSfcList = dto.SfcList.Select(sfc => new ContainerSfcItem
                    ContainerSfcList = stockInfo.Details.Select(sfc => new ContainerSfcItem
                    {
                        Sfc = sfc,
                        Location = dto.Location ?? ""
                        Sfc = sfc.SerialNumber,
                        Location = sfc.InboundOrderRowNo.ToString() ?? ""
                    }).ToList(),
                    OperationType = dto.OperationType
                };
@@ -101,9 +100,9 @@
                            ? _mesService.BindContainer(mesRequest)
                            : _mesService.BindContainer(mesRequest, token);
                        return (
                            result?.IsSuccess ?? false,
                            result?.Data?.IsSuccess ?? false,
                            System.Text.Json.JsonSerializer.Serialize(result),
                            result?.ErrorMessage ?? "未知错误"
                            result?.Data?.Msg ?? result?.ErrorMessage ?? "未知错误"
                        );
                    },
                    App.User.UserName);
@@ -130,18 +129,18 @@
            try
            {
                // 1. 参数验证
                if (dto.SfcList == null || !dto.SfcList.Any())
                {
                    return response.Error("电芯码列表不能为空");
                }
                //if (dto.SfcList == null || !dto.SfcList.Any())
                //{
                //    return response.Error("电芯码列表不能为空");
                //}
                // 2. 验证电芯状态(非'已锁定'状态允许解绑)
                var stockDetail = await Service.Repository.QueryFirstAsync(x => dto.SfcList.Contains(x.SerialNumber));
                if (stockDetail != null && stockDetail.Status == 99)
                {
                    return response.Error("当前库存明细包含已锁定状态,不允许执行解绑操作");
                }
                var stockInfo = await _stockInfoService.Repository.QueryFirstAsync(x => stockDetail.StockId == x.Id);
                //// 2. 验证电芯状态(非'已锁定'状态允许解绑)
                //var stockDetail = await Service.Repository.QueryFirstAsync(x => dto.SfcList.Contains(x.SerialNumber));
                //if (stockDetail != null && stockDetail.Status == 99)
                //{
                //    return response.Error("当前库存明细包含已锁定状态,不允许执行解绑操作");
                //}
                var stockInfo = await _stockInfoService.Repository.QueryDataNavFirstAsync(x => dto.PalletCode == x.PalletCode);
                // 3. 动态获取MES凭证
                var mesConfig = _mesDeviceConfigService.GetByDeviceName("组盘机械手");
@@ -156,7 +155,7 @@
                    ResourceCode = resourceCode,
                    LocalTime = DateTime.Now,
                    ContainCode = stockInfo.PalletCode,
                    SfcList = dto.SfcList
                    SfcList = stockInfo.Details.Select(x => x.SerialNumber).ToList(),
                };
                string requestJson = System.Text.Json.JsonSerializer.Serialize(mesRequest);
@@ -173,9 +172,9 @@
                            ? _mesService.UnBindContainer(mesRequest)
                            : _mesService.UnBindContainer(mesRequest, token);
                        return (
                            result?.IsSuccess ?? false,
                            result?.Data?.IsSuccess ?? false,
                            System.Text.Json.JsonSerializer.Serialize(result),
                            result?.ErrorMessage ?? "未知错误"
                            result?.Data?.Msg ?? result?.ErrorMessage ?? "未知错误"
                        );
                    },
                    App.User.UserName);
@@ -315,4 +314,4 @@
            return defaultValue;
        }
    }
}
}