using Mapster;
using MapsterMapper;
using WIDESEA_Common.CommonEnum;
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.Utilities;
using WIDESEA_DTO.Basic;
using WIDESEA_DTO.Task;
using WIDESEA_IBasicService;
using WIDESEA_Model.Models;
namespace WIDESEA_BasicService
{
///
/// 货位信息服务实现类
///
public partial class LocationInfoService : ServiceBase>, ILocationInfoService
{
private readonly IMapper _mapper;
private readonly IRepository _taskRepository;
private readonly IRepository _stockInfoRepository;
///
/// 构造函数
///
/// 基础数据访问对象
/// 任务仓储
/// 库存信息仓储
public LocationInfoService(
IRepository baseDal,
IRepository taskRepository,
IRepository stockInfoRepository,
IMapper mapper) : base(baseDal)
{
_taskRepository = taskRepository;
_stockInfoRepository = stockInfoRepository;
_mapper = mapper;
}
///
/// 获取货位信息仓储
///
public IRepository Repository => BaseDal;
///
/// 批量启用货位
///
/// 货位主键数组
/// 操作结果
public WebResponseContent LocationEnableStatus(int[] keys)
{
var locationInfos = Repository.QueryData(x => keys.Contains(x.Id));
locationInfos.ForEach(x => x.EnableStatus = EnableStatusEnum.Normal.GetHashCode());
Repository.UpdateData(locationInfos);
return WebResponseContent.Instance.OK();
}
///
/// 批量禁用货位
///
/// 货位主键数组
/// 操作结果
public WebResponseContent LocationDisableStatus(int[] keys)
{
var locationInfos = Repository.QueryData(x => keys.Contains(x.Id));
locationInfos.ForEach(x => x.EnableStatus = EnableStatusEnum.Disable.GetHashCode());
Repository.UpdateData(locationInfos);
return WebResponseContent.Instance.OK();
}
///
/// 单个启用货位
///
/// 货位主键
/// 操作结果
public WebResponseContent LocationEnableStatus(int key) => LocationEnableStatus(new[] { key });
///
/// 单个禁用货位
///
/// 货位主键
/// 操作结果
public WebResponseContent LocationDisableStatus(int key) => LocationDisableStatus(new[] { key });
///
/// 初始化货位
///
/// 初始化货位数据传输对象
/// 操作结果
public WebResponseContent InitializationLocation(InitializationLocationDTO dto)
{
try
{
var (isValid, errorMsg, _) = ModelValidate.ValidateModelData(dto);
if (!isValid) return WebResponseContent.Instance.Error(errorMsg);
var locationInfos = new List();
int depth = dto.Depth;
for (int row = 1; row <= dto.MaxRow; row++)
{
depth = CalculateDepth(row, dto.MaxRow, dto.Depth, depth);
for (int col = 1; col <= dto.MaxColumn; col++)
{
for (int layer = 1; layer <= dto.MaxLayer; layer++)
{
var location = CreateLocationInfo(dto.Roadway, row, col, layer, depth);
locationInfos.Add(location);
}
}
}
BaseDal.AddData(locationInfos);
return WebResponseContent.Instance.OK();
}
catch (Exception ex)
{
return WebResponseContent.Instance.Error(ex.Message);
}
}
///
/// 根据巷道获取空闲货位信息
///
/// 巷道编号
/// 空闲货位信息,如果未找到则返回null
public async Task GetLocationInfo(string roadwayNo)
{
var locations = await BaseDal.QueryDataAsync(x =>
x.EnableStatus == EnableStatusEnum.Normal.GetHashCode() &&
x.RoadwayNo == roadwayNo &&
x.LocationStatus == LocationStatusEnum.Free.GetHashCode());
return locations?
.OrderBy(x => x.Layer)
.ThenByDescending(x => x.Depth)
.ThenBy(x => x.Column)
.ThenBy(x => x.Row)
.FirstOrDefault();
}
///
/// 根据巷道和货位编码获取空闲货位信息
///
/// 巷道编号
/// 货位编码
/// 货位信息,如果未找到则返回null
public async Task GetLocationInfo(string roadwayNo, string locationCode)
{
return await BaseDal.QueryFirstAsync(x => x.RoadwayNo == roadwayNo && x.LocationCode == locationCode);
}
///
/// 根据货位编码获取货位信息
///
/// 货位编码
/// 货位信息
public async Task GetLocationInfoAsync(string locationCode)
{
return await BaseDal.QueryFirstAsync(x => x.LocationCode == locationCode);
}
///
/// 更新货位信息
///
/// 货位信息对象
/// 更新是否成功
public async Task UpdateLocationInfoAsync(Dt_LocationInfo locationInfo)
{
return await BaseDal.UpdateDataAsync(locationInfo);
}
///
/// 检查并生成移库任务或返回出库任务
///
/// 任务号
/// 任务信息
public async Task TransferCheckAsync(int taskNum)
{
var content = new WebResponseContent();
try
{
// 根据任务号获取任务
var outboundTask = await _taskRepository.QueryFirstAsync(x => x.TaskNum == taskNum);
if (outboundTask == null)
return content.Error("任务不存在");
var location = await BaseDal.QueryFirstAsync(x => x.LocationCode == outboundTask.SourceAddress && x.RoadwayNo == outboundTask.Roadway);
// 检查是否需要进行移库
if (CheckForInternalTransfer(location))
{
// 计算对应位置的相对库位(奇数行的下一行或者偶数行的上一行)
var newLocationID = GetRelativeLocationID(location);
// 获取新的库位的任务
var internalTransferTask = await _taskRepository.QueryFirstAsync(x => x.SourceAddress == newLocationID && x.Roadway == outboundTask.Roadway);
// 如果新的库位没有找到对应的任务
if (internalTransferTask == null)
{
return content.OK("获取到移库任务", await HandleNoTaskAtLocation(newLocationID, outboundTask));
}
// 直接返回一深位出库任务
return content.OK("获取到一深位出库任务", internalTransferTask);
}
// 返回当前库位的出库任务
return content.OK("当前出库任务", outboundTask);
}
catch (Exception ex)
{
return content.Error($"发生错误:{ex.Message}");
}
}
#region 私有方法
///
/// 计算相对的库位ID
///
/// 货位信息
/// 相对的库位ID
private static string GetRelativeLocationID(Dt_LocationInfo locationInfo)
{
int line = locationInfo.Row;
// 计算相对的货位行值,奇数行的下一行或者偶数行的上一行
int relativeLine = line % 2 == 1 ? line + 1 : line - 1;
// 构建新的库位ID
string[] newLocationParts = new string[] { relativeLine.ToString().PadLeft(3, '0'), locationInfo.Column.ToString().PadLeft(3, '0'), locationInfo.Layer.ToString().PadLeft(3, '0') };
return string.Join("-", newLocationParts);
}
///
/// 处理没有任务的库位情况
///
/// 新的库位ID
/// 出库任务
/// 生成的移库任务或原始出库任务
private async Task HandleNoTaskAtLocation(string newLocationID, Dt_Task outboundTask)
{
// 判断该位置是否有库存
var stockInfo = await _stockInfoRepository.QueryDataNavFirstAsync(x =>
x.LocationCode == newLocationID &&
x.StockStatus == StockStatusEmun.入库完成.GetHashCode() &&
x.LocationDetails.LocationStatus == LocationStatusEnum.InStock.GetHashCode());
if (stockInfo == null)
{
// 如果没有库存,直接返回当前出库任务
return outboundTask;
}
// 如果有库存,生成移库任务
var emptyLocation = await GetTransferLocationEmptyAsync(outboundTask.Roadway);
var taskNo = await _taskRepository.GetTaskNo();
var newTransferTask = new Dt_Task
{
CreateDate = DateTime.Now,
Creater = App.User.UserName ?? "system",
CurrentAddress = newLocationID,
Grade = 99,
NextAddress = emptyLocation.LocationCode,
PalletCode = stockInfo.PalletCode,
Remark = "移库",
Roadway = stockInfo.LocationDetails.RoadwayNo,
SourceAddress = newLocationID,
TaskNum = taskNo,
TargetAddress = emptyLocation.LocationCode,
TaskType = TaskRelocationTypeEnum.Relocation.GetHashCode(),
TaskStatus = TaskRelocationStatusEnum.RelocationNew.GetHashCode(),
WarehouseId = stockInfo.WarehouseId
};
var createdTask = await _taskRepository.Db.Insertable(newTransferTask).ExecuteReturnEntityAsync();
// 创建移库任务后,立即锁定库存和相关货位,避免并发重复分配
stockInfo.StockStatus = StockStatusEmun.移库锁定.GetHashCode();
var updateStockResult = await _stockInfoRepository.UpdateDataAsync(stockInfo);
var locationsToUpdate = new List();
if (stockInfo.LocationDetails != null)
{
stockInfo.LocationDetails.LocationStatus = LocationStatusEnum.InStockLock.GetHashCode();
locationsToUpdate.Add(stockInfo.LocationDetails);
}
emptyLocation.LocationStatus = LocationStatusEnum.FreeLock.GetHashCode();
locationsToUpdate.Add(emptyLocation);
var updateLocationResult = await BaseDal.UpdateDataAsync(locationsToUpdate);
if (!updateStockResult || !updateLocationResult)
{
throw new Exception("创建移库任务后更新库存状态或货位状态失败");
}
return createdTask;
}
///
/// 检查货位是否需要移库
///
/// 货位信息
/// 是否需要移库
private static bool CheckForInternalTransfer(Dt_LocationInfo location)
{
return location.Depth == 2;
}
///
/// 根据巷道获取二深位的空库位
///
/// 巷道编号
/// 货位对象
private async Task GetTransferLocationEmptyAsync(string roadway)
{
return await BaseDal.QueryFirstAsync(x => x.Depth == 2 && x.LocationStatus == LocationStatusEnum.Free.GetHashCode() && x.RoadwayNo == roadway);
}
///
/// 计算深度
///
/// 行号
/// 最大行数
/// 最大深度
/// 当前深度
/// 计算后的深度
private static int CalculateDepth(int row, int maxRow, int maxDepth, int currentDepth)
{
int mod = row % maxRow;
if (mod == 1) return maxDepth;
if (mod == maxDepth + 1) return 1;
if (mod > 1 && mod <= maxDepth) return currentDepth - 1;
return currentDepth + 1;
}
///
/// 创建货位信息
///
/// 巷道编号
/// 行号
/// 列号
/// 层数
/// 深度
/// 货位信息对象
private static Dt_LocationInfo CreateLocationInfo(string roadwayNo, int row, int col, int layer, int depth)
{
return new Dt_LocationInfo
{
WarehouseId = 0,
Row = row,
Column = col,
Layer = layer,
Depth = depth,
RoadwayNo = roadwayNo,
EnableStatus = EnableStatusEnum.Normal.GetHashCode(),
LocationStatus = LocationStatusEnum.Free.GetHashCode(),
LocationType = LocationTypeEnum.Undefined.GetHashCode(),
LocationCode = $"{row:D3}-{col:D3}-{layer:D3}",
LocationName = $"{roadwayNo}巷道{row:D3}行{col:D3}列{layer:D3}层{depth:D2}深"
};
}
#endregion 私有方法
}
}