wanshenmean
6 天以前 5171d3f59b89389bf75293afd210cfa6de4ccff7
Code/WMS/WIDESEA_WMSServer/WIDESEA_StockService/StockInfoService.cs
@@ -1,21 +1,320 @@
using AutoMapper;
using WIDESEA_Common.StockEnum;
using WIDESEA_Core;
using WIDESEA_Core.BaseRepository;
using WIDESEA_Core.BaseServices;
using WIDESEA_DTO.Stock;
using WIDESEA_IBasicService;
using WIDESEA_IRecordService;
using WIDESEA_IStockService;
using WIDESEA_Model.Models;
namespace WIDESEA_StockService
{
    /// <summary>
    /// 库存信息服务实现类
    /// </summary>
    public partial class StockInfoService : ServiceBase<Dt_StockInfo, IRepository<Dt_StockInfo>>, IStockInfoService
    {
        private readonly IMapper _mapper;
        /// <summary>
        /// 获取库存信息仓储接口
        /// </summary>
        public IRepository<Dt_StockInfo> Repository => BaseDal;
        public StockInfoService(IRepository<Dt_StockInfo> BaseDal, IMapper mapper) : base(BaseDal)
        /// <summary>
        /// 货位信息服务接口(用于获取仓库货位信息)
        /// </summary>
        private readonly ILocationInfoService _locationInfoService;
        /// <summary>
        /// 仓库信息服务接口(用于获取仓库基本信息)
        /// </summary>
        private readonly IWarehouseService _warehouseService;
        private readonly IRecordService _recordService;
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="baseDal">基础数据访问对象</param>
        public StockInfoService(
            IRepository<Dt_StockInfo> baseDal,
            ILocationInfoService locationInfoService,
            IWarehouseService warehouseService,
            IRecordService recordService) : base(baseDal)
        {
            _mapper = mapper;
            _locationInfoService = locationInfoService;
            _warehouseService = warehouseService;
            _recordService = recordService;
        }
        /// <summary>
        /// 获取库存信息列表(出库日期小于当前时间且库存状态为入库完成的记录)
        /// </summary>
        /// <returns>库存信息列表</returns>
        public async Task<List<Dt_StockInfo>> GetStockInfoAsync()
        {
            return await BaseDal.QueryDataAsync(x =>
                x.OutboundDate < DateTime.Now &&
                x.StockStatus == StockStatusEmun.入库完成.GetHashCode());
        }
        /// <summary>
        /// 获取库存信息列表(出库日期小于当前时间且库存状态为入库完成的记录,且仓库ID匹配)
        /// </summary>
        /// <param name="warehouseId">仓库ID</param>
        /// <returns>库存信息列表</returns>
        public async Task<List<Dt_StockInfo>> GetStockInfoAsync(int warehouseId)
        {
            return await BaseDal.QueryDataAsync(x =>
                x.OutboundDate < DateTime.Now &&
                x.StockStatus == StockStatusEmun.入库完成.GetHashCode() &&
                x.WarehouseId == warehouseId);
        }
        /// <summary>
        /// 获取库存信息(根据托盘码查询)
        /// </summary>
        /// <param name="palletCode">托盘编码</param>
        /// <returns>库存信息</returns>
        public async Task<Dt_StockInfo> GetStockInfoAsync(string palletCode)
        {
            return await BaseDal.QueryDataNavFirstAsync(x => x.PalletCode == palletCode);
        }
        /// <summary>
        /// 更新库存数据
        /// </summary>
        /// <param name="stockInfo">库存信息对象</param>
        /// <returns>更新是否成功</returns>
        public async Task<bool> UpdateStockAsync(Dt_StockInfo stockInfo)
        {
            var beforeStock = await BaseDal.QueryDataNavFirstAsync(x => x.Id == stockInfo.Id);
            var result = await BaseDal.UpdateDataAsync(stockInfo);
            if (!result)
                return false;
            var afterStock = await BaseDal.QueryDataNavFirstAsync(x => x.Id == stockInfo.Id) ?? stockInfo;
            var changeType = ResolveChangeType(beforeStock, afterStock);
            return await _recordService.AddStockChangeRecordAsync(beforeStock, afterStock, changeType, remark: "库存更新");
        }
        public override WebResponseContent AddData(Dt_StockInfo entity)
        {
            var result = base.AddData(entity);
            if (!result.Status)
                return result;
            var saveRecordResult = _recordService.AddStockChangeRecordAsync(null, entity, StockChangeTypeEnum.Inbound, remark: "库存新增").GetAwaiter().GetResult();
            return saveRecordResult ? result : WebResponseContent.Instance.Error("库存变更记录保存失败");
        }
        public override WebResponseContent UpdateData(Dt_StockInfo entity)
        {
            var beforeStock = BaseDal.QueryFirst(x => x.Id == entity.Id);
            var result = base.UpdateData(entity);
            if (!result.Status)
                return result;
            var changeType = ResolveChangeType(beforeStock, entity);
            var saveRecordResult = _recordService.AddStockChangeRecordAsync(beforeStock, entity, changeType, remark: "库存更新").GetAwaiter().GetResult();
            return saveRecordResult ? result : WebResponseContent.Instance.Error("库存变更记录保存失败");
        }
        public override WebResponseContent DeleteData(Dt_StockInfo entity)
        {
            var beforeStock = CloneStockSnapshot(entity);
            var result = base.DeleteData(entity);
            if (!result.Status)
                return result;
            var saveRecordResult = _recordService.AddStockChangeRecordAsync(beforeStock, null, StockChangeTypeEnum.Outbound, remark: "库存删除").GetAwaiter().GetResult();
            return saveRecordResult ? result : WebResponseContent.Instance.Error("库存变更记录保存失败");
        }
        private static StockChangeTypeEnum ResolveChangeType(Dt_StockInfo? beforeStock, Dt_StockInfo? afterStock)
        {
            if (beforeStock == null)
                return StockChangeTypeEnum.Inbound;
            if (afterStock == null)
                return StockChangeTypeEnum.Outbound;
            if (!string.Equals(beforeStock.LocationCode, afterStock.LocationCode, StringComparison.OrdinalIgnoreCase))
                return StockChangeTypeEnum.Relocation;
            if (beforeStock.StockStatus != afterStock.StockStatus)
                return StockChangeTypeEnum.StockLock;
            var beforeQuantity = beforeStock.Details?.Sum(x => x.StockQuantity) ?? 0;
            var afterQuantity = afterStock.Details?.Sum(x => x.StockQuantity) ?? 0;
            return afterQuantity >= beforeQuantity ? StockChangeTypeEnum.Inbound : StockChangeTypeEnum.Outbound;
        }
        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>
        /// 检索指定托盘在给定位置的库存详细信息
        /// </summary>
        /// <param name="palletCode">托盘编码</param>
        /// <param name="locationCode">货位编码</param>
        /// <returns>库存信息</returns>
        public async Task<Dt_StockInfo> GetStockInfoAsync(string palletCode, string locationCode)
        {
            return await BaseDal.QueryFirstAsync(x => x.PalletCode == palletCode && x.LocationCode == locationCode);
        }
        /// <summary>
        /// 获取仓库3D布局数据
        /// </summary>
        /// <param name="warehouseId">仓库ID</param>
        /// <returns>3D布局DTO</returns>
        public async Task<Stock3DLayoutDTO> Get3DLayoutAsync(int warehouseId)
        {
            // 1. 查询仓库信息
            var warehouse = await _warehouseService.Repository.QueryFirstAsync(x => x.WarehouseId == warehouseId);
            // 2. 查询该仓库所有货位
            var locations = await _locationInfoService.Repository.QueryDataAsync(x => x.WarehouseId == warehouseId);
            // 3. 查询该仓库所有库存信息(包含Details导航属性)
            var stockInfos = await Repository.QueryDataNavAsync(x => x.WarehouseId == warehouseId && x.LocationId != 0);
            // 4. 提取物料编号和批次号列表(去重)
            var materielCodeList = stockInfos
                .Where(s => s.Details != null)
                .SelectMany(s => s.Details)
                .Select(d => d.MaterielCode)
                .Where(c => !string.IsNullOrEmpty(c))
                .Distinct()
                .ToList();
            var batchNoList = stockInfos
                .Where(s => s.Details != null)
                .SelectMany(s => s.Details)
                .Select(d => d.BatchNo)
                .Where(b => !string.IsNullOrEmpty(b))
                .Distinct()
                .ToList();
            // 5. 创建库存字典用于快速查找(以LocationId为键)
            var stockDict = stockInfos.ToDictionary(s => s.LocationId, s => s);
            // 6. 映射每个货位到Location3DItemDTO
            const float defaultMaxCapacity = 100f;
            var locationItems = locations.Select(loc =>
            {
                var item = new Location3DItemDTO
                {
                    LocationId = loc.Id,
                    LocationCode = loc.LocationCode,
                    Row = loc.Row,
                    Column = loc.Column,
                    Layer = loc.Layer,
                    LocationStatus = loc.LocationStatus,
                    MaxCapacity = defaultMaxCapacity
                };
                // 尝试从库存字典中获取库存信息
                if (stockDict.TryGetValue(loc.Id, out var stockInfo))
                {
                    // 空托盘也有库存记录,只是不包含明细
                    item.PalletCode = stockInfo.PalletCode;
                    item.StockStatus = stockInfo.StockStatus; // 直接使用后端库存状态
                    // 只有当Details不为null且有数据时才处理库存明细
                    if (stockInfo.Details != null && stockInfo.Details.Any())
                    {
                        item.StockQuantity = stockInfo.Details.Sum(d => d.StockQuantity);
                        // 获取第一个明细的物料信息(如果存在)
                        var firstDetail = stockInfo.Details.FirstOrDefault();
                        if (firstDetail != null)
                        {
                            item.MaterielCode = firstDetail.MaterielCode;
                            item.MaterielName = firstDetail.MaterielName;
                            item.BatchNo = firstDetail.BatchNo;
                        }
                        // 填充库存明细列表
                        item.Details = stockInfo.Details.Select(d => new StockDetailItemDTO
                        {
                            Id = d.Id,
                            MaterielCode = d.MaterielCode,
                            MaterielName = d.MaterielName,
                            BatchNo = d.BatchNo,
                            StockQuantity = d.StockQuantity,
                            Unit = d.Unit,
                            ProductionDate = d.ProductionDate,
                            EffectiveDate = d.EffectiveDate,
                            OrderNo = d.OrderNo,
                            Status = d.Status
                        }).ToList();
                    }
                    else
                    {
                        // 空托盘(无明细)
                        item.StockQuantity = 0;
                        item.Details = new List<StockDetailItemDTO>(); // 确保是空列表而非null
                    }
                }
                else
                {
                    // 无库存记录,货位为空
                    item.StockStatus = 0; // 空闲
                    item.StockQuantity = 0;
                }
                return item;
            }).ToList();
            // 7. 计算仓库尺寸
            var maxRow = locations.Any() ? locations.Max(l => l.Row) : 0;
            var maxColumn = locations.Any() ? locations.Max(l => l.Column) : 0;
            var maxLayer = locations.Any() ? locations.Max(l => l.Layer) : 0;
            // 8. 构建返回结果
            return new Stock3DLayoutDTO
            {
                WarehouseId = warehouseId,
                WarehouseName = warehouse?.WarehouseName ?? string.Empty,
                MaxRow = maxRow,
                MaxColumn = maxColumn,
                MaxLayer = maxLayer,
                MaterielCodeList = materielCodeList,
                BatchNoList = batchNoList,
                Locations = locationItems
            };
        }
    }
}