wanshenmean
6 天以前 5171d3f59b89389bf75293afd210cfa6de4ccff7
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
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
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
    {
        /// <summary>
        /// 获取库存信息仓储接口
        /// </summary>
        public IRepository<Dt_StockInfo> Repository => 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)
        {
            _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
            };
        }
    }
}