using Microsoft.Extensions.Configuration;
using WIDESEA_Common.LocationEnum;
using WIDESEA_Common.StockEnum;
using WIDESEA_Common.TaskEnum;
using WIDESEA_Core;
using WIDESEA_Core.Helper;
using WIDESEA_DTO.Task;
using WIDESEA_Model.Models;
namespace WIDESEA_TaskInfoService
{
public partial class TaskService
{
#region 自动出库任务
///
/// 自动创建出库任务 - 查询到期库存并创建任务
///
public async Task 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>>()
?? new Dictionary>();
// 5. 批量创建任务
var taskList = new List();
foreach (var stock in stocksToProcess)
{
// 根据巷道确定目标地址(优先根据 Remark 确定,Remark 为空则根据巷道配置)
var targetAddress = DetermineTargetAddressByRemark(
stock.Remark ?? "",
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 _unitOfWorkManage.BeginTranAsync(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>(taskList);
_httpClientHelper.Post(
"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}");
}
}
#endregion 自动出库任务
}
}