wanshenmean
8 天以前 559bb7b4be83575cc5fedee98484647243c96f89
Code/WMS/WIDESEA_WMSServer/WIDESEA_BasicService/LocationInfoService.cs
@@ -11,6 +11,7 @@
using WIDESEA_DTO.Basic;
using WIDESEA_DTO.Task;
using WIDESEA_IBasicService;
using WIDESEA_IRecordService;
using WIDESEA_Model.Models;
namespace WIDESEA_BasicService
@@ -23,6 +24,8 @@
        private readonly IMapper _mapper;
        private readonly IRepository<Dt_Task> _taskRepository;
        private readonly IRepository<Dt_StockInfo> _stockInfoRepository;
        private readonly IRecordService _recordService;
        private readonly IRepository<Dt_Warehouse> _warehouseRepository;
        /// <summary>
        /// 构造函数
@@ -34,11 +37,15 @@
            IRepository<Dt_LocationInfo> baseDal,
            IRepository<Dt_Task> taskRepository,
            IRepository<Dt_StockInfo> stockInfoRepository,
            IMapper mapper) : base(baseDal)
            IRepository<Dt_Warehouse> warehouseRepository,
            IMapper mapper,
            IRecordService recordService) : base(baseDal)
        {
            _taskRepository = taskRepository;
            _stockInfoRepository = stockInfoRepository;
            _mapper = mapper;
            _recordService = recordService;
            _warehouseRepository = warehouseRepository;
        }
        /// <summary>
@@ -137,10 +144,10 @@
                x.LocationStatus == LocationStatusEnum.Free.GetHashCode());
            return locations?
                .OrderBy(x => x.Layer)
                .ThenByDescending(x => x.Depth)
                .ThenBy(x => x.Column)
                .ThenBy(x => x.Row)
                .OrderByDescending(x => x.Depth)  // 1. 深度优先(从大到小)
                .ThenBy(x => x.Layer)             // 2. 层数
                .ThenBy(x => x.Column)            // 3. 列
                .ThenBy(x => x.Row)               // 4. 行
                .FirstOrDefault();
        }
@@ -166,13 +173,64 @@
        }
        /// <summary>
        /// 根据货位ID获取货位信息
        /// </summary>
        /// <param name="id">货位id</param>
        /// <returns>货位信息</returns>
        public async Task<Dt_LocationInfo> GetLocationInfoAsync(int id)
        {
            return await BaseDal.QueryFirstAsync(x => x.Id == id);
        }
        /// <summary>
        /// 更新货位信息
        /// </summary>
        /// <param name="locationInfo">货位信息对象</param>
        /// <returns>更新是否成功</returns>
        public async Task<bool> UpdateLocationInfoAsync(Dt_LocationInfo locationInfo)
        {
            return await BaseDal.UpdateDataAsync(locationInfo);
            var beforeLocation = await BaseDal.QueryFirstAsync(x => x.Id == locationInfo.Id);
            var result = await BaseDal.UpdateDataAsync(locationInfo);
            if (!result)
                return false;
            return beforeLocation == null
                || await _recordService.AddLocationChangeRecordAsync(beforeLocation, locationInfo, LocationChangeType.HandUpdate, remark: "货位更新");
        }
        public override WebResponseContent UpdateData(Dt_LocationInfo entity)
        {
            var beforeLocation = BaseDal.QueryFirst(x => x.Id == entity.Id);
            var result = base.UpdateData(entity);
            if (!result.Status || beforeLocation == null)
                return result;
            var saveRecordResult = _recordService.AddLocationChangeRecordAsync(beforeLocation, entity, LocationChangeType.HandUpdate, remark: "货位更新").GetAwaiter().GetResult();
            return saveRecordResult ? result : WebResponseContent.Instance.Error("货位状态变更记录保存失败");
        }
        public override WebResponseContent UpdateData(List<Dt_LocationInfo> entities)
        {
            var beforeLocations = entities
                .Select(entity => BaseDal.QueryFirst(x => x.Id == entity.Id))
                .Where(location => location != null)
                .ToDictionary(location => location!.Id, location => location!);
            var result = base.UpdateData(entities);
            if (!result.Status)
                return result;
            foreach (var entity in entities)
            {
                if (!beforeLocations.TryGetValue(entity.Id, out var beforeLocation))
                    continue;
                var saveRecordResult = _recordService.AddLocationChangeRecordAsync(beforeLocation, entity, LocationChangeType.HandUpdate, remark: "批量货位更新").GetAwaiter().GetResult();
                if (!saveRecordResult)
                    return WebResponseContent.Instance.Error("货位状态变更记录保存失败");
            }
            return result;
        }
        /// <summary>
@@ -281,6 +339,9 @@
            };
            var createdTask = await _taskRepository.Db.Insertable(newTransferTask).ExecuteReturnEntityAsync();
            var beforeStock = CloneStockSnapshot(stockInfo);
            var beforeSourceLocation = stockInfo.LocationDetails == null ? null : CloneLocationSnapshot(stockInfo.LocationDetails);
            var beforeTargetLocation = CloneLocationSnapshot(emptyLocation);
            // 创建移库任务后,立即锁定库存和相关货位,避免并发重复分配
            stockInfo.StockStatus = StockStatusEmun.移库锁定.GetHashCode();
@@ -302,7 +363,102 @@
                throw new Exception("创建移库任务后更新库存状态或货位状态失败");
            }
            var saveStockRecordResult = await _recordService.AddStockChangeRecordAsync(
                beforeStock,
                stockInfo,
                StockChangeTypeEnum.Relocation,
                createdTask.TaskNum,
                createdTask.OrderNo,
                "移库任务预占库存");
            if (!saveStockRecordResult)
            {
                throw new Exception("创建移库任务后记录库存变更失败");
            }
            if (beforeSourceLocation != null && stockInfo.LocationDetails != null)
            {
                var saveSourceLocationRecordResult = await _recordService.AddLocationChangeRecordAsync(
                    beforeSourceLocation,
                    stockInfo.LocationDetails,
                    LocationChangeType.RelocationAssignLocation,
                    createdTask.TaskNum,
                    createdTask.OrderNo,
                    null,
                    "移库任务锁定源货位");
                if (!saveSourceLocationRecordResult)
                {
                    throw new Exception("创建移库任务后记录源货位变更失败");
                }
            }
            var saveTargetLocationRecordResult = await _recordService.AddLocationChangeRecordAsync(
                beforeTargetLocation,
                emptyLocation,
                LocationChangeType.RelocationAssignLocation,
                createdTask.TaskNum,
                createdTask.OrderNo,
                null,
                "移库任务锁定目标货位");
            if (!saveTargetLocationRecordResult)
            {
                throw new Exception("创建移库任务后记录目标货位变更失败");
            }
            return createdTask;
        }
        private static Dt_LocationInfo CloneLocationSnapshot(Dt_LocationInfo location)
        {
            return new Dt_LocationInfo
            {
                Id = location.Id,
                WarehouseId = location.WarehouseId,
                LocationCode = location.LocationCode,
                LocationName = location.LocationName,
                RoadwayNo = location.RoadwayNo,
                Row = location.Row,
                Column = location.Column,
                Layer = location.Layer,
                Depth = location.Depth,
                LocationType = location.LocationType,
                LocationStatus = location.LocationStatus,
                EnableStatus = location.EnableStatus,
                Remark = location.Remark
            };
        }
        private static Dt_StockInfo CloneStockSnapshot(Dt_StockInfo stockInfo)
        {
            return new Dt_StockInfo
            {
                Id = stockInfo.Id,
                PalletCode = stockInfo.PalletCode,
                PalletType = stockInfo.PalletType,
                LocationId = stockInfo.LocationId,
                LocationCode = stockInfo.LocationCode,
                WarehouseId = stockInfo.WarehouseId,
                StockStatus = stockInfo.StockStatus,
                Remark = stockInfo.Remark,
                OutboundDate = stockInfo.OutboundDate,
                Details = stockInfo.Details?.Select(detail => new Dt_StockInfoDetail
                {
                    Id = detail.Id,
                    StockId = detail.StockId,
                    MaterielCode = detail.MaterielCode,
                    MaterielName = detail.MaterielName,
                    OrderNo = detail.OrderNo,
                    BatchNo = detail.BatchNo,
                    ProductionDate = detail.ProductionDate,
                    EffectiveDate = detail.EffectiveDate,
                    SerialNumber = detail.SerialNumber,
                    StockQuantity = detail.StockQuantity,
                    OutboundQuantity = detail.OutboundQuantity,
                    Status = detail.Status,
                    Unit = detail.Unit,
                    InboundOrderRowNo = detail.InboundOrderRowNo,
                    Remark = detail.Remark
                }).ToList()
            };
        }
        /// <summary>
@@ -351,11 +507,18 @@
        /// <param name="layer">层数</param>
        /// <param name="depth">深度</param>
        /// <returns>货位信息对象</returns>
        private static Dt_LocationInfo CreateLocationInfo(string roadwayNo, int row, int col, int layer, int depth)
        private Dt_LocationInfo CreateLocationInfo(string roadwayNo, int row, int col, int layer, int depth)
        {
            var warehouse = _warehouseRepository.QueryData(x => x.WarehouseCode == roadwayNo).FirstOrDefault();
            if (warehouse == null)
            {
                throw new InvalidOperationException($"未找到巷道编号为 {roadwayNo} 的仓库信息");
            }
            return new Dt_LocationInfo
            {
                WarehouseId = 0,
                WarehouseId = warehouse.WarehouseId,
                Row = row,
                Column = col,
                Layer = layer,
@@ -364,7 +527,7 @@
                EnableStatus = EnableStatusEnum.Normal.GetHashCode(),
                LocationStatus = LocationStatusEnum.Free.GetHashCode(),
                LocationType = LocationTypeEnum.Undefined.GetHashCode(),
                LocationCode = $"{row:D3}-{col:D3}-{layer:D3}",
                LocationCode = $"{roadwayNo}-{row:D3}-{col:D3}-{layer:D3}",
                LocationName = $"{roadwayNo}巷道{row:D3}行{col:D3}列{layer:D3}层{depth:D2}深"
            };
        }