using Mapster;
|
using MapsterMapper;
|
using Microsoft.Extensions.Configuration;
|
using SqlSugar;
|
using System.Text.Json;
|
using WIDESEA_Common.LocationEnum;
|
using WIDESEA_Common.StockEnum;
|
using WIDESEA_Common.TaskEnum;
|
using WIDESEA_Core;
|
using WIDESEA_Core.BaseRepository;
|
using WIDESEA_Core.BaseServices;
|
using WIDESEA_Core.Core;
|
using WIDESEA_Core.Helper;
|
using WIDESEA_DTO.GradingMachine;
|
using WIDESEA_DTO.MES;
|
using WIDESEA_DTO.Stock;
|
using WIDESEA_DTO.Task;
|
using WIDESEA_IBasicService;
|
using WIDESEA_IStockService;
|
using WIDESEA_ITaskInfoService;
|
using WIDESEA_Model.Models;
|
|
namespace WIDESEA_TaskInfoService
|
{
|
public partial class TaskService : ServiceBase<Dt_Task, IRepository<Dt_Task>>, ITaskService
|
{
|
private readonly IMapper _mapper;
|
private readonly IStockInfoService _stockInfoService;
|
private readonly ILocationInfoService _locationInfoService;
|
private readonly HttpClientHelper _httpClientHelper;
|
private readonly IConfiguration _configuration;
|
private readonly RoundRobinService _roundRobinService;
|
private readonly IMesService _mesService;
|
private readonly ITask_HtyService _task_HtyService;
|
private readonly IStockInfo_HtyService _stockInfo_HtyService;
|
|
public IRepository<Dt_Task> Repository => BaseDal;
|
|
private readonly Dictionary<string, OrderByType> _taskOrderBy = new()
|
{
|
{ nameof(Dt_Task.Grade), OrderByType.Desc },
|
{ nameof(Dt_Task.CreateDate), OrderByType.Asc },
|
};
|
|
public List<int> TaskTypes => typeof(TaskTypeEnum).GetEnumIndexList();
|
public List<int> TaskOutboundTypes => typeof(TaskTypeEnum).GetEnumIndexList();
|
|
public TaskService(
|
IRepository<Dt_Task> BaseDal,
|
IMapper mapper,
|
IStockInfoService stockInfoService,
|
ILocationInfoService locationInfoService,
|
HttpClientHelper httpClientHelper,
|
IConfiguration configuration,
|
RoundRobinService roundRobinService,
|
IMesService mesService,
|
ITask_HtyService task_HtyService,
|
IStockInfo_HtyService stockInfo_HtyService) : base(BaseDal)
|
{
|
_mapper = mapper;
|
_stockInfoService = stockInfoService;
|
_locationInfoService = locationInfoService;
|
_httpClientHelper = httpClientHelper;
|
_configuration = configuration;
|
_roundRobinService = roundRobinService;
|
_mesService = mesService;
|
_task_HtyService = task_HtyService;
|
_stockInfo_HtyService = stockInfo_HtyService;
|
}
|
|
#region WCS逻辑处理
|
|
/// <summary>
|
/// 创建任务(组盘入库任务、空托盘回库任务)
|
/// </summary>
|
public async Task<WebResponseContent> CreateTaskInboundAsync(CreateTaskDto taskDto)
|
{
|
try
|
{
|
WebResponseContent content = await GetTaskByPalletCodeAsync(taskDto.PalletCode);
|
if (content.Status)
|
{
|
return content;
|
}
|
|
if (string.IsNullOrWhiteSpace(taskDto.PalletCode) ||
|
string.IsNullOrWhiteSpace(taskDto.Roadway))
|
{
|
return WebResponseContent.Instance.Error("无效的任务详情");
|
}
|
|
if (taskDto.TaskType != TaskTypeEnum.Inbound && taskDto.TaskType != TaskTypeEnum.InEmpty)
|
{
|
return WebResponseContent.Instance.Error("无效的任务详情");
|
}
|
|
// 使用 switch 表达式映射任务类型
|
int taskInboundType = taskDto.TaskType switch
|
{
|
TaskTypeEnum.Inbound => TaskInboundTypeEnum.Inbound.GetHashCode(),
|
TaskTypeEnum.InEmpty => TaskInboundTypeEnum.InEmpty.GetHashCode(),
|
_ => 0 // 理论上不会走到这里,因为已经验证过了
|
};
|
|
var task = new Dt_Task
|
{
|
TaskNum = await BaseDal.GetTaskNo(),
|
PalletCode = taskDto.PalletCode,
|
PalletType = taskDto.PalletType,
|
Roadway = taskDto.Roadway,
|
TaskType = taskInboundType,
|
TaskStatus = TaskInStatusEnum.InNew.GetHashCode(),
|
SourceAddress = taskDto.SourceAddress,
|
TargetAddress = taskDto.TargetAddress,
|
CurrentAddress = taskDto.SourceAddress,
|
NextAddress = taskDto.TargetAddress,
|
WarehouseId = taskDto.WarehouseId,
|
Grade = 1,
|
Creater = "system"
|
};
|
|
var result = await Repository.AddDataAsync(task) > 0;
|
if (!result) return WebResponseContent.Instance.Error("任务创建失败");
|
|
var wmstaskDto = _mapper.Map<WMSTaskDTO>(task);
|
return WebResponseContent.Instance.OK("任务创建成功", wmstaskDto);
|
}
|
catch (Exception ex)
|
{
|
return WebResponseContent.Instance.Error($"任务创建失败: {ex.Message}");
|
}
|
}
|
|
/// <summary>
|
/// 根据指定的任务详情异步创建新的出库任务
|
/// </summary>
|
public async Task<WebResponseContent> CreateTaskOutboundAsync(CreateTaskDto taskDto)
|
{
|
try
|
{
|
var stockResult = await _stockInfoService.GetStockInfoAsync(taskDto.WarehouseId);
|
if (stockResult == null || !stockResult.Any())
|
return WebResponseContent.Instance.Error("未找到库存信息");
|
|
var taskList = stockResult.Select(item => new Dt_Task
|
{
|
WarehouseId = item.WarehouseId,
|
PalletCode = item.PalletCode,
|
PalletType = item.PalletType,
|
SourceAddress = item.LocationCode,
|
TargetAddress = taskDto.TargetAddress,
|
Roadway = item.LocationDetails.RoadwayNo,
|
TaskType = TaskTypeEnum.Outbound.GetHashCode(),
|
TaskStatus = TaskStatusEnum.New.GetHashCode(),
|
Grade = 1,
|
TaskNum = 0,
|
CurrentAddress = item.LocationCode,
|
NextAddress = taskDto.TargetAddress,
|
Creater = "system",
|
}).ToList();
|
|
var result = await BaseDal.AddDataAsync(taskList) > 0;
|
var wmstaskDto = result ? _mapper.Map<WMSTaskDTO>(taskList) : null;
|
return WebResponseContent.Instance.OK(result ? "任务创建成功" : "任务创建失败", wmstaskDto ?? new object());
|
}
|
catch (Exception ex)
|
{
|
return WebResponseContent.Instance.Error($"任务创建失败: {ex.Message}");
|
}
|
}
|
|
/// <summary>
|
/// 获取可入库货位
|
/// </summary>
|
public async Task<WebResponseContent> GetTasksLocationAsync(CreateTaskDto taskDto)
|
{
|
try
|
{
|
var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode);
|
if (task == null) return WebResponseContent.Instance.Error("未找到对应的任务");
|
|
var locationInfo = await _locationInfoService.GetLocationInfo(task.Roadway);
|
if (locationInfo == null) return WebResponseContent.Instance.Error("未找到对应的货位");
|
|
return await ExecuteWithinTransactionAsync(async () =>
|
{
|
locationInfo.LocationStatus = LocationStatusEnum.FreeLock.GetHashCode();
|
task.CurrentAddress = task.SourceAddress;
|
task.NextAddress = locationInfo.LocationCode;
|
task.TargetAddress = locationInfo.LocationCode;
|
task.TaskStatus = TaskInStatusEnum.Line_InFinish.GetHashCode();
|
|
var updateTaskResult = await BaseDal.UpdateDataAsync(task);
|
var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(locationInfo);
|
if (!updateTaskResult || !updateLocationResult)
|
{
|
return WebResponseContent.Instance.Error("任务更新失败");
|
}
|
|
return WebResponseContent.Instance.OK("任务更新成功", locationInfo.LocationCode);
|
});
|
}
|
catch (Exception ex)
|
{
|
return WebResponseContent.Instance.Error($"获取任务失败: {ex.Message}");
|
}
|
}
|
|
/// <summary>
|
/// 入库任务完成:添加库存,修改货位状态,删除任务数据,添加历史任务数据
|
/// </summary>
|
public async Task<WebResponseContent> InboundFinishTaskAsync(CreateTaskDto taskDto)
|
{
|
try
|
{
|
var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode);
|
if (task == null) return WebResponseContent.Instance.Error("未找到对应的任务");
|
|
var location = await _locationInfoService.GetLocationInfo(task.Roadway, task.TargetAddress);
|
if (location == null) return WebResponseContent.Instance.Error("未找到对应的货位");
|
|
var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode);
|
if (stockInfo == null) return WebResponseContent.Instance.Error("未找到对应库存信息");
|
|
return await ExecuteWithinTransactionAsync(async () =>
|
{
|
WebResponseContent content = new WebResponseContent();
|
stockInfo.LocationCode = location.LocationCode;
|
stockInfo.LocationId = location.Id;
|
stockInfo.OutboundDate = task.Roadway switch
|
{
|
var r when r.Contains("GW") => DateTime.Now.AddHours(2),
|
var r when r.Contains("CW") => DateTime.Now.AddHours(1),
|
_ => DateTime.Now
|
};
|
stockInfo.StockStatus = StockStatusEmun.入库完成.GetHashCode();
|
|
location.LocationStatus = LocationStatusEnum.InStock.GetHashCode();
|
|
var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location);
|
var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo);
|
if (!updateLocationResult || !updateStockResult)
|
return WebResponseContent.Instance.Error("任务完成失败");
|
// 调用MES托盘进站
|
var inboundRequest = new InboundInContainerRequest
|
{
|
EquipmentCode = "STK-GROUP-001",
|
ResourceCode = "STK-GROUP-001",
|
LocalTime = DateTime.Now,
|
ContainerCode = taskDto.PalletCode
|
};
|
var inboundResult = _mesService.InboundInContainer(inboundRequest);
|
if (inboundResult == null || inboundResult.Data == null || !inboundResult.Data.IsSuccess)
|
{
|
return content.Error($"任务完成失败:MES进站失败: {inboundResult?.Data?.Msg ?? inboundResult?.ErrorMessage ?? "未知错误"}");
|
}
|
return await CompleteTaskAsync(task, "入库完成");
|
});
|
}
|
catch (Exception ex)
|
{
|
return WebResponseContent.Instance.Error($"完成任务失败: {ex.Message}");
|
}
|
}
|
|
/// <summary>
|
/// 出库任务完成 :修改库存,修改货位状态,删除任务数据,添加历史任务数据
|
/// </summary>
|
public async Task<WebResponseContent> OutboundFinishTaskAsync(CreateTaskDto taskDto)
|
{
|
try
|
{
|
var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode);
|
if (task == null) return WebResponseContent.Instance.Error("未找到对应的任务");
|
|
var location = await _locationInfoService.GetLocationInfo(task.Roadway, task.SourceAddress);
|
if (location == null) return WebResponseContent.Instance.Error("未找到对应的货位");
|
|
var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode);
|
if (stockInfo == null) return WebResponseContent.Instance.Error("未找到对应库存信息");
|
|
WebResponseContent content = new WebResponseContent();
|
return await ExecuteWithinTransactionAsync(async () =>
|
{
|
stockInfo.LocationId = 0;
|
stockInfo.LocationCode = null;
|
stockInfo.OutboundDate = DateTime.Now;
|
|
location.LocationStatus = LocationStatusEnum.Free.GetHashCode();
|
|
var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location);
|
var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo);
|
if (!updateLocationResult || !updateStockResult)
|
return WebResponseContent.Instance.Error("任务完成失败");
|
|
// 调用MES托盘出站
|
var outboundRequest = new OutboundInContainerRequest
|
{
|
EquipmentCode = "STK-GROUP-001",
|
ResourceCode = "STK-GROUP-001",
|
LocalTime = DateTime.Now,
|
ContainerCode = taskDto.PalletCode,
|
ParamList = new List<ParamItem>()
|
};
|
var outboundResult = _mesService.OutboundInContainer(outboundRequest);
|
if (outboundResult == null || outboundResult.Data == null || !outboundResult.Data.IsSuccess)
|
{
|
return content.Error($"任务完成失败:MES出站失败: {outboundResult?.Data?.Msg ?? outboundResult?.ErrorMessage ?? "未知错误"}");
|
}
|
|
return await CompleteTaskAsync(task, "出库完成");
|
});
|
}
|
catch (Exception ex)
|
{
|
return WebResponseContent.Instance.Error($"完成任务失败: {ex.Message}");
|
}
|
}
|
|
/// <summary>
|
/// 移库任务完成:修改库存位置与状态,修改源/目标货位状态,删除任务数据
|
/// </summary>
|
public async Task<WebResponseContent> RelocationFinishTaskAsync(CreateTaskDto taskDto)
|
{
|
try
|
{
|
var task = await BaseDal.QueryFirstAsync(s =>
|
s.PalletCode == taskDto.PalletCode &&
|
s.TaskType == TaskRelocationTypeEnum.Relocation.GetHashCode());
|
if (task == null) return WebResponseContent.Instance.Error("未找到对应的移库任务");
|
|
var sourceLocation = await _locationInfoService.GetLocationInfo(task.Roadway, task.SourceAddress);
|
if (sourceLocation == null) return WebResponseContent.Instance.Error("未找到移库源货位");
|
|
var targetLocation = await _locationInfoService.GetLocationInfo(task.Roadway, task.TargetAddress);
|
if (targetLocation == null) return WebResponseContent.Instance.Error("未找到移库目标货位");
|
|
var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode);
|
if (stockInfo == null) return WebResponseContent.Instance.Error("未找到对应库存信息");
|
|
return await ExecuteWithinTransactionAsync(async () =>
|
{
|
stockInfo.LocationCode = targetLocation.LocationCode;
|
stockInfo.LocationId = targetLocation.Id;
|
stockInfo.StockStatus = StockStatusEmun.入库完成.GetHashCode();
|
|
sourceLocation.LocationStatus = LocationStatusEnum.Free.GetHashCode();
|
targetLocation.LocationStatus = LocationStatusEnum.InStock.GetHashCode();
|
|
var updateSourceResult = await _locationInfoService.UpdateLocationInfoAsync(sourceLocation);
|
var updateTargetResult = await _locationInfoService.UpdateLocationInfoAsync(targetLocation);
|
var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo);
|
|
if (!updateSourceResult || !updateTargetResult || !updateStockResult)
|
return WebResponseContent.Instance.Error("移库任务完成失败");
|
|
return await CompleteTaskAsync(task, "移库完成");
|
});
|
}
|
catch (Exception ex)
|
{
|
return WebResponseContent.Instance.Error($"完成任务失败: {ex.Message}");
|
}
|
}
|
|
/// <summary>
|
/// 创建空托盘入库任务
|
/// </summary>
|
/// <param name="taskDto"></param>
|
/// <returns></returns>
|
public async Task<WebResponseContent> CreateTaskInboundTrayAsync(CreateTaskDto taskDto)
|
{
|
try
|
{
|
WebResponseContent content = await GetTaskByPalletCodeAsync(taskDto.PalletCode);
|
if (content.Status)
|
{
|
return content;
|
}
|
|
//var tasks = await BaseDal.QueryAsync(s => s.PalletCode == palletCode);
|
//if (tasks == null || !tasks.Any())
|
// return WebResponseContent.Instance.Error("未找到对应的任务");
|
//var taskDtos = _mapper.Map<List<WMSTaskDTO>>(tasks);
|
return WebResponseContent.Instance.OK("查询成功"/*, taskDtos*/);
|
}
|
catch (Exception ex)
|
{
|
return WebResponseContent.Instance.Error($"查询任务失败: {ex.Message}");
|
}
|
}
|
|
/// <summary>
|
/// 空托盘入库完成
|
/// </summary>
|
public async Task<WebResponseContent> InboundFinishTaskTrayAsync(CreateTaskDto taskDto)
|
{
|
try
|
{
|
var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode);
|
if (task == null) return WebResponseContent.Instance.Error("未找到对应的任务");
|
|
var location = await _locationInfoService.GetLocationInfo(task.Roadway, task.TargetAddress);
|
if (location == null) return WebResponseContent.Instance.Error("未找到对应的货位");
|
|
var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode);
|
if (stockInfo == null) return WebResponseContent.Instance.Error("未找到对应库存信息");
|
|
return await ExecuteWithinTransactionAsync(async () =>
|
{
|
stockInfo.LocationCode = location.LocationCode;
|
stockInfo.LocationId = location.Id;
|
stockInfo.StockStatus = StockStatusEmun.空托盘库存.GetHashCode();
|
|
location.LocationStatus = LocationStatusEnum.InStock.GetHashCode();
|
|
var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location);
|
var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo);
|
if (!updateLocationResult || !updateStockResult)
|
return WebResponseContent.Instance.Error("任务完成失败");
|
|
var deleteResult = await BaseDal.DeleteDataAsync(task);
|
if (!deleteResult) return WebResponseContent.Instance.Error("任务完成失败");
|
|
return WebResponseContent.Instance.OK("任务完成");
|
});
|
}
|
catch (Exception ex)
|
{
|
return WebResponseContent.Instance.Error($"完成任务失败: {ex.Message}");
|
}
|
}
|
|
/// <summary>
|
/// 创建空托盘出库任务
|
/// </summary>
|
/// <param name="taskDto"></param>
|
/// <returns></returns>
|
public async Task<WebResponseContent> GetOutBoundTrayTaskAsync(CreateTaskDto taskDto)
|
{
|
try
|
{
|
|
var stockInfo = await _stockInfoService.Repository.QueryDataNavFirstAsync(x => x.LocationDetails.WarehouseId == taskDto.WarehouseId && x.LocationDetails.LocationStatus == LocationStatusEnum.InStock.GetHashCode() && x.StockStatus == StockStatusEmun.空托盘库存.GetHashCode());
|
if (stockInfo == null)
|
return WebResponseContent.Instance.Error("未找到对应的库存信息");
|
|
var task = new Dt_Task()
|
{
|
WarehouseId = stockInfo.LocationDetails.WarehouseId,
|
PalletCode = stockInfo.PalletCode,
|
PalletType = stockInfo.PalletType,
|
SourceAddress = stockInfo.LocationCode,
|
CurrentAddress = stockInfo.LocationCode,
|
NextAddress = taskDto.TargetAddress,
|
TargetAddress = taskDto.TargetAddress,
|
Roadway = stockInfo.LocationDetails.RoadwayNo,
|
TaskType = TaskOutboundTypeEnum.OutEmpty.GetHashCode(),
|
TaskStatus = TaskStatusEnum.New.GetHashCode(),
|
Grade = 1,
|
TaskNum = await BaseDal.GetTaskNo(),
|
Creater = "system",
|
};
|
var taskDtos = task.Adapt<WMSTaskDTO>();
|
|
var addResult = await BaseDal.AddDataAsync(task) > 0;
|
if (!addResult)
|
return WebResponseContent.Instance.Error("任务创建失败");
|
return WebResponseContent.Instance.OK("任务创建成功", taskDtos);
|
}
|
catch (Exception ex)
|
{
|
return WebResponseContent.Instance.Error($"查询任务失败: {ex.Message}");
|
}
|
}
|
|
/// <summary>
|
/// 空托盘出库完成
|
/// </summary>
|
public async Task<WebResponseContent> OutboundFinishTaskTrayAsync(CreateTaskDto taskDto)
|
{
|
try
|
{
|
var task = await BaseDal.QueryFirstAsync(s => s.PalletCode == taskDto.PalletCode);
|
if (task == null) return WebResponseContent.Instance.Error("未找到对应的任务");
|
|
var location = await _locationInfoService.GetLocationInfo(task.Roadway, task.SourceAddress);
|
if (location == null) return WebResponseContent.Instance.Error("未找到对应的货位");
|
|
var stockInfo = await _stockInfoService.GetStockInfoAsync(taskDto.PalletCode);
|
if (stockInfo == null) return WebResponseContent.Instance.Error("未找到对应库存信息");
|
|
return await ExecuteWithinTransactionAsync(async () =>
|
{
|
stockInfo.LocationId = 0;
|
stockInfo.LocationCode = null;
|
stockInfo.StockStatus = StockStatusEmun.出库完成.GetHashCode();
|
|
location.LocationStatus = LocationStatusEnum.Free.GetHashCode();
|
|
var updateLocationResult = await _locationInfoService.UpdateLocationInfoAsync(location);
|
var updateStockResult = await _stockInfoService.UpdateStockAsync(stockInfo);
|
if (!updateLocationResult || !updateStockResult)
|
return WebResponseContent.Instance.Error("任务完成失败");
|
|
var deleteResult = await BaseDal.DeleteDataAsync(task);
|
if (!deleteResult) return WebResponseContent.Instance.Error("任务完成失败");
|
|
return WebResponseContent.Instance.OK("任务完成");
|
});
|
}
|
catch (Exception ex)
|
{
|
return WebResponseContent.Instance.Error($"完成任务失败: {ex.Message}");
|
}
|
}
|
|
/// <summary>
|
/// 修改任务状态(根据任务ID修改为指定状态)
|
/// </summary>
|
/// <param name="taskId"></param>
|
/// <param name="newStatus"></param>
|
/// <returns></returns>
|
public async Task<WebResponseContent> UpdateTaskByStatusAsync(int taskId, int newStatus)
|
{
|
try
|
{
|
var tasks = await BaseDal.QueryFirstAsync(s => s.TaskNum == taskId);
|
if (tasks == null)
|
return WebResponseContent.Instance.Error("未找到对应的任务");
|
|
tasks.TaskStatus = newStatus;
|
await BaseDal.UpdateDataAsync(tasks);
|
|
return WebResponseContent.Instance.OK("修改成功", tasks);
|
}
|
catch (Exception ex)
|
{
|
return WebResponseContent.Instance.Error($"修改失败: {ex.Message}");
|
}
|
}
|
|
|
/// <summary>
|
/// 查找托盘是否有任务
|
/// </summary>
|
/// <param name="palletCode"></param>
|
/// <returns></returns>
|
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> CompleteTaskAsync(Dt_Task task, string operateType = "")
|
{
|
var deleteTaskResult = await BaseDal.DeleteDataAsync(task);
|
if (!deleteTaskResult) return WebResponseContent.Instance.Error("任务完成失败");
|
|
var historyTask = _mapper.Map<Dt_Task_Hty>(task);
|
historyTask.InsertTime = DateTime.Now;
|
historyTask.OperateType = operateType;
|
var saveResult = await _task_HtyService.Repository.AddDataAsync(historyTask) > 0;
|
if (!saveResult) return WebResponseContent.Instance.Error("任务历史保存失败");
|
|
return WebResponseContent.Instance.OK("任务完成");
|
}
|
|
/// <summary>
|
/// 根据巷道确定目标地址(支持多出库口轮询)
|
/// </summary>
|
private string DetermineTargetAddress(string roadway, Dictionary<string, List<string>> addressMap)
|
{
|
if (string.IsNullOrWhiteSpace(roadway))
|
return "10080";
|
|
// 查找匹配的巷道前缀
|
string matchedPrefix = null;
|
foreach (var kvp in addressMap)
|
{
|
if (roadway.Contains(kvp.Key))
|
{
|
matchedPrefix = kvp.Key;
|
break;
|
}
|
}
|
|
if (matchedPrefix == null)
|
return "10080";
|
|
var addresses = addressMap[matchedPrefix];
|
if (addresses == null || addresses.Count == 0)
|
return "10080";
|
|
// 单个地址,直接返回
|
if (addresses.Count == 1)
|
return addresses[0];
|
|
// 多个地址,使用轮询服务
|
return _roundRobinService.GetNextAddress(matchedPrefix, addresses);
|
}
|
|
/// <summary>
|
/// 自动创建出库任务 - 查询到期库存并创建任务
|
/// </summary>
|
public async Task<WebResponseContent> CreateAutoOutboundTasksAsync()
|
{
|
try
|
{
|
// 1. 查询到期库存
|
var expiredStocks = await _stockInfoService.Repository
|
.QueryDataNavAsync(s => s.OutboundDate <= DateTime.Now
|
&& s.StockStatus == StockStatusEmun.入库完成.GetHashCode());
|
|
if (expiredStocks == null || !expiredStocks.Any())
|
{
|
return WebResponseContent.Instance.OK("无到期库存需要处理");
|
}
|
|
// 过滤有位置且位置有库存的记录
|
expiredStocks = expiredStocks
|
.Where(s => s.LocationDetails != null
|
&& s.LocationDetails.LocationStatus == LocationStatusEnum.InStock.GetHashCode())
|
.ToList();
|
|
if (!expiredStocks.Any())
|
{
|
return WebResponseContent.Instance.OK("无符合条件的到期库存");
|
}
|
|
// 2. 检查已存在的任务
|
var palletCodes = expiredStocks.Select(s => s.PalletCode).ToList();
|
var existingTasks = await Repository.QueryDataAsync(t =>
|
palletCodes.Contains(t.PalletCode)
|
&& (t.TaskStatus == TaskStatusEnum.New.GetHashCode()
|
|| t.TaskStatus == TaskStatusEnum.SC_Executing.GetHashCode()
|
|| t.TaskStatus == TaskInStatusEnum.InNew.GetHashCode()));
|
|
var processedPallets = existingTasks.Select(t => t.PalletCode).ToHashSet();
|
|
// 3. 筛选需要处理的库存
|
var stocksToProcess = expiredStocks
|
.Where(s => !processedPallets.Contains(s.PalletCode))
|
.ToList();
|
|
if (!stocksToProcess.Any())
|
{
|
return WebResponseContent.Instance.OK("所有到期库存已存在任务");
|
}
|
|
// 4. 获取配置的目标地址映射
|
var targetAddressMap = _configuration.GetSection("AutoOutboundTask:TargetAddresses")
|
.Get<Dictionary<string, List<string>>>()
|
?? new Dictionary<string, List<string>>();
|
|
// 5. 批量创建任务
|
var taskList = new List<Dt_Task>();
|
foreach (var stock in stocksToProcess)
|
{
|
// 根据巷道确定目标地址
|
var targetAddress = DetermineTargetAddress(
|
stock.LocationDetails?.RoadwayNo ?? "",
|
targetAddressMap);
|
|
var task = new Dt_Task
|
{
|
WarehouseId = stock.WarehouseId,
|
PalletCode = stock.PalletCode,
|
PalletType = stock.PalletType,
|
SourceAddress = stock.LocationCode,
|
CurrentAddress = stock.LocationCode,
|
NextAddress = targetAddress,
|
TargetAddress = targetAddress,
|
Roadway = stock.LocationDetails?.RoadwayNo ?? "",
|
TaskType = TaskTypeEnum.Outbound.GetHashCode(),
|
TaskStatus = TaskStatusEnum.New.GetHashCode(),
|
Grade = 1,
|
TaskNum = await BaseDal.GetTaskNo(),
|
Creater = "system_auto"
|
};
|
taskList.Add(task);
|
}
|
|
var transactionResult = await ExecuteWithinTransactionAsync(async () =>
|
{
|
var addResult = await BaseDal.AddDataAsync(taskList) > 0;
|
if (!addResult)
|
{
|
return WebResponseContent.Instance.Error($"批量创建任务失败,共 {taskList.Count} 个任务");
|
}
|
|
// 任务创建成功后,同步锁定库存和货位状态,避免重复分配
|
var stocksToUpdate = stocksToProcess
|
.Select(s =>
|
{
|
s.StockStatus = StockStatusEmun.出库锁定.GetHashCode();
|
return s;
|
})
|
.ToList();
|
|
var updateStockResult = await _stockInfoService.Repository.UpdateDataAsync(stocksToUpdate);
|
if (!updateStockResult)
|
{
|
return WebResponseContent.Instance.Error($"任务创建成功,但库存状态更新失败,共 {stocksToUpdate.Count} 条");
|
}
|
|
var locationsToUpdate = stocksToProcess
|
.Where(s => s.LocationDetails != null)
|
.GroupBy(s => s.LocationDetails.Id)
|
.Select(g =>
|
{
|
var location = g.First().LocationDetails;
|
location.LocationStatus = LocationStatusEnum.InStockLock.GetHashCode();
|
return location;
|
})
|
.ToList();
|
|
if (locationsToUpdate.Any())
|
{
|
var updateLocationResult = await _locationInfoService.Repository.UpdateDataAsync(locationsToUpdate);
|
if (!updateLocationResult)
|
{
|
return WebResponseContent.Instance.Error($"任务创建成功,但货位状态更新失败,共 {locationsToUpdate.Count} 条");
|
}
|
}
|
|
return WebResponseContent.Instance.OK();
|
});
|
if (!transactionResult.Status)
|
{
|
return transactionResult;
|
}
|
|
// 6. 通知 WCS(异步,不影响主流程)
|
_ = Task.Run(() =>
|
{
|
try
|
{
|
var wmstaskDtos = _mapper.Map<List<WMSTaskDTO>>(taskList);
|
_httpClientHelper.Post<WebResponseContent>(
|
"http://localhost:9292/api/Task/ReceiveTask",
|
wmstaskDtos.ToJson());
|
}
|
catch (Exception ex)
|
{
|
// WCS 通知失败不影响任务创建,记录日志即可
|
Console.WriteLine($"WCS 批量通知失败,任务数量: {taskList.Count}, 错误: {ex.Message}");
|
}
|
});
|
|
return WebResponseContent.Instance.OK($"成功创建 {taskList.Count} 个出库任务", taskList.Count);
|
}
|
catch (Exception ex)
|
{
|
return WebResponseContent.Instance.Error($"自动创建出库任务失败: {ex.Message}");
|
}
|
}
|
|
/// <summary>
|
/// 创建机械手组盘任务
|
/// </summary>
|
public async Task<WebResponseContent> CreateRobotGroupPalletTaskAsync(StockDTO stock)
|
{
|
return await CreateRobotPalletTaskAsync(
|
stock,
|
"组盘",
|
RobotTaskTypeEnum.GroupPallet,
|
s => string.IsNullOrWhiteSpace(s.TargetPalletNo) ? s.SourcePalletNo : s.TargetPalletNo,
|
requireStockWithoutLocation: false);
|
}
|
|
/// <summary>
|
/// 创建机械手换盘任务
|
/// </summary>
|
public async Task<WebResponseContent> CreateRobotChangePalletTaskAsync(StockDTO stock)
|
{
|
return await CreateRobotPalletTaskAsync(
|
stock,
|
"换盘",
|
RobotTaskTypeEnum.ChangePallet,
|
s => s.SourcePalletNo,
|
requireStockWithoutLocation: true,
|
stockPalletCodeSelector: s => s.SourcePalletNo);
|
}
|
|
/// <summary>
|
/// 创建机械手拆盘任务
|
/// </summary>
|
public async Task<WebResponseContent> CreateRobotSplitPalletTaskAsync(StockDTO stock)
|
{
|
return await CreateRobotPalletTaskAsync(
|
stock,
|
"拆盘",
|
RobotTaskTypeEnum.SplitPallet,
|
s => s.SourcePalletNo,
|
requireStockWithoutLocation: true,
|
stockPalletCodeSelector: s => s.SourcePalletNo);
|
}
|
|
private async Task<WebResponseContent> CreateRobotPalletTaskAsync(
|
StockDTO stock,
|
string taskName,
|
RobotTaskTypeEnum taskType,
|
Func<StockDTO, string?> palletCodeSelector,
|
bool requireStockWithoutLocation,
|
Func<StockDTO, string?>? stockPalletCodeSelector = null)
|
{
|
try
|
{
|
if (stock == null)
|
return WebResponseContent.Instance.Error("任务参数不能为空");
|
|
var palletCode = palletCodeSelector(stock)?.Trim();
|
if (string.IsNullOrWhiteSpace(palletCode))
|
return WebResponseContent.Instance.Error("托盘号不能为空");
|
|
var sourceLineNo = stock.SourceLineNo?.Trim();
|
var targetLineNo = stock.TargetLineNo?.Trim();
|
if (string.IsNullOrWhiteSpace(sourceLineNo) || string.IsNullOrWhiteSpace(targetLineNo))
|
return WebResponseContent.Instance.Error("来源线体编号和目标线体编号不能为空");
|
|
var existingTask = await BaseDal.QueryFirstAsync(t =>
|
t.PalletCode == palletCode &&
|
(t.TaskStatus == TaskRobotStatusEnum.RobotNew.GetHashCode()
|
|| t.TaskStatus == TaskRobotStatusEnum.RobotExecuting.GetHashCode()
|
|| t.TaskStatus == TaskRobotStatusEnum.RobotPickFinish.GetHashCode()
|
|| t.TaskStatus == TaskRobotStatusEnum.RobotPutFinish.GetHashCode()
|
|| t.TaskStatus == TaskRobotStatusEnum.RobotPending.GetHashCode()));
|
if (existingTask != null)
|
return WebResponseContent.Instance.Error($"托盘[{palletCode}]已存在未完成任务");
|
|
Dt_StockInfo? stockInfo = null;
|
if (requireStockWithoutLocation)
|
{
|
var stockPalletCode = (stockPalletCodeSelector ?? palletCodeSelector).Invoke(stock)?.Trim();
|
if (string.IsNullOrWhiteSpace(stockPalletCode))
|
return WebResponseContent.Instance.Error("源托盘号不能为空");
|
|
stockInfo = await _stockInfoService.GetStockInfoAsync(stockPalletCode);
|
if (stockInfo == null)
|
return WebResponseContent.Instance.Error($"托盘[{stockPalletCode}]库存不存在");
|
|
if (stockInfo.LocationId > 0 || !string.IsNullOrWhiteSpace(stockInfo.LocationCode))
|
return WebResponseContent.Instance.Error($"托盘[{stockPalletCode}]库存已绑定货位,不能创建机械手{taskName}任务");
|
}
|
|
var task = new Dt_Task
|
{
|
TaskNum = await BaseDal.GetTaskNo(),
|
PalletCode = palletCode,
|
PalletType = stockInfo?.PalletType ?? 0,
|
Roadway = stock.Roadway,
|
TaskType = taskType.GetHashCode(),
|
TaskStatus = TaskRobotStatusEnum.RobotNew.GetHashCode(),
|
SourceAddress = sourceLineNo,
|
TargetAddress = targetLineNo,
|
CurrentAddress = sourceLineNo,
|
NextAddress = targetLineNo,
|
WarehouseId = stockInfo?.WarehouseId ?? 1,
|
Grade = 1,
|
Remark = $"机械手{taskName}",
|
Creater = "system"
|
};
|
|
var result = await Repository.AddDataAsync(task) > 0;
|
if (!result)
|
return WebResponseContent.Instance.Error($"机械手{taskName}任务创建失败");
|
|
var wmstaskDto = _mapper.Map<WMSTaskDTO>(task);
|
return WebResponseContent.Instance.OK($"机械手{taskName}任务创建成功", wmstaskDto);
|
}
|
catch (Exception ex)
|
{
|
return WebResponseContent.Instance.Error($"机械手{taskName}任务创建失败: {ex.Message}");
|
}
|
}
|
|
#endregion WCS逻辑处理
|
|
#region 分容柜接口
|
|
/// <summary>
|
/// 堆垛机取放货完成后物流通知化成分容柜完成信号
|
/// </summary>
|
public async Task<WebResponseContent> InOrOutCompletedAsync(GradingMachineInputDto input)
|
{
|
WebResponseContent content = new WebResponseContent();
|
if (string.IsNullOrWhiteSpace(input.PalletCode) || string.IsNullOrWhiteSpace(input.LocationCode))
|
{
|
return content.Error($"托盘号或者货位编号不能为空");
|
}
|
|
try
|
{
|
var stockInfo = await _stockInfoService.GetStockInfoAsync(input.PalletCode, input.LocationCode);
|
|
int locationStatus;
|
if (stockInfo == null)
|
{
|
var location = await _locationInfoService.GetLocationInfoAsync(input.LocationCode);
|
locationStatus = location?.LocationStatus ?? 0;
|
}
|
else
|
{
|
locationStatus = stockInfo.LocationDetails?.LocationStatus ?? 0;
|
}
|
|
OutputDto outPutDto = new OutputDto()
|
{
|
LocationCode = input.LocationCode,
|
PalletCode = input.PalletCode,
|
IsNormalProcedure = 1,
|
LocationStatus = locationStatus
|
};
|
return content.OK(data: outPutDto);
|
}
|
catch (Exception ex)
|
{
|
content.Error(ex.Message);
|
}
|
|
return content;
|
}
|
|
/// <summary>
|
/// 化成分容柜定时向物流更新分容柜状态信息
|
/// </summary>
|
/// <param name="input"></param>
|
/// <returns></returns>
|
public async Task<WebResponseContent> SendLocationStatusAsync(GradingMachineInputDto input)
|
{
|
WebResponseContent content = new WebResponseContent();
|
if (string.IsNullOrWhiteSpace(input.LocationCode))
|
{
|
return content.Error($"货位编号不能为空");
|
}
|
|
try
|
{
|
var result = await _locationInfoService.Db.Updateable<Dt_LocationInfo>()
|
.SetColumns(s => new Dt_LocationInfo
|
{
|
LocationStatus = input.LocationStatus
|
}).Where(s => s.LocationCode == input.LocationCode).ExecuteCommandAsync() > 0;
|
|
if (result)
|
{
|
content.OK("更新成功");
|
}
|
else
|
{
|
content.Error("更新失败");
|
}
|
}
|
catch (Exception ex)
|
{
|
content.Error(ex.Message);
|
}
|
return content;
|
}
|
|
/// <summary>
|
/// 分容柜工作完成后调用此接口通知物流出库
|
/// </summary>
|
/// <param name="input"></param>
|
/// <returns></returns>
|
public async Task<WebResponseContent> RequestOutboundAsync(GradingMachineInputDto input)
|
{
|
WebResponseContent content = new WebResponseContent();
|
if (string.IsNullOrWhiteSpace(input.LocationCode) || string.IsNullOrWhiteSpace(input.PalletCode))
|
{
|
return content.Error($"托盘号或者货位编号不能为空");
|
}
|
try
|
{
|
var stock = await _stockInfoService.GetStockInfoAsync(input.PalletCode, input.LocationCode);
|
if (stock == null)
|
{
|
content.Error("未找到对应的托盘");
|
}
|
else
|
{
|
var taskList = new Dt_Task
|
{
|
WarehouseId = stock.WarehouseId,
|
PalletCode = stock.PalletCode,
|
PalletType = stock.PalletType,
|
SourceAddress = stock.LocationCode,
|
CurrentAddress = stock.LocationCode,
|
NextAddress = "10080",
|
TargetAddress = "10081",
|
Roadway = stock.LocationDetails.RoadwayNo,
|
TaskType = TaskTypeEnum.Outbound.GetHashCode(),
|
TaskStatus = TaskStatusEnum.New.GetHashCode(),
|
Grade = 1,
|
TaskNum = await BaseDal.GetTaskNo(),
|
Creater = "system",
|
};
|
|
var result = await BaseDal.AddDataAsync(taskList) > 0;
|
var wmstaskDto = result ? _mapper.Map<WMSTaskDTO>(taskList) : null;
|
|
var httpResponse = _httpClientHelper.Post<WebResponseContent>("http://logistics-service/api/logistics/notifyoutbound", JsonSerializer.Serialize(wmstaskDto)).Data;
|
if (result && httpResponse != null)
|
{
|
content.OK("出库请求成功");
|
}
|
else
|
{
|
content.Error("出库请求失败");
|
}
|
}
|
}
|
catch (Exception ex)
|
{
|
content.Error(ex.Message);
|
}
|
return content;
|
}
|
|
/// <summary>
|
/// 入库完成分容调用获取托盘上每个通道电芯
|
/// </summary>
|
/// <param name="input"></param>
|
/// <returns></returns>
|
public async Task<WebResponseContent> GetPalletCodeCellAsync(GradingMachineInputDto input)
|
{
|
WebResponseContent content = new WebResponseContent();
|
if (string.IsNullOrWhiteSpace(input.PalletCode) || string.IsNullOrWhiteSpace(input.LocationCode))
|
{
|
return content.Error($"托盘号或者货位编号不能为空");
|
}
|
try
|
{
|
var stockInfo = await _stockInfoService.GetStockInfoAsync(input.PalletCode, input.LocationCode);
|
if (stockInfo == null)
|
{
|
return content.Error("未找到对应的托盘");
|
}
|
var outPutDtos = stockInfo.Details.Select(x => new OutputDto()
|
{
|
LocationCode = input.LocationCode,
|
PalletCode = input.PalletCode,
|
IsNormalProcedure = 1,
|
LocationStatus = stockInfo.LocationDetails.LocationStatus,
|
CellCode = x.SerialNumber,
|
Channel = x.InboundOrderRowNo.ToString()
|
}).ToList();
|
return content.OK(data: outPutDtos);
|
}
|
catch (Exception ex)
|
{
|
return content.Error(ex.Message);
|
}
|
}
|
|
#endregion 分容柜接口
|
}
|
}
|