using System; using System.Globalization; using System.Linq; using System.Net; using System.Net.Http; using System.Threading.Tasks; using Infrastructure; using Infrastructure.Attribute; using Newtonsoft.Json; using NLog; using U8Server.Util; using ZR.Model.MES.wms; using ZR.Service.mes.wms_u8.IService; namespace ZR.Service.mes.wms_u8 { [AppService(ServiceType = typeof(IERP_WMS_interactive), ServiceLifetime = LifeTime.Transient)] public class ERP_WMS_interactiveService : IERP_WMS_interactive { private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); // 假设接口密钥(需根据实际文档填写) //private const string ApiSecret = "your_api_secret"; // 替换为实际密钥 #region 同步方法 public ERP_WMS_interactiveModelResult Inbounded( string urlBase, List models ) { return ProcessSyncRequest(urlBase, models, "inbounded", isInbound: true); } public ERP_WMS_interactiveModelResult Outbounded( string urlBase, List models ) { return ProcessSyncRequest(urlBase, models, "outbounded", isInbound: false); } /// /// 根据物料编码聚合模型数据 /// /// 原始模型列表 /// 聚合后的模型列表 private List AggregateModelsByMaterialCode( List models ) { if (models == null || models.Count == 0) return models; // 按物料编码分组并计算总数量 var aggregatedModels = models .GroupBy(m => m.materialCode) .Select(g => { // 获取分组内的第一个模型 var firstModel = g.First(); return new ERP_WMS_interactiveModelQuery { customerCode = firstModel.customerCode, // 使用分组内第一个模型的客户编码 materialCode = g.Key, location = firstModel.location, // 位置留空 Qty = g.Sum(m => { // 确保数量可以转换为数字 if (decimal.TryParse(m.Qty, out decimal qty)) return qty; return 0; }) .ToString(), LotNo = firstModel.LotNo, createTime = firstModel.createTime, userID = firstModel.userID, guid = Guid.NewGuid().ToString(), lineno = firstModel.lineno, }; }) .ToList(); return aggregatedModels; } #endregion #region 异步方法 public async Task InboundedAsync( string urlBase, List models, bool isRedSingle = false ) { return await ProcessAsyncRequest(urlBase, models, "inbounded", isInbound: true, isRedSingle); } public async Task OutboundedAsync( string urlBase, List models, bool isRedSingle = false ) { return await ProcessAsyncRequest(urlBase, models, "outbounded", isInbound: false, isRedSingle); } #endregion #region 公共处理方法 /// /// 同步请求处理(抽取公共逻辑) /// private ERP_WMS_interactiveModelResult ProcessSyncRequest( string urlBase, List models, string action, bool isInbound ) { var operation = isInbound ? "入库" : "出库"; _logger.Info($"开始处理{operation}请求 - URL基础: {urlBase}, 记录数: {models?.Count ?? 0}"); // 1. 基础参数校验 if (!ValidateBaseParams(urlBase, models, operation, out var errorMsg)) { _logger.Error($"{operation}请求失败: {errorMsg}"); return null; } // 2. 构建URL和请求数据 string url = BuildUrl(urlBase, action, models); string requestData = JsonConvert.SerializeObject(models); _logger.Debug($"{operation}请求数据: {requestData}"); try { _logger.Trace($"发送{operation}同步HTTP请求 - URL: {url}"); object result = HttpHelper.HttpPost(url, requestData, "application/json", 5, null); // 4. 处理响应(同步方法假设HttpPost返回已反序列化对象,需根据实际HttpHelper调整) return ProcessSyncResponse(result, operation, url); } catch (Exception ex) { return HandleException(ex, operation, url, requestData); } } /// /// 异步请求处理(抽取公共逻辑) /// // 配置项:控制是否启用物料编码聚合功能 private static bool EnableMaterialAggregation = true; // 默认为false,不启用聚合 private async Task ProcessAsyncRequest( string urlBase, List models, string action, bool isInbound, bool isRedSingle = false ) { var operation = isInbound ? "异步入库" : "异步出库"; if(isRedSingle) { operation += "红单"; } _logger.Info($"开始处理{operation}请求 - URL基础: {urlBase}, 记录数: {models?.Count ?? 0}"); // 1. 基础参数校验 if (!ValidateBaseParams(urlBase, models, operation, out var errorMsg)) { _logger.Error($"{operation}请求失败: {errorMsg}"); return null; } // 2. 根据配置决定是否聚合数据 List processedModels = models; if (EnableMaterialAggregation) { processedModels = AggregateModelsByMaterialCode(models); _logger.Info( $"{operation}请求数据已聚合 - 聚合前记录数: {models.Count}, 聚合后记录数: {processedModels.Count}" ); } // 3. 构建URL和请求数据 string url = BuildUrl(urlBase, action, processedModels); string requestData = JsonConvert.SerializeObject(processedModels); _logger.Debug($"{operation}请求数据: {requestData}"); try { _logger.Trace($"发送{operation}异步HTTP请求 - URL: {url}"); string resultJson = await HttpHelper.HttpPostAsync( url, requestData, "application/json", 5, null ); // 4. 处理响应(先校验JSON格式,再反序列化) return await ProcessAsyncResponse(resultJson, operation, url); } catch (Exception ex) { return HandleException(ex, operation, url, requestData); } } #endregion #region 工具方法 /// /// 构建URL(避免双斜杠问题,并添加查询字符串参数) /// private string BuildUrl( string urlBase, string action, List models ) { // 移除urlBase结尾的斜杠,再拼接路径 string baseUrl = $"{urlBase.TrimEnd('/')}/wms/mes/{action}"; // 构建请求参数 string requestData = JsonConvert.SerializeObject(models); var headers = BuildHeaders(requestData); // 构建查询字符串 var queryString = string.Join( "&", headers.Select(kv => $"{WebUtility.UrlEncode(kv.Key)}={WebUtility.UrlEncode(kv.Value)}" ) ); if (!string.IsNullOrEmpty(queryString)) { baseUrl += $"?{queryString}"; } return baseUrl; } /// /// 构建请求参数(原构建请求头的逻辑) /// private Dictionary BuildHeaders(string requestData) { string timestamp = DateTime.Now.ToString( "yyyyMMddHHmmss", CultureInfo.InvariantCulture ); string appid = "gN9yId!!lfwaRoi3"; // 签名生成规则:通常为appid + timestamp + requestData + secret的MD5(需根据接口文档调整) //string signSource = $"{appid}{timestamp}{requestData}{ApiSecret}"; string sign = GetSign.GetBy16Md5(); // 修正签名生成逻辑 return new Dictionary { { "appid", appid }, { "timestamp", timestamp }, { "sign", sign }, }; } /// /// 基础参数校验 /// private bool ValidateBaseParams( string urlBase, List models, string operation, out string errorMsg ) { if (string.IsNullOrEmpty(urlBase)) { errorMsg = "基础URL为空"; return false; } if (models == null || !models.Any()) { errorMsg = "请求数据为空"; return false; } errorMsg = string.Empty; return true; } /// /// 处理同步响应(假设HttpHelper返回已反序列化对象,需根据实际情况调整) /// private ERP_WMS_interactiveModelResult ProcessSyncResponse( object result, string operation, string url ) { if (result == null) { _logger.Warn($"{operation}请求返回空结果 - URL: {url}"); return null; } if (result is ERP_WMS_interactiveModelResult modelResult) { _logger.Info( $"{operation}请求处理成功 - 结果: {modelResult.result}, 消息: {modelResult.message}" ); return modelResult; } _logger.Error($"{operation}请求返回意外类型 - 类型: {result.GetType().FullName}, URL: {url}"); return null; } /// /// 处理异步响应(先校验JSON格式) /// private async Task ProcessAsyncResponse( string resultJson, string operation, string url ) { if (string.IsNullOrEmpty(resultJson)) { _logger.Warn($"{operation}请求返回空结果 - URL: {url}"); return null; } // 检查是否为已知异常标识 if (resultJson == "异常:*") { _logger.Warn($"{operation}请求返回异常标识 - URL: {url}"); return null; } // 验证JSON格式 if (!IsValidJson(resultJson)) { _logger.Error($"{operation}请求返回非JSON数据 - URL: {url}, 响应内容: {resultJson}"); return new ERP_WMS_interactiveModelResult { result = "fail", message = $"{operation}响应格式错误,非JSON数据" }; } // 反序列化 try { var result = JsonConvert.DeserializeObject( resultJson ); if (result != null) { _logger.Info($"{operation}请求处理成功 - 结果: {result.code}, 消息: {result.message}"); return result; } else { _logger.Warn($"{operation}请求反序列化结果为空 - URL: {url}"); return null; } } catch (JsonReaderException ex) { _logger.Error(ex, $"{operation}请求JSON解析失败 - URL: {url}, 响应内容: {resultJson}"); return new ERP_WMS_interactiveModelResult { result = "fail", message = $"{operation}响应解析错误: {ex.Message}" }; } } /// /// 异常处理(细化异常类型) /// private ERP_WMS_interactiveModelResult HandleException( Exception ex, string operation, string url, string requestData ) { var errorMsg = $"{operation}处理异常"; // 区分异常类型 if (ex is HttpRequestException httpEx) { _logger.Error(httpEx, $"{operation}HTTP请求失败 - URL: {url}, 请求数据: {requestData}"); errorMsg += $": HTTP请求失败: {httpEx.Message}"; } else if (ex is JsonReaderException jsonEx) { _logger.Error(jsonEx, $"{operation}JSON解析失败 - URL: {url}, 请求数据: {requestData}"); errorMsg += $": 数据解析失败: {jsonEx.Message}"; } else { _logger.Error(ex, $"{operation}未知异常 - URL: {url}, 请求数据: {requestData}"); errorMsg += $": 未知错误: {ex.Message}"; } return new ERP_WMS_interactiveModelResult { result = "fail", message = errorMsg }; } /// /// 验证JSON格式是否有效 /// private bool IsValidJson(string json) { if (string.IsNullOrWhiteSpace(json)) return false; json = json.Trim(); // 基本格式校验:以{或[开头,以}或]结尾 if ( (json.StartsWith("{") && json.EndsWith("}")) || (json.StartsWith("[") && json.EndsWith("]")) ) { try { // 尝试解析验证 JsonConvert.DeserializeObject(json); return true; } catch { return false; } } return false; } #endregion } }