xiazhengtongxue
23 小时以前 595b44f79c7580b3d95c63194b1df4f00600f926
Code/WMS/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_AGV.cs
@@ -1,5 +1,6 @@
using Mapster;
using System.Text.Json;
using WIDESEA_Common.Constants;
using WIDESEA_Common.LocationEnum;
using WIDESEA_Common.StockEnum;
using WIDESEA_Common.TaskEnum;
@@ -8,19 +9,19 @@
using WIDESEA_Core.Enums;
using WIDESEA_Core.Helper;
using WIDESEA_DTO.Task;
using WIDESEA_IStockService;
using WIDESEA_Model.Models;
namespace WIDESEA_TaskInfoService
{
    public partial class TaskService
    {
        #region 极卷库任务模块
        public string AGV_OutTaskComplete = WIDESEA_Core.Helper.AppSettings.Configuration["AGV_OutTaskComplete"];
        public string WCS_ReceiveTask = WIDESEA_Core.Helper.AppSettings.Configuration["WCS_ReceiveTask"];
        /// <summary>
        /// 出入库申请。
        /// 极卷库出入库申请
        /// </summary>
        public async Task<AGVResponse> ApplyInOutAsync(ApplyInOutDto applyInOutDto)
        {
@@ -32,7 +33,7 @@
                if (validationMessage != null)
                    return response.Error(validationMessage);
                var existingTask = await BaseDal.QueryFirstAsync(x => x.PalletCode == applyInOutDto.TrayNumber);
                var existingTask = await BaseDal.QueryFirstAsync(x => x.PalletCode == applyInOutDto.TrayNumber || x.OrderNo == applyInOutDto.TaskId);
                if (existingTask != null)
                    return response.Error($"WMS已有当前任务,不可重复下发,任务号:{applyInOutDto.TaskId}");
@@ -61,7 +62,7 @@
        }
        /// <summary>
        /// 手动出库完成反馈给AGV。
        /// 手动出库完成反馈给AGV
        /// </summary>
        public async Task<WebResponseContent> OutTaskComplete(OutTaskCompleteDto outTaskCompleteDto)
        {
@@ -83,7 +84,7 @@
                    return response.Error(httpResponse?.Msg ?? "AGV接口调用异常");
                if (!httpResponse.Code)
                    return response.Error(string.IsNullOrWhiteSpace(httpResponse.Msg) ? "AGV接口调用失败" : httpResponse.Msg);
                        return response.Error(string.IsNullOrWhiteSpace(httpResponse.Msg) ? "AGV接口调用失败" : httpResponse.Msg);
                var syncResult = await CompleteLocalOutboundAfterAgvAckAsync(task);
                return syncResult.Status ? response.OK(httpResponse.Msg) : syncResult;
@@ -96,7 +97,7 @@
        }
        /// <summary>
        /// 输送线申请进入。
        /// 输送线申请进入
        /// </summary>
        public async Task<AGVResponse> ApplyEnterAsync(ApplyEnterDto applyEnterDto)
        {
@@ -124,7 +125,7 @@
        }
        /// <summary>
        /// 取放货完成。
        /// 取放货完成
        /// </summary>
        public async Task<AGVResponse> TaskCompleteAsync(TaskCompleteDto taskCompleteDto)
        {
@@ -152,7 +153,7 @@
        }
        /// <summary>
        /// 任务取消。
        /// 任务取消
        /// </summary>
        public async Task<AGVResponse> TaskCancelAsync(TaskCancelDto taskCancelDto)
        {
@@ -169,7 +170,7 @@
                    return response.OK();
                if (task.TaskStatus == (int)TaskInStatusEnum.InNew)
                    return CancelAgvInboundTask(task);
                    return await CancelAgvInboundTask(task);
                if (task.TaskStatus == (int)TaskOutStatusEnum.OutNew)
                    return await CancelAgvOutboundTaskAsync(task);
@@ -183,6 +184,7 @@
            }
        }
        #region 参数验证
        private static string? ValidateApplyInOutRequest(ApplyInOutDto dto)
        {
            if (dto == null) return "请求参数不能为空";
@@ -231,7 +233,10 @@
            if (string.IsNullOrWhiteSpace(dto.TaskId)) return "任务号不能为空";
            return null;
        }
        #endregion 参数验证
        #region 具体实现
        // 出入库共用创建任务
        private Dt_Task BuildAgvTask(ApplyInOutDto dto)
        {
            var task = new Dt_Task
@@ -265,6 +270,7 @@
            return task;
        }
        // 构建返回AGV出入库请求体
        private AGVDataDto BuildAgvDataDto(Dt_Task task, ApplyInOutDto dto)
        {
            return new AGVDataDto
@@ -287,6 +293,7 @@
            };
        }
        // 入库创建
        private async Task<AGVResponse?> CreateAgvInboundTaskAsync(Dt_Task task, ApplyInOutDto dto)
        {
            AGVResponse response = new AGVResponse();
@@ -294,39 +301,132 @@
            if (stockInfo != null)
                return response.Error($"当前托盘{dto.TrayNumber}已经入库了");
            // 创建库存明细
            var details = new Dt_StockInfoDetail
            {
                MaterielCode = dto.ProductNo,
                MaterielName = dto.ProductName,
                StockQuantity = int.TryParse(dto.Quantity, out int quantity) ? quantity : 0,
                Unit = dto.UomCode,
                OrderNo = dto.ProductNo,
                ProductionDate =dto.ProductionDate,
                EffectiveDate = dto.LowerLimitTime,
                SerialNumber = dto.TrayNumber,
                Status = (int)StockStatusEmun.入库确认,
                InboundOrderRowNo = 1,
                Creater = StockConstants.AGV_USER,
                CreateDate = DateTime.Now,
                Remark = $"AGV入库任务创建,任务号:{dto.TaskId}"
            };
            // 创建库存主记录
            var stock = new Dt_StockInfo
            {
                PalletCode = dto.TrayNumber,
                PalletType = dto.Floor,
                WarehouseId = dto.YinYang == 1 ? (int)WarehouseEnum.FJ1 : (int)WarehouseEnum.ZJ1,
                StockStatus = (int)StockStatusEmun.入库确认,
                Creater = StockConstants.AGV_USER,
                CreateDate = DateTime.Now,
                Remark = $"AGV入库任务创建,任务号:{dto.TaskId}",
                Details = new List<Dt_StockInfoDetail> { details }
            };
            task.TaskType = (int)TaskInboundTypeEnum.Inbound;
            task.TaskStatus = (int)TaskInStatusEnum.InNew;
            task.CurrentAddress = task.SourceAddress;
            var addResult = await BaseDal.AddDataAsync(task) > 0;
            return addResult ? null : response.Error("入库任务创建失败");
            _unitOfWorkManage.BeginTran();
            try
            {
                // 先创建任务
                var taskResult = await BaseDal.AddDataAsync(task) > 0;
                if (!taskResult)
                {
                    _unitOfWorkManage.RollbackTran();
                    return response.Error("入库任务创建失败");
                }
                var result = _stockInfoService.Repository.AddData(stock, x => x.Details);
                if (result)
                {
                    _unitOfWorkManage.CommitTran();
                    return null;
                }
                else
                {
                    _unitOfWorkManage.RollbackTran();
                    return response.Error("库存信息创建失败");
                }
                // 使用库存服务添加库存主记录和明细
                //var stockResult = await _stockInfoService.AddStockWithDetailsUsingTransactionAsync(stock);
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                return response.Error($"入库任务创建异常:{ex.Message}");
            }
        }
        // 出库创建
        private async Task<AGVResponse?> CreateAgvOutboundTaskAsync(Dt_Task task, ApplyInOutDto dto)
        {
            AGVResponse response = new AGVResponse();
            // 检查库存是否存在
            var stockInfo = await _stockInfoService.GetStockInfoAsync(dto.TrayNumber);
            if (stockInfo == null)
                return response.Error($"未找到托盘{dto.TrayNumber}的库存信息");
            if (stockInfo.WarehouseId != dto.YinYang)
                return response.Error($"托盘{dto.TrayNumber}不属于当前{(dto.YinYang == 1 ? "阴极" : "阳极")}");
            // 检查库存是否有明细(即是否真的有库存)
            if (stockInfo.Details == null || !stockInfo.Details.Any())
                return response.Error($"托盘{dto.TrayNumber}没有库存明细,无法出库");
            // 检查库存总数量是否大于0
            var totalQuantity = stockInfo.Details.Sum(d => d.StockQuantity);
            if (totalQuantity <= 0)
                return response.Error($"托盘{dto.TrayNumber}库存数量不足,无法出库");
            // 根据dto参数进一步验证库存信息
            if (!string.IsNullOrEmpty(dto.ProductNo))
            {
                // 检查库存明细中是否包含指定的物料编码
                var hasMatchingMaterial = stockInfo.Details.Any(d => d.MaterielCode == dto.ProductNo);
                if (!hasMatchingMaterial)
                    return response.Error($"托盘{dto.TrayNumber}中没有物料编码为{dto.ProductNo}的库存,无法出库");
            }
            // 检查库存状态是否允许出库
            if (stockInfo.StockStatus != (int)StockStatusEmun.入库完成)
                return response.Error($"托盘{dto.TrayNumber}正在移动中,请稍后!");
            var locationInfo = await _locationInfoService.GetLocationInfoAsync(stockInfo.LocationCode);
            // 检查货位信息
            var locationInfo = await _locationInfoService.GetLocationInfo(stockInfo.LocationCode);
            if (locationInfo == null)
                return response.Error($"未找到托盘{stockInfo.LocationCode}的货位信息");
            // 检查货位状态是否允许出库
            if (locationInfo.LocationStatus != (int)LocationStatusEnum.InStock)
                return response.Error($"当前货位{locationInfo.LocationStatus}状态信息错误");
            // 验证仓库ID是否匹配(根据dto的阴阳极参数)
            var expectedWarehouseId = dto.YinYang == 1 ? (int)WarehouseEnum.FJ1 : (int)WarehouseEnum.ZJ1;
            if (stockInfo.WarehouseId != expectedWarehouseId)
                return response.Error($"托盘{dto.TrayNumber}不在预期的仓库中,无法出库");
            task.TaskType = (int)TaskOutboundTypeEnum.Outbound;
            task.TaskStatus = (int)TaskOutStatusEnum.OutNew;
            task.SourceAddress = stockInfo.LocationCode;
            task.CurrentAddress = stockInfo.LocationCode;
            task.TargetAddress = dto.YinYang == 1 ? "D10020" : "D10090";
            var wmsTaskDto = _mapper.Map<WMSTaskDTO>(task);
            var taskList = new List<WMSTaskDTO> { wmsTaskDto };
            var requestBody = JsonSerializer.Serialize(taskList);
            var httpResponse = _httpClientHelper.Post<WebResponseContent>(WCS_ReceiveTask, requestBody);
            if (httpResponse == null || httpResponse.Data == null || !httpResponse.Data.Status)
                return response.Error(httpResponse?.Data?.Message ?? "下发WCS失败");
            stockInfo.StockStatus = (int)StockStatusEmun.出库锁定;
            locationInfo.LocationStatus = (int)LocationStatusEnum.InStockLock;
@@ -340,34 +440,22 @@
                _unitOfWorkManage.RollbackTran();
                return response.Error("出库任务创建失败");
            }
            _unitOfWorkManage.CommitTran();
            return null;
        }
        private async Task<WebResponseContent> CompleteLocalOutboundAfterAgvAckAsync(Dt_Task task)
        {
            var stockInfo = await _stockInfoService.GetStockInfoAsync(task.PalletCode);
            if (stockInfo == null)
                return WebResponseContent.Instance.Error($"未找到托盘{task.PalletCode}的库存信息");
            var locationInfo = await _locationInfoService.GetLocationInfoAsync(stockInfo.LocationCode);
            if (locationInfo == null)
                return WebResponseContent.Instance.Error($"未找到托盘{stockInfo.LocationCode}的货位信息");
            if (stockInfo.StockStatus != (int)StockStatusEmun.出库锁定 || locationInfo.LocationStatus != (int)LocationStatusEnum.InStockLock)
                return WebResponseContent.Instance.Error($"当前库存{stockInfo.StockStatus}或者货位{locationInfo.LocationStatus}状态信息错误");
            locationInfo.LocationStatus = (int)LocationStatusEnum.Free;
            task.TaskStatus = (int)TaskOutStatusEnum.Line_OutFinish;
            _unitOfWorkManage.BeginTran();
            var deleteTaskResult = BaseDal.DeleteData(task);
            var updateLocationResult = _locationInfoService.UpdateData(locationInfo);
            var deleteStockResult = _stockInfoService.DeleteData(stockInfo);
            if (!deleteTaskResult || !updateLocationResult.Status || !deleteStockResult.Status)
            var updateResult = BaseDal.UpdateData(task);
            if (!updateResult)
            {
                _unitOfWorkManage.RollbackTran();
                return WebResponseContent.Instance.Error("AGV完成回传后,本地任务/库存/货位更新失败");
                return WebResponseContent.Instance.Error("AGV完成回传后,任务更新失败");
            }
            _unitOfWorkManage.CommitTran();
@@ -376,11 +464,62 @@
        private bool CanApplyEnter(Dt_Task task, ApplyEnterDto dto)
        {
            return dto.InOut == 1
                ? task.TaskType == (int)TaskInboundTypeEnum.Inbound && task.TaskStatus == (int)TaskInStatusEnum.InNew
                : task.TaskType == (int)TaskOutboundTypeEnum.Outbound && task.TaskStatus == (int)TaskStatusEnum.Line_Finish;
            if (dto.InOut == 1)
            {
                var hasExecutingOutTask = BaseDal.QueryFirst(x => x.TaskType == (int)TaskOutboundTypeEnum.Outbound
                    && x.TargetAddress == task.SourceAddress
                    && (x.TaskStatus == (int)TaskOutStatusEnum.SC_OutExecuting
                        || x.TaskStatus == (int)TaskOutStatusEnum.Line_OutExecuting));
                // 如果没有正在执行的出库任务,则允许入库
                return hasExecutingOutTask == null;
            }
            else
            {
                return task.TaskType == (int)TaskOutboundTypeEnum.Outbound
                    && task.TaskStatus == (int)TaskStatusEnum.Line_Finish;
            }
        }
        // WCS入库完成
        private async Task<WebResponseContent> CompleteAgvInboundTaskAsync(CreateTaskDto taskDto)
        {
            WebResponseContent response = new WebResponseContent();
            var task = await BaseDal.QueryFirstAsync(x => x.PalletType == taskDto.PalletType);
            if (task == null)
                return response.Error($"没有当前托盘{taskDto.PalletType}入库任务");
            var stockInfo = await _stockInfoService.GetStockInfoAsync(task.PalletCode);
            if (stockInfo == null)
                return response.Error($"未找到托盘{task.PalletCode}的库存信息");
            var locationInfo = await _locationInfoService.GetLocationInfoAsync(task.TargetAddress);
            if (locationInfo == null)
                return response.Error($"未找到货位{task.TargetAddress}的信息");
            if (locationInfo.LocationStatus == (int)LocationStatusEnum.InStock)
                return response.Error($"当前货位{locationInfo.LocationStatus}状态不是空闲状态,无法入库");
            // 更新货位状态为占用
            locationInfo.LocationStatus = (int)LocationStatusEnum.InStock;
            task.TaskStatus = (int)TaskInStatusEnum.InFinish;
            stockInfo.StockStatus = (int)StockStatusEmun.入库完成;
            _unitOfWorkManage.BeginTran();
            var addStockResult = _stockInfoService.UpdateData(stockInfo);
            var updateLocationResult = _locationInfoService.UpdateData(locationInfo);
            BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成);
            if (!addStockResult.Status || !updateLocationResult.Status)
            {
                _unitOfWorkManage.RollbackTran();
                return response.Error("入库完成后,添加库存或货位更新失败");
            }
            _unitOfWorkManage.CommitTran();
            return response.OK();
        }
        // AGV出库完成
        private async Task<AGVResponse> CompleteAgvOutboundTaskAsync(Dt_Task task)
        {
            AGVResponse response = new AGVResponse();
@@ -399,10 +538,11 @@
            task.TaskStatus = (int)TaskOutStatusEnum.OutFinish;
            _unitOfWorkManage.BeginTran();
            var deleteStockResult = _stockInfoService.DeleteData(stockInfo);
            var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(locationInfo);
            //var deleteStockResult = _stockInfoService.DeleteData(stockInfo)
            var deleteStockResult = await _stockInfoService.DeleteStockWithDetailsAsync(stockInfo.Id);
            var updateLocationResult = _locationInfoService.UpdateData(locationInfo);
            BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成);
            if (!deleteStockResult.Status || !updateLocationResult)
            if (!deleteStockResult.Status || !updateLocationResult.Status)
            {
                _unitOfWorkManage.RollbackTran();
                return response.Error("出库完成后,本地库存或货位更新失败");
@@ -412,6 +552,7 @@
            return response.OK();
        }
        // AGV已放货,准备输送线入库
        private async Task<AGVResponse> CompleteAgvInboundTaskAsync(Dt_Task task)
        {
            AGVResponse response = new AGVResponse();
@@ -421,12 +562,15 @@
            task.TargetAddress = availableLocation.LocationCode;
            var wcsTaskDto = _mapper.Map<WCSTaskDTO>(task);
            var httpResponse = _httpClientHelper.Post<WebResponseContent>(WCS_ReceiveTask, JsonSerializer.Serialize(wcsTaskDto));
            var wmsTaskDto = _mapper.Map<WMSTaskDTO>(task);
            var taskList = new List<WMSTaskDTO> { wmsTaskDto };
            var requestBody = JsonSerializer.Serialize(taskList);
            var httpResponse = _httpClientHelper.Post<WebResponseContent>(WCS_ReceiveTask, requestBody);
            if (httpResponse == null || httpResponse.Data == null || !httpResponse.Data.Status)
                return response.Error(httpResponse?.Data?.Message ?? "下发WCS失败");
            task.TaskStatus = (int)TaskStatusEnum.Line_Executing;
            task.TaskStatus = (int)TaskInStatusEnum.Line_InExecuting;
            task.Dispatchertime = DateTime.Now;
            var locationInfo = await _locationInfoService.GetLocationInfoAsync(task.TargetAddress);
@@ -436,23 +580,27 @@
            if (locationInfo.LocationStatus != (int)LocationStatusEnum.Free)
                return response.Error($"当前货位{locationInfo.LocationStatus}状态信息错误");
            Dt_StockInfo stockInfo = new Dt_StockInfo
            {
                PalletCode = task.PalletCode,
                StockStatus = (int)StockStatusEmun.入库确认,
                LocationCode = locationInfo.LocationCode,
                WarehouseId = task.WarehouseId,
                Creater = "AGV",
                CreateDate = DateTime.Now
            };
            var existingStock = await _stockInfoService.GetStockInfoAsync(task.PalletCode);
            if (existingStock != null)
                return response.Error($"托盘{task.PalletCode}的库存信息已存在,请勿重复入库");
            //Dt_StockInfo stockInfo = new Dt_StockInfo
            //{
            //    PalletCode = task.PalletCode,
            //    StockStatus = (int)StockStatusEmun.入库确认,
            //    LocationCode = locationInfo.LocationCode,
            //    WarehouseId = task.WarehouseId,
            //    Creater = "AGV",
            //    CreateDate = DateTime.Now
            //};
            locationInfo.LocationStatus = (int)LocationStatusEnum.FreeLock;
            _unitOfWorkManage.BeginTran();
            var updateTaskResult = BaseDal.UpdateData(task);
            var updateLocationResult = _locationInfoService.UpdateData(locationInfo);
            var addStockResult = _stockInfoService.AddData(stockInfo);
            if (!updateTaskResult || !updateLocationResult.Status || !addStockResult.Status)
            //var addStockResult = _stockInfoService.AddData(stockInfo);
            if (!updateTaskResult || !updateLocationResult.Status /*|| !addStockResult.Status*/)
            {
                _unitOfWorkManage.RollbackTran();
                return response.Error("入库完成后,本地任务、库存或货位更新失败");
@@ -462,17 +610,40 @@
            return response.OK();
        }
        private AGVResponse CancelAgvInboundTask(Dt_Task task)
        // AGV入库取消
        private async Task<AGVResponse> CancelAgvInboundTask(Dt_Task task)
        {
            AGVResponse response = new AGVResponse();
            task.TaskStatus = (int)TaskInStatusEnum.InCancel;
            _unitOfWorkManage.BeginTran();
            BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成);
            _unitOfWorkManage.CommitTran();
            return response.OK();
            try
            {
                var stockInfo = await _stockInfoService.GetStockInfoAsync(task.PalletCode);
                if (stockInfo != null)
                {
                    var deleteResult = await _stockInfoService.DeleteStockWithDetailsAsync(stockInfo.Id);
                    if (!deleteResult.Status)
                    {
                        _unitOfWorkManage.RollbackTran();
                        return response.Error($"删除库存失败: {deleteResult.Message}");
                    }
                }
                BaseDal.DeleteAndMoveIntoHty(task, App.User.UserId == 0 ? OperateTypeEnum.自动完成 : OperateTypeEnum.人工完成);
                _unitOfWorkManage.CommitTran();
                return response.OK();
            }
            catch (Exception ex)
            {
                _unitOfWorkManage.RollbackTran();
                return response.Error($"取消入库任务时发生异常: {ex.Message}");
            }
        }
        // AGV出库取消
        private async Task<AGVResponse> CancelAgvOutboundTaskAsync(Dt_Task task)
        {
            AGVResponse response = new AGVResponse();
@@ -505,6 +676,6 @@
            return response.OK();
        }
        #endregion 极卷库任务模块
        #endregion 具体实现
    }
}
}