schangxiang@126.com
2025-05-19 b586f4883139022280ac994d7ed02906c0f6c89e
HIAWms/server/src/CMS.Plugin.HIAWms.Application/Implements/WmsCommonAppService.cs
@@ -1,38 +1,71 @@
using CMS.Plugin.HIAWms.Application.Contracts.Dtos.CommonDto;
using CMS.Plugin.HIAWms.Application.Contracts.Dtos.WmsMaterialStocks;
using CMS.Plugin.HIAWms.Application.Contracts.Dtos.WmsPlaces;
using CMS.Plugin.HIAWms.Application.Contracts.Dtos.WmsPlace;
using CMS.Plugin.HIAWms.Application.Contracts.Services;
using CMS.Plugin.HIAWms.Domain.Shared.Enums;
using CMS.Plugin.HIAWms.Domain.WmsInOutStockRecord;
using CMS.Plugin.HIAWms.Domain.WmsMaterials;
using CMS.Plugin.HIAWms.Domain.WmsMaterialStocks;
using CMS.Plugin.HIAWms.Domain.WmsPlaces;
using CmsQueryExtensions.Extension;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Uow;
namespace CMS.Plugin.HIAWms.Application.Implements
{
    /// <summary>
    /// 公共操作服务
    /// </summary>
    public class WmsCommonAppService : CMSPluginAppService,IWmsCommonAppService
    public class WmsCommonAppService : CMSPluginAppService, IWmsCommonAppService
    {
        private readonly IWmsMaterialRepository _wmsMaterialRepository;
        private readonly IWmsPlaceRepository _wmsPlaceRepository;
        private readonly IWmsMaterialStockRepository _wmsMaterialStockRepository;
        private readonly IWmsInOutStockRecordRepository _wmsInOutStockRecordRepository;
        private readonly IServiceProvider _serviceProvider;
        public WmsCommonAppService(IWmsMaterialRepository wmsMaterialRepository,
            IWmsPlaceRepository wmsPlaceRepository,
            IWmsMaterialStockRepository wmsMaterialStockRepository
            )
,
            IServiceProvider serviceProvider,
            IWmsInOutStockRecordRepository wmsInOutStockRecordRepository)
        {
            _wmsMaterialRepository = wmsMaterialRepository;
            _wmsPlaceRepository = wmsPlaceRepository;
            _wmsMaterialStockRepository = wmsMaterialStockRepository;
            _serviceProvider = serviceProvider;
            _wmsInOutStockRecordRepository = wmsInOutStockRecordRepository;
        }
        /// 动态构造查询条件
        /// </summary>
        /// <param name="input">输入参数</param>
        /// <returns></returns>
        private FunReturnResultModel<Expression<Func<WmsPlace, bool>>> DynamicGetQueryPlaceParams(GetWmsPlaceInput input)
        {
            // 动态构造查询条件
            var whereConditions = WhereConditionsExtensions.GetWhereConditions<WmsPlace, GetWmsPlaceInput>(input);
            if (!whereConditions.IsSuccess)
            {
                throw new Exception("动态构造查询条件失败:" + whereConditions.ErrMsg);
            }
            //也可再次自定义构建查询条件
            Expression<Func<WmsPlace, bool>> extendExpression = a => a.IsDeleted == false;
            // 使用 System.Linq.PredicateBuilder 的 And
            var pres = (System.Linq.Expressions.Expression<Func<WmsPlace, bool>>)(whereConditions.data);
            whereConditions.data = System.Linq.PredicateBuilder.And(pres, extendExpression);
            return whereConditions;
        }
        /// <summary>
        /// 查找空库位
@@ -42,12 +75,17 @@
        /// <param name="requiredNum"></param>
        /// <returns></returns>
        /// <exception cref="UserFriendlyException"></exception>
        public async Task<Dictionary<WmsPlaceDto, int>> FindAvailablePlacesAsync(string materialModel,int requiredNum, string materialNo="" )
        public async Task<Dictionary<WmsPlaceDto, int>> FindAvailablePlacesAsync(string materialModel, int requiredNum, string materialNo = "")
        {
            // 1. 获取所有库存和库位信息
            var stockList = await _wmsMaterialStockRepository.GetListAsync(new WmsMaterialStock());
            var allPlaceList = ObjectMapper.Map<List<WmsPlace>, List<WmsPlaceDto>>(await _wmsPlaceRepository.GetListAsync(new WmsPlace()));
            var whereConditions = DynamicGetQueryParams(new GetWmsMaterialStockInput { StorageTypeNo = Domain.Shared.Enums.PlaceTypeEnum.YUANLIAOKUWEI });
            // 1. 获取所有库存和库位信息
            var stockList = await _wmsMaterialStockRepository.GetListAsync(whereConditions);
            var placeConditions = DynamicGetQueryPlaceParams(new GetWmsPlaceInput { StorageTypeNo = (int)PlaceTypeEnum.YUANLIAOKUWEI });
            var placeList = await _wmsPlaceRepository.GetListByFilterAsync(placeConditions.data);
            var allPlaceList = ObjectMapper.Map<List<WmsPlace>, List<WmsPlaceDto>>(placeList.Where(x => !x.IsDisabled).ToList());
            // 2. 查找相同物料型号和编号的库存(按库存量降序)
            var sameModelStocks = stockList
@@ -109,12 +147,28 @@
        /// <param name="requiredNum"></param>
        /// <param name="materialNo"></param>
        /// <returns></returns>
        public async Task<Dictionary<WmsMaterialStockDto, int>> FindStockAsync(string materialModel, int requiredNum, string materialNo = "")
        public async Task<Dictionary<WmsMaterialStockDto, int>> FindStockAsync(string materialModel, int requiredNum, string placeNo = "", PlaceTypeEnum placeType = PlaceTypeEnum.YUANLIAOKUWEI, string materialNo = "")
        {
            var allocation = new Dictionary<WmsMaterialStockDto, int>(); // <库位号, 出库数>
            // 1. 获取所有库存(排除锁定库存)
            var stockList = (await _wmsMaterialStockRepository.GetListAsync(new WmsMaterialStock()))
            var input = new GetWmsMaterialStockInput();
            var whereConditions = DynamicGetQueryParams(new GetWmsMaterialStockInput());
            var stockList = (await _wmsMaterialStockRepository.GetListAsync(whereConditions))
                .Where(x => x.IsLock == Domain.Shared.Enums.YesNoEnum.N)
                .Where(x => x.StorageTypeNo == placeType)
                .ToList();
            if (!string.IsNullOrEmpty(placeNo))
            {
                var stock = stockList.Where(x => x.PlaceNo == placeNo).FirstOrDefault();
                if (stock.StockNumber < requiredNum)
                {
                    throw new UserFriendlyException($"库位{placeNo}库存不足,可用: {stock.StockNumber}, 缺: {requiredNum - stock.StockNumber}");
                }
                allocation.Add(ObjectMapper.Map<WmsMaterialStock, WmsMaterialStockDto>(stock), requiredNum);
                return allocation;
            }
            // 2. 筛选匹配物料
            var availableStocklist = stockList
@@ -134,7 +188,6 @@
            }
            // 4. 计算各库位出库数量
            var allocation = new Dictionary<WmsMaterialStockDto, int>(); // <库位号, 出库数>
            int remaining = requiredNum;
            foreach (var stock in availableStocks)
@@ -148,5 +201,172 @@
            return allocation;
        }
        private FunReturnResultModel<Expression<Func<WmsMaterialStock, bool>>> DynamicGetQueryParams(GetWmsMaterialStockInput input)
        {
            //动态构造查询条件
            var whereConditions = WhereConditionsExtensions.GetWhereConditions<WmsMaterialStock, GetWmsMaterialStockInput>(input);
            if (!whereConditions.IsSuccess)
            {
                throw new Exception("动态构造查询条件失败:" + whereConditions.ErrMsg);
            }
            //也可再次自定义构建查询条件
            Expression<Func<WmsMaterialStock, bool>> extendExpression = a => a.IsDeleted == false;
            // 使用 System.Linq.PredicateBuilder 的 And
            var pres = (System.Linq.Expressions.Expression<Func<WmsMaterialStock, bool>>)(whereConditions.data);
            whereConditions.data = System.Linq.PredicateBuilder.And(pres, extendExpression);
            return whereConditions;
        }
        /// <summary>
        /// 扣减库存
        /// </summary>
        /// <returns></returns>
        public async Task<List<WmsMaterialStockDto>> ReduceMaterialStockAsync(ReduceStockInput input)
        {
            using var scope = _serviceProvider.CreateScope();
            var unitOfWorkManager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>();
            using var uow = unitOfWorkManager.Begin(requiresNew: true);
            var materialModel = await _wmsMaterialRepository.FindByModelAsync(input.MaterialModel);
            if (materialModel == null)
            {
                throw new UserFriendlyException("物料型号不存在");
            }
            var placeInfo = await _wmsPlaceRepository.FindByNameAsync(input.PlaceNo);
            if (placeInfo == null)
            {
                throw new UserFriendlyException("库位信息不存在");
            }
            var stockresult = await FindStockAsync(input.MaterialModel, input.StockNumber, input.PlaceNo, placeInfo.StorageTypeNo);
            if (stockresult == null)
            {
                throw new UserFriendlyException("当前无库存");
            }
            var recordList = new List<WmsInOutStockRecord>();
            var delStock = new List<WmsMaterialStock>();
            foreach (var kvp in stockresult)
            {
                var stock = kvp.Key;    // WmsPlace对象
                var quantity = kvp.Value; // 分配数量
                // 扣减库存
                var stockList = await _wmsMaterialStockRepository.GetStockListAsync(new WmsMaterialStock { MaterialModel = input.MaterialModel, PlaceNo = stock.PlaceNo });
                var reduceStockList = stockList.OrderBy(x => x.InStockTime).Take(quantity).ToList();
                delStock.AddRange(reduceStockList);
                // 出入库记录
                foreach (var item in reduceStockList)
                {
                    var record = new WmsInOutStockRecord
                    {
                        TaskNo = string.IsNullOrEmpty(input.OrderNo) ? "Task_" + DateTime.Now.ToString("yyyyMMddHHmmssfff") : input.OrderNo,
                        OrderNo = input.OrderNo,
                        StockType = StockTypeEnum.Move,
                        ContainerNo = item.ContainerNo,
                        MaterialId = item.MaterialId,
                        MaterialModel = item.MaterialModel,
                        MaterialName = item.MaterialName,
                        MaterialNo = item.MaterialNo,
                        MaterialBatch = item.MaterialBatch,
                        SourcePlace = item.PlaceNo,
                        ToPlace = "出库",
                        OperateTime = DateTime.Now,
                        Remark = "出库扣减",
                    };
                    recordList.Add(record);
                }
            }
            await _wmsMaterialStockRepository.DeleteManyAsync(delStock);
            await _wmsInOutStockRecordRepository.InsertManyAsync(recordList);
            await uow.SaveChangesAsync();
            await uow.CompleteAsync();
            var result = ObjectMapper.Map<List<WmsMaterialStock>, List<WmsMaterialStockDto>>(delStock);
            return result;
        }
        /// <summary>
        /// 库存转移
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<List<WmsMaterialStockDto>> MoveMaterialStock(MoveStockInput input)
        {
            using var scope = _serviceProvider.CreateScope();
            var unitOfWorkManager = scope.ServiceProvider.GetRequiredService<IUnitOfWorkManager>();
            using var uow = unitOfWorkManager.Begin(requiresNew: true);
            var materialModel = await _wmsMaterialRepository.FindByModelAsync(input.MaterialModel);
            if (materialModel == null)
            {
                throw new UserFriendlyException("物料型号不存在");
            }
            var placeInfo = await _wmsPlaceRepository.FindByNameAsync(input.SourcePlace);
            if (placeInfo == null)
            {
                throw new UserFriendlyException("来源库位信息不存在");
            }
            var toPlace = await _wmsPlaceRepository.FindByNameAsync(input.ToPlace);
            if (toPlace == null)
            {
                throw new UserFriendlyException("目标库位信息不存在");
            }
            var stockresult = await FindStockAsync(input.MaterialModel, input.StockNumber, input.SourcePlace, placeInfo.StorageTypeNo);
            if (stockresult == null)
            {
                throw new UserFriendlyException("当前无库存");
            }
            var recordList = new List<WmsInOutStockRecord>();
            var result = new List<WmsMaterialStockDto>();
            foreach (var kvp in stockresult)
            {
                var stock = kvp.Key;    // WmsPlace对象
                var quantity = kvp.Value; // 分配数量
                // 扣减转移
                var stockList = await _wmsMaterialStockRepository.GetStockListAsync(new WmsMaterialStock { MaterialModel = input.MaterialModel, PlaceNo = stock.PlaceNo });
                var reduceStockList = stockList.OrderBy(x => x.InStockTime).Take(quantity).ToList();
                // 出入库记录
                foreach (var item in reduceStockList)
                {
                    var record = new WmsInOutStockRecord
                    {
                        TaskNo = string.IsNullOrEmpty(input.OrderNo) ? "Task_" + DateTime.Now.ToString("yyyyMMddHHmmssfff") : input.OrderNo,
                        OrderNo = input.OrderNo,
                        StockType = StockTypeEnum.Move,
                        ContainerNo = item.ContainerNo,
                        MaterialId = item.MaterialId,
                        MaterialModel = item.MaterialModel,
                        MaterialName = item.MaterialName,
                        MaterialNo = item.MaterialNo,
                        MaterialBatch = item.MaterialBatch,
                        SourcePlace = item.PlaceNo,
                        ToPlace = input.ToPlace,
                        OperateTime = DateTime.Now,
                        Remark = "出库扣减",
                    };
                    recordList.Add(record);
                    item.PlaceNo = input.ToPlace;
                }
                await _wmsMaterialStockRepository.UpdateManyAsync(reduceStockList);
                await _wmsInOutStockRecordRepository.InsertManyAsync(recordList);
                result.AddRange(ObjectMapper.Map<List<WmsMaterialStock>, List<WmsMaterialStockDto>>(reduceStockList));
            }
            await uow.SaveChangesAsync();
            await uow.CompleteAsync();
            return result;
        }
    }
}