shgx_tz_mom/ZR.Service/mes/wms-u8/ERP_WMS_interactiveService.cs
赵正易 1be5945f2f feat(wms-u8): 添加物料编码聚合功能并优化出库订单匹配逻辑
在ERP_WMS_interactiveService中添加物料编码聚合功能,通过配置控制是否启用
优化WmOutOrderService中的物料匹配逻辑,支持处理多个零件号
2025-08-08 16:04:25 +08:00

372 lines
14 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Infrastructure;
using Infrastructure.Attribute;
using Newtonsoft.Json;
using NLog;
using System;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
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<ERP_WMS_interactiveModelQuery> models)
{
return ProcessSyncRequest(urlBase, models, "inbounded", isInbound: true);
}
public ERP_WMS_interactiveModelResult Outbounded(string urlBase, List<ERP_WMS_interactiveModelQuery> models)
{
return ProcessSyncRequest(urlBase, models, "outbounded", isInbound: false);
}
/// <summary>
/// 根据物料编码聚合模型数据
/// </summary>
/// <param name="models">原始模型列表</param>
/// <returns>聚合后的模型列表</returns>
private List<ERP_WMS_interactiveModelQuery> AggregateModelsByMaterialCode(List<ERP_WMS_interactiveModelQuery> 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<ERP_WMS_interactiveModelResult> InboundedAsync(string urlBase, List<ERP_WMS_interactiveModelQuery> models)
{
return await ProcessAsyncRequest(urlBase, models, "inbounded", isInbound: true);
}
public async Task<ERP_WMS_interactiveModelResult> OutboundedAsync(string urlBase, List<ERP_WMS_interactiveModelQuery> models)
{
return await ProcessAsyncRequest(urlBase, models, "outbounded", isInbound: false);
}
#endregion
#region
/// <summary>
/// 同步请求处理(抽取公共逻辑)
/// </summary>
private ERP_WMS_interactiveModelResult ProcessSyncRequest(string urlBase, List<ERP_WMS_interactiveModelQuery> 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);
}
}
/// <summary>
/// 异步请求处理(抽取公共逻辑)
/// </summary>
// 配置项:控制是否启用物料编码聚合功能
private static bool EnableMaterialAggregation = true; // 默认为false不启用聚合
private async Task<ERP_WMS_interactiveModelResult> ProcessAsyncRequest(string urlBase, List<ERP_WMS_interactiveModelQuery> 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. 根据配置决定是否聚合数据
List<ERP_WMS_interactiveModelQuery> 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
/// <summary>
/// 构建URL避免双斜杠问题并添加查询字符串参数
/// </summary>
private string BuildUrl(string urlBase, string action, List<ERP_WMS_interactiveModelQuery> 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;
}
/// <summary>
/// 构建请求参数(原构建请求头的逻辑)
/// </summary>
private Dictionary<string, string> 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<string, string>
{
{ "appid", appid },
{ "timestamp", timestamp },
{ "sign", sign },
};
}
/// <summary>
/// 基础参数校验
/// </summary>
private bool ValidateBaseParams(string urlBase, List<ERP_WMS_interactiveModelQuery> 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;
}
/// <summary>
/// 处理同步响应假设HttpHelper返回已反序列化对象需根据实际情况调整
/// </summary>
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;
}
/// <summary>
/// 处理异步响应先校验JSON格式
/// </summary>
private async Task<ERP_WMS_interactiveModelResult> 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<ERP_WMS_interactiveModelResult>(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}"
};
}
}
/// <summary>
/// 异常处理(细化异常类型)
/// </summary>
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
};
}
/// <summary>
/// 验证JSON格式是否有效
/// </summary>
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
}
}