|
|
|
|
|
#region << 版 本 注 释 >>
|
|
|
|
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------
|
|
|
|
|
|
|* 版权所有 (c) 2026 WenJY 保留所有权利。
|
|
|
|
|
|
|* CLR版本:4.0.30319.42000
|
|
|
|
|
|
|* 机器名称:Mr.Wen's MacBook Pro
|
|
|
|
|
|
|* 命名空间:Sln.Wcs.Business
|
|
|
|
|
|
|* 唯一标识:7E8A9B2C-3D4F-5E6A-7B8C-9D0E1F2A3B4C
|
|
|
|
|
|
|*
|
|
|
|
|
|
|* 创建者:WenJY
|
|
|
|
|
|
|* 电子邮箱:
|
|
|
|
|
|
|* 创建时间:2026-05-15 15:30:00
|
|
|
|
|
|
|* 版本:V1.0.0
|
|
|
|
|
|
|* 描述:通用仓储任务业务处理类,支持入库/出库,包材/成品/托盘分类
|
|
|
|
|
|
|*
|
|
|
|
|
|
|*--------------------------------------------------------------------
|
|
|
|
|
|
|* 修改人:
|
|
|
|
|
|
|* 时间:
|
|
|
|
|
|
|* 修改说明:
|
|
|
|
|
|
|*
|
|
|
|
|
|
|* 版本:V1.0.0
|
|
|
|
|
|
|*--------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
|
|
|
|
#endregion << 版 本 注 释 >>
|
|
|
|
|
|
|
|
|
|
|
|
using System.Linq.Expressions;
|
|
|
|
|
|
using Sln.Wcs.Business.Domain.Dto.CreateTask;
|
|
|
|
|
|
using Sln.Wcs.Business.Domain.Dto.FilterLocation;
|
|
|
|
|
|
using Sln.Wcs.Business.Domain.Dto.SaveTask;
|
|
|
|
|
|
using Sln.Wcs.Business.Domain.Dto.ValidateMaterial;
|
|
|
|
|
|
using Sln.Wcs.Business.Domain.Enum;
|
|
|
|
|
|
using Sln.Wcs.Business.Domain.Model.CreateTask;
|
|
|
|
|
|
using Sln.Wcs.Business.Domain.Model.FilterLocation;
|
|
|
|
|
|
using Sln.Wcs.Business.Domain.Model.SaveTask;
|
|
|
|
|
|
using Sln.Wcs.Business.Util;
|
|
|
|
|
|
using Sln.Wcs.Model.Domain;
|
|
|
|
|
|
using Sln.Wcs.Repository.service;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Sln.Wcs.Business;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 通用仓储任务业务处理类
|
|
|
|
|
|
/// 支持扩展点:子类可重写虚方法实现特定业务逻辑
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public class StoreTaskBusiness : EntityWrapper
|
|
|
|
|
|
{
|
|
|
|
|
|
private readonly IBasePathInfoService _basePathInfoService;
|
|
|
|
|
|
private readonly ILiveTaskQueueService _liveTaskQueueService;
|
|
|
|
|
|
private readonly IBaseStoreInfoService _baseStoreInfoService;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 构造函数
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="basePathInfoService">路径服务</param>
|
|
|
|
|
|
/// <param name="liveTaskQueueService">任务队列服务</param>
|
|
|
|
|
|
/// <param name="baseStoreInfoService">仓库服务</param>
|
|
|
|
|
|
public StoreTaskBusiness(
|
|
|
|
|
|
IBasePathInfoService basePathInfoService,
|
|
|
|
|
|
ILiveTaskQueueService liveTaskQueueService,
|
|
|
|
|
|
IBaseStoreInfoService baseStoreInfoService)
|
|
|
|
|
|
{
|
|
|
|
|
|
_basePathInfoService = basePathInfoService;
|
|
|
|
|
|
_liveTaskQueueService = liveTaskQueueService;
|
|
|
|
|
|
_baseStoreInfoService = baseStoreInfoService;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#region 虚方法 - 供子类重写的扩展点
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 校验物料(可被子类重写)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public virtual ValidateMaterialResultDto ValidateMaterial(ValidateMaterialDto validateMaterialDto)
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 创建任务(可被子类重写)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="createTaskDto">创建任务参数(包含taskType和taskCategory)</param>
|
|
|
|
|
|
public virtual CreateTaskResultDto CreateTask(CreateTaskDto createTaskDto)
|
|
|
|
|
|
{
|
|
|
|
|
|
CreateTaskResultDto resultDto = new CreateTaskResultDto();
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
ValidateCreateTaskParams(createTaskDto);
|
|
|
|
|
|
|
|
|
|
|
|
BasePathInfo pathInfo = GetPathInfo(createTaskDto);
|
|
|
|
|
|
|
|
|
|
|
|
string taskCode = GenerateTaskCode();
|
|
|
|
|
|
|
|
|
|
|
|
List<LiveTaskDetail> taskDetails = pathInfo.pathDetails
|
|
|
|
|
|
.Select(item => LiveTaskDetailWrapper(taskCode, createTaskDto, item))
|
|
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
|
|
var taskQueue = LiveTaskQueueWrapper(taskCode, createTaskDto, pathInfo);
|
|
|
|
|
|
taskQueue.taskSteps = taskDetails.Count;
|
|
|
|
|
|
taskQueue.taskDetails = taskDetails;
|
|
|
|
|
|
|
|
|
|
|
|
resultDto.code = Domain.Enum.BusinessStatusEnum.成功;
|
|
|
|
|
|
resultDto.msg = $"{GetCategoryName(createTaskDto.taskCategory)}{GetTaskTypeName(createTaskDto.taskType)}任务创建成功:{taskCode};关联路径:{pathInfo.pathName}";
|
|
|
|
|
|
resultDto.data = new CreateTaskResultModel()
|
|
|
|
|
|
{
|
|
|
|
|
|
taskCode = taskCode,
|
|
|
|
|
|
taskQueue = taskQueue,
|
|
|
|
|
|
taskDetails = taskDetails,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
|
|
|
|
|
resultDto.code = Domain.Enum.BusinessStatusEnum.方法执行异常;
|
|
|
|
|
|
resultDto.msg = e.Message;
|
|
|
|
|
|
}
|
|
|
|
|
|
return resultDto;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 筛选库位(可被子类重写)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="filterLocationDto">筛选库位参数(包含taskType和taskCategory)</param>
|
|
|
|
|
|
public virtual FilterLocationResultDto FilterLocation(FilterLocationDto filterLocationDto)
|
|
|
|
|
|
{
|
|
|
|
|
|
FilterLocationResultDto resultDto = new FilterLocationResultDto();
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
ValidateFilterLocationParams(filterLocationDto);
|
|
|
|
|
|
|
|
|
|
|
|
int locationStatus = filterLocationDto.taskType == TaskTypeEnum.InStore ? 0 : 1;
|
|
|
|
|
|
|
|
|
|
|
|
Expression<Func<BaseStoreInfo, bool>> storeWhere = GetStoreWhere(filterLocationDto.taskType, filterLocationDto.taskCategory);
|
|
|
|
|
|
Expression<Func<BaseLocationInfo, bool>> locationWhere = GetLocationWhere(filterLocationDto, locationStatus);
|
|
|
|
|
|
|
|
|
|
|
|
List<BaseStoreInfo> storeInfos = _baseStoreInfoService.GetBasePathInfo(storeWhere, locationWhere);
|
|
|
|
|
|
|
|
|
|
|
|
BaseStoreInfo? storeInfo = storeInfos
|
|
|
|
|
|
.Where(s => s.locationInfos.Count > 0)
|
|
|
|
|
|
.OrderBy(x => x.storeCode)
|
|
|
|
|
|
.FirstOrDefault() ?? throw new ArgumentNullException($"未获取到可用仓库");
|
|
|
|
|
|
|
|
|
|
|
|
BaseLocationInfo? locationInfo = storeInfo.locationInfos
|
|
|
|
|
|
.OrderBy(x => x.locationRows)
|
|
|
|
|
|
.ThenBy(x => x.locationColumns)
|
|
|
|
|
|
.ThenBy(x => x.locationLayers)
|
|
|
|
|
|
.FirstOrDefault() ?? throw new ArgumentNullException($"目标仓库:{storeInfo.storeName}中未获取到可用库位");
|
|
|
|
|
|
|
|
|
|
|
|
resultDto.code = Domain.Enum.BusinessStatusEnum.成功;
|
|
|
|
|
|
resultDto.msg = "执行完成";
|
|
|
|
|
|
resultDto.data = new FilterLocationResultModel()
|
|
|
|
|
|
{
|
|
|
|
|
|
storeInfo = storeInfo,
|
|
|
|
|
|
locationInfos = storeInfo.locationInfos,
|
|
|
|
|
|
locationInfo = locationInfo,
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
|
|
|
|
|
resultDto.code = Domain.Enum.BusinessStatusEnum.方法执行异常;
|
|
|
|
|
|
resultDto.msg = e.Message;
|
|
|
|
|
|
}
|
|
|
|
|
|
return resultDto;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 保存任务(可被子类重写)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="saveTaskDto">保存任务参数</param>
|
|
|
|
|
|
public virtual SaveTaskResultDto SaveTask(SaveTaskDto saveTaskDto)
|
|
|
|
|
|
{
|
|
|
|
|
|
SaveTaskResultDto resultDto = new SaveTaskResultDto();
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
var inRes = _liveTaskQueueService.InsertTaskQueue(saveTaskDto.taskQueue);
|
|
|
|
|
|
|
|
|
|
|
|
resultDto.code = Domain.Enum.BusinessStatusEnum.成功;
|
|
|
|
|
|
resultDto.msg = "执行完成";
|
|
|
|
|
|
resultDto.data = new SaveTaskResultModel()
|
|
|
|
|
|
{
|
|
|
|
|
|
isRes = inRes
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
|
|
|
|
|
resultDto.code = Domain.Enum.BusinessStatusEnum.方法执行异常;
|
|
|
|
|
|
resultDto.msg = e.Message;
|
|
|
|
|
|
}
|
|
|
|
|
|
return resultDto;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region 受保护虚方法 - 可被子类定制
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 校验创建任务参数(可被子类重写添加额外校验)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
protected virtual void ValidateCreateTaskParams(CreateTaskDto createTaskDto)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (string.IsNullOrEmpty(createTaskDto.materialCode))
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new InvalidOperationException($"物料编号不允许为 NULL");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(createTaskDto.palletBarcode))
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new InvalidOperationException($"{GetCategoryName(createTaskDto.taskCategory)}条码不允许为 NULL");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(createTaskDto.startPoint) || string.IsNullOrEmpty(createTaskDto.endPoint))
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new InvalidOperationException($"起始位置、终点位置不允许为 NULL");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 校验筛选库位参数(可被子类重写添加额外校验)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
protected virtual void ValidateFilterLocationParams(FilterLocationDto filterLocationDto)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (string.IsNullOrEmpty(filterLocationDto.materialCode))
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new InvalidOperationException($"物料编号不允许为 NULL");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取路径信息(可被子类重写定制路径查询逻辑)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
protected virtual BasePathInfo GetPathInfo(CreateTaskDto createTaskDto)
|
|
|
|
|
|
{
|
|
|
|
|
|
Expression<Func<BasePathInfo, bool>> exp = x =>
|
|
|
|
|
|
x.startPoint == createTaskDto.startPoint &&
|
|
|
|
|
|
x.endPoint == createTaskDto.endPoint &&
|
|
|
|
|
|
x.pathType == (int)createTaskDto.taskType &&
|
|
|
|
|
|
x.pathCategory == (int)createTaskDto.taskCategory;
|
|
|
|
|
|
|
|
|
|
|
|
return _basePathInfoService.GetBasePathInfo(exp)
|
|
|
|
|
|
.FirstOrDefault() ?? throw new InvalidOperationException($"{GetCategoryName(createTaskDto.taskCategory)}{GetTaskTypeName(createTaskDto.taskType)}输送路径为 NULL");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 生成任务编号(可被子类重写定制生成规则)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
protected virtual string GenerateTaskCode()
|
|
|
|
|
|
{
|
|
|
|
|
|
return DateTime.Now.ToString("yyyyMMddHHmmss") + new Random().Next(10, 99);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取仓库查询条件(可被子类重写)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
protected virtual Expression<Func<BaseStoreInfo, bool>> GetStoreWhere(TaskTypeEnum taskType, TaskCategoryEnum taskCategory)
|
|
|
|
|
|
{
|
|
|
|
|
|
return x => x.storeType == (int)taskCategory && x.isFlag == 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取库位查询条件(可被子类重写)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
protected virtual Expression<Func<BaseLocationInfo, bool>> GetLocationWhere(FilterLocationDto filterLocationDto, int locationStatus)
|
|
|
|
|
|
{
|
|
|
|
|
|
return x => x.materialCode == filterLocationDto.materialCode &&
|
|
|
|
|
|
x.locationStatus == locationStatus &&
|
|
|
|
|
|
x.isFlag == 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取分类名称
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private string GetCategoryName(TaskCategoryEnum taskCategory)
|
|
|
|
|
|
{
|
|
|
|
|
|
return taskCategory switch
|
|
|
|
|
|
{
|
|
|
|
|
|
TaskCategoryEnum.Material => "包材",
|
|
|
|
|
|
TaskCategoryEnum.Product => "成品",
|
|
|
|
|
|
TaskCategoryEnum.Pallet => "托盘",
|
|
|
|
|
|
_ => "未知"
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取任务类型名称
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private string GetTaskTypeName(TaskTypeEnum taskType)
|
|
|
|
|
|
{
|
|
|
|
|
|
return taskType switch
|
|
|
|
|
|
{
|
|
|
|
|
|
TaskTypeEnum.InStore => "入库",
|
|
|
|
|
|
TaskTypeEnum.OutStore => "出库",
|
|
|
|
|
|
_ => "未知"
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|