wanshenmean
2026-03-30 1f15dd43b334e31189c4580b98f57d2cc71935d8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
using WIDESEA_Common.StockEnum;
using WIDESEA_Core.BaseRepository;
using WIDESEA_Core.BaseServices;
using WIDESEA_DTO.Stock;
using WIDESEA_IBasicService;
using WIDESEA_IStockService;
using WIDESEA_Model.Models;
 
namespace WIDESEA_StockService
{
    /// <summary>
    /// 库存信息服务实现类
    /// </summary>
    public partial class StockInfoService : ServiceBase<Dt_StockInfo, IRepository<Dt_StockInfo>>, IStockInfoService
    {
        /// <summary>
        /// 获取库存信息仓储接口
        /// </summary>
        public IRepository<Dt_StockInfo> Repository => BaseDal;
 
        /// <summary>
        /// 货位信息服务接口(用于获取仓库货位信息)
        /// </summary>
        private readonly ILocationInfoService _locationInfoService;
 
        /// <summary>
        /// 仓库信息服务接口(用于获取仓库基本信息)
        /// </summary>
        private readonly IWarehouseService _warehouseService;
 
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="baseDal">基础数据访问对象</param>
        public StockInfoService(IRepository<Dt_StockInfo> baseDal, ILocationInfoService locationInfoService, IWarehouseService warehouseService) : base(baseDal)
        {
            _locationInfoService = locationInfoService;
            _warehouseService = warehouseService;
        }
 
        /// <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)
        {
            return await BaseDal.UpdateDataAsync(stockInfo);
        }
 
        /// <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);
 
            // 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) && stockInfo.Details != null)
                {
                    item.PalletCode = stockInfo.PalletCode;
                    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;
                    }
 
                    // 计算库存状态
                    var ratio = item.MaxCapacity > 0 ? item.StockQuantity / item.MaxCapacity : 0;
                    if (ratio >= 0.9f)
                        item.StockStatus = 3; // 已满 (FULL)
                    else if (ratio >= 0.1f)
                        item.StockStatus = 1; // 有货 (HAS_STOCK)
                    else if (ratio > 0)
                        item.StockStatus = 2; // 库存紧张 (LOW_STOCK)
                    else
                        item.StockStatus = 0; // 无货 (EMPTY)
                }
                else
                {
                    item.StockStatus = 0; // 无货 (EMPTY)
                    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
            };
        }
    }
}