wanshenmean
14 小时以前 1330eff40e79aecc1722402786b4aa52cc1b574f
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs
@@ -4,6 +4,7 @@
using SqlSugar;
using System.DirectoryServices.Protocols;
using System.Text.Json;
using WIDESEA_Common.Constants;
using WIDESEA_Common.LocationEnum;
using WIDESEA_Common.StockEnum;
using WIDESEA_Common.TaskEnum;
@@ -18,7 +19,10 @@
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;
using WIDESEA_ITaskInfoService;
using WIDESEA_Model.Models;
@@ -37,6 +41,9 @@
        private readonly ITask_HtyService _task_HtyService;
        private readonly IStockInfo_HtyService _stockInfo_HtyService;
        private readonly IUnitOfWorkManage _unitOfWorkManage;
        private readonly IRecordService _recordService;
        private readonly IMESDeviceConfigService _mesDeviceConfigService;
        private readonly IMesLogService _mesLogService;
        public IRepository<Dt_Task> Repository => BaseDal;
@@ -60,7 +67,10 @@
            IMesService mesService,
            ITask_HtyService task_HtyService,
            IStockInfo_HtyService stockInfo_HtyService,
            IUnitOfWorkManage unitOfWorkManage) : base(BaseDal)
            IUnitOfWorkManage unitOfWorkManage,
            IRecordService recordService,
            IMESDeviceConfigService mesDeviceConfigService,
            IMesLogService mesLogService) : base(BaseDal)
        {
            _mapper = mapper;
            _stockInfoService = stockInfoService;
@@ -72,7 +82,198 @@
            _task_HtyService = task_HtyService;
            _stockInfo_HtyService = stockInfo_HtyService;
            _unitOfWorkManage = unitOfWorkManage;
            _recordService = recordService;
            _mesDeviceConfigService = mesDeviceConfigService;
            _mesLogService = mesLogService;
        }
        /// <summary>
        /// 查找托盘是否存在未完成任务。
        /// </summary>
        private async Task<WebResponseContent> GetTaskByPalletCodeAsync(string palletCode)
        {
            try
            {
                var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == palletCode);
                if (task == null)
                    return WebResponseContent.Instance.Error("未找到对应的任务");
                var taskDto = _mapper.Map<WMSTaskDTO>(task);
                return WebResponseContent.Instance.OK("查询成功", taskDto);
            }
            catch (Exception ex)
            {
                return WebResponseContent.Instance.Error($"查询任务失败: {ex.Message}");
            }
        }
        /// <summary>
        /// 保存任务历史。
        /// </summary>
        private async Task<WebResponseContent> SaveTaskHistoryAsync(Dt_Task task, string operateType)
        {
            var historyTask = _mapper.Map<Dt_Task_Hty>(task);
            historyTask.InsertTime = DateTime.Now;
            historyTask.OperateType = operateType;
            var saved = await _task_HtyService.Repository.AddDataAsync(historyTask) > 0;
            return saved
                ? WebResponseContent.Instance.OK()
                : WebResponseContent.Instance.Error("任务历史保存失败");
        }
        /// <summary>
        /// 保存库存历史。
        /// </summary>
        private async Task<WebResponseContent> SaveStockHistoryAsync(Dt_StockInfo stockInfo, string operateType)
        {
            var historyStock = _mapper.Map<Dt_StockInfo_Hty>(stockInfo);
            historyStock.InsertTime = DateTime.Now;
            historyStock.OperateType = operateType;
            var saved = await _stockInfo_HtyService.Repository.AddDataAsync(historyStock) > 0;
            return saved
                ? WebResponseContent.Instance.OK()
                : WebResponseContent.Instance.Error("库存历史保存失败");
        }
        /// <summary>
        /// 完成任务后统一处理。
        /// </summary>
        private async Task<WebResponseContent> CompleteTaskAsync(Dt_Task task, string operateType = "")
        {
            var deleteTaskResult = await BaseDal.DeleteDataAsync(task);
            if (!deleteTaskResult)
                return WebResponseContent.Instance.Error("任务完成失败");
            return await SaveTaskHistoryAsync(task, operateType);
        }
        /// <summary>
        /// 根据巷道确定目标地址,支持多出库口轮询。
        /// </summary>
        private string DetermineTargetAddress(string roadway, Dictionary<string, List<string>> addressMap)
        {
            if (string.IsNullOrWhiteSpace(roadway))
                return TaskAddressConstants.DEFAULT_ADDRESS;
            string? matchedPrefix = null;
            foreach (var kvp in addressMap)
            {
                if (roadway.Contains(kvp.Key))
                {
                    matchedPrefix = kvp.Key;
                    break;
                }
            }
            if (matchedPrefix == null)
                return TaskAddressConstants.DEFAULT_ADDRESS;
            if (!addressMap.TryGetValue(matchedPrefix, out var addresses) || addresses == null || addresses.Count == 0)
                return TaskAddressConstants.DEFAULT_ADDRESS;
            if (addresses.Count == 1)
                return addresses[0];
            return _roundRobinService.GetNextAddress(matchedPrefix, addresses);
        }
        /// <summary>
        /// 根据库存 Remark 确定目标地址(GW_1→[11001,11010]轮询,GW_2→CWSC1,CW_1→22001)。
        /// </summary>
        private string DetermineTargetAddressByRemark(string remark, string roadway, Dictionary<string, List<string>> addressMap)
        {
            // 根据 Remark 确定目标地址
            if (!string.IsNullOrWhiteSpace(remark))
            {
                return remark switch
                {
                    StockRemarkConstants.GW1 => _roundRobinService.GetNextAddress(StockRemarkConstants.GW1, TaskAddressConstants.GW1_ADDRESSES.ToList()),
                    StockRemarkConstants.GW2 => TaskAddressConstants.GW2_ADDRESS,
                    StockRemarkConstants.CW1 => TaskAddressConstants.CW1_ADDRESS,
                    _ => DetermineTargetAddress(roadway, addressMap)
                };
            }
            // Remark 为空时,回退到巷道配置
            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
            {
                // 日志记录失败不影响主流程
            }
        }
    }
}