You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

317 lines
11 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#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
{
protected readonly IBasePathInfoService _basePathInfoService;
protected readonly ILiveTaskQueueService _liveTaskQueueService;
protected 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
{
// 1. 参数校验(可扩展)
ValidateCreateTaskParams(createTaskDto);
// 2. 获取输送路径
BasePathInfo pathInfo = GetPathInfo(createTaskDto, createTaskDto.taskType, createTaskDto.taskCategory);
// 4. 生成任务编号
string taskCode = GenerateTaskCode();
// 5. 路径转为任务
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;
// 6. 返回结果
resultDto.code = Domain.Enum.BusinessStatusEnum.;
resultDto.msg = GetTaskSuccessMessage(taskCode, pathInfo.pathName, createTaskDto.taskType, createTaskDto.taskCategory);
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
{
// 1. 参数校验(可扩展)
ValidateFilterLocationParams(filterLocationDto);
// 2. 获取库位状态(入库:未使用=0出库已使用=1
int locationStatus = filterLocationDto.taskType == TaskTypeEnum.InStore ? 0 : 1;
// 3. 构建查询条件
Expression<Func<BaseStoreInfo, bool>> storeWhere = GetStoreWhere(filterLocationDto.taskType, filterLocationDto.taskCategory);
Expression<Func<BaseLocationInfo, bool>> locationWhere = GetLocationWhere(filterLocationDto, locationStatus);
// 4. 查询仓库和库位
List<BaseStoreInfo> storeInfos = _baseStoreInfoService.GetBasePathInfo(storeWhere, locationWhere);
// 5. 选择最优仓库和库位
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, TaskTypeEnum taskType, TaskCategoryEnum taskCategory)
{
Expression<Func<BasePathInfo, bool>> exp = x =>
x.startPoint == createTaskDto.startPoint &&
x.endPoint == createTaskDto.endPoint &&
x.pathType == (int)taskType &&
x.pathCategory == (int)taskCategory;
return _basePathInfoService.GetBasePathInfo(exp)
.FirstOrDefault() ?? throw new InvalidOperationException($"{GetCategoryName(taskCategory)}{GetTaskTypeName(taskType)}输送路径为 NULL");
}
/// <summary>
/// 生成任务编号(可被子类重写定制生成规则)
/// </summary>
protected virtual string GenerateTaskCode()
{
return DateTime.Now.ToString("yyyyMMddHHmmss") + new Random().Next(1000, 9999);
}
/// <summary>
/// 获取任务成功的消息(可被子类重写)
/// </summary>
protected virtual string GetTaskSuccessMessage(string taskCode, string pathName, TaskTypeEnum taskType, TaskCategoryEnum taskCategory)
{
return $"{GetCategoryName(taskCategory)}{GetTaskTypeName(taskType)}任务创建成功:{taskCode};关联路径:{pathName}";
}
/// <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
#region 辅助方法
/// <summary>
/// 获取分类名称
/// </summary>
protected string GetCategoryName(TaskCategoryEnum taskCategory)
{
return taskCategory switch
{
TaskCategoryEnum.Material => "包材",
TaskCategoryEnum.Product => "成品",
TaskCategoryEnum.Pallet => "托盘",
_ => "未知"
};
}
/// <summary>
/// 获取任务类型名称
/// </summary>
protected string GetTaskTypeName(TaskTypeEnum taskType)
{
return taskType switch
{
TaskTypeEnum.InStore => "入库",
TaskTypeEnum.OutStore => "出库",
_ => "未知"
};
}
#endregion
}