1
pengmn
2025-05-29 f2518583da4cc3d3737986e112b92154535aea4d
HH.WCS.Mobox3.HangYang/wms/WMSHelper.cs
@@ -1,5 +1,6 @@
using HH.WCS.HangYang.api;
using HH.WCS.HangYang.LISTA.models;
using HH.WCS.HangYang.LISTA.models.wms;
using HH.WCS.HangYang.models.other;
using HH.WCS.HangYang.util;
using Newtonsoft.Json;
@@ -10,10 +11,12 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IdentityModel.Protocols.WSTrust;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.Remoting.Messaging;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
@@ -39,63 +42,10 @@
            var date = DateTime.Now.ToString("yyMMdd");
            return $"SO{date}{id.ToString().PadLeft(4, '0')}";
        }
        internal static List<WMSTask> GetOperationListByState(string state)
        {
            var db = new SqlHelper<object>().GetInstance();
            return db.Queryable<WMSTask>().Where(a => a.S_B_STATE == state).ToList();
        }
        internal static List<WMSTask> GetOperationListByState(int state)
        {
            var db = new SqlHelper<object>().GetInstance();
            return db.Queryable<WMSTask>().Where(a => a.N_B_STATE == state).ToList();
        }
        internal static List<WMSTask> GetWaitingOperationList()
        {
            var db = new SqlHelper<object>().GetInstance();
            return db.Queryable<WMSTask>().Where(a => a.N_B_STATE == 0 || a.N_B_STATE == 3).ToList();
        }
        internal static PutawayOrder GetPutawayOrder(string no)
        {
            var db = new SqlHelper<object>().GetInstance();
            return db.Queryable<PutawayOrder>().Where(a => a.S_NO == no).First();
        }
        internal static bool CreatePutawayOrder(PutawayOrder model)
        {
            var db = new SqlHelper<object>().GetInstance();
            var result = db.Insertable<PutawayOrder>(model).ExecuteCommand() > 0;
            db.Insertable<PutawayDetail>(model.Details).ExecuteCommand();
            return result;
        }
        internal static PutawayDetail GetPutawayOrderDetail(string no, string item_code)
        {
            var db = new SqlHelper<object>().GetInstance();
            return db.Queryable<PutawayDetail>().Where(a => a.S_PUTAWAY_NO == no && a.S_ITEM_CODE == item_code).First();
        }
        internal static PutawayDetail GetPutawayOrderDetail(string item_code)
        {
            var db = new SqlHelper<object>().GetInstance();
            return db.Queryable<PutawayDetail>().Where(a => a.S_ITEM_CODE == item_code && a.F_QTY - a.F_ACC_B_QTY > 0).OrderByDescending(a => a.T_CREATE).First();
        }
        internal static void UpdatePutawayOrderDetailQty(PutawayDetail model)
        {
            var db = new SqlHelper<object>().GetInstance();
            db.Updateable(model).UpdateColumns(it => new { it.F_ACC_B_QTY }).ExecuteCommand();
        }
        internal static ShippingOrder GetShippingOrder(string no)
        {
            var db = new SqlHelper<object>().GetInstance();
            return db.Queryable<ShippingOrder>().Includes(a => a.Details).Where(a => a.S_NO == no).First();
        }
        internal static bool CreateShippingOrder(ShippingOrder model)
        {
            var db = new SqlHelper<object>().GetInstance();
            var result = db.Insertable<ShippingOrder>(model).ExecuteCommand() > 0;
            db.Insertable<ShippingDetail>(model.Details).ExecuteCommand();
            return result;
        }
        internal static bool CreateSortingOrder(List<string> list)
@@ -270,11 +220,6 @@
            throw new NotImplementedException();
        }
        internal static Location GetStart(WMSTask a)
        {
            throw new NotImplementedException();
        }
        internal static void UpdateTaskState(WMSTask task)
        {
            var db = new SqlHelper<object>().GetInstance();
@@ -290,11 +235,8 @@
            return db.Updateable<WMSTask>(a).UpdateColumns(it => new { it.S_END_LOC, it.T_MODIFY }).ExecuteCommand() > 0;
        }
        internal static WMSTask GetWmsTask(string code)
        {
            var db = new SqlHelper<object>().GetInstance();
            return db.Queryable<WMSTask>().Where(a => a.S_CODE == code).First();
        }
        internal static void CreateSortingOrderDetail(string so_no)
        {
@@ -369,15 +311,6 @@
            }
        }
        /// <summary>
        /// 获取开始配货的分拣单,一次性生成分拣明细,避免生成一半再生成,所以创建分拣明细的时候加上事务
        /// </summary>
        /// <returns></returns>
        internal static List<SortingOrder> GetWaitingSortingOrderList()
        {
            var db = new SqlHelper<object>().GetInstance();
            return db.Queryable<SortingOrder>().Includes(a => a.Composes).Where(a => a.N_B_STATE == 1 || a.N_B_STATE == 20).ToList();
        }
        /// <summary>
        /// 获取配货完成的分拣单,每个分拣单单独创建分拣作业
        /// </summary>
@@ -608,6 +541,7 @@
            }
            return result;
        }
        #region 杭氧WMS帮助方法
        /// <summary>
        /// 新增入库单
@@ -632,7 +566,15 @@
            }
            catch (Exception ex)
            {
                LogHelper.Error("创建入库单失败", ex, "杭氧");
                var sugarEx = ex as SqlSugar.SqlSugarException;
                if (sugarEx != null)
                {
                    LogHelper.Error($"创建入库单SQL错误: {sugarEx.Sql}", sugarEx, "杭氧");
                }
                else
                {
                    LogHelper.Error($"创建入库单失败:{ex.Message}", ex, "杭氧");
                }
                return false;
            }
@@ -661,7 +603,7 @@
            }
            catch (Exception ex)
            {
                LogHelper.Error("创建出库单失败", ex, "杭氧");
                LogHelper.Error($"创建出库单失败:{ex.Message}", ex, "杭氧");
                return false;
            }
@@ -727,6 +669,28 @@
        }
        /// <summary>
        /// 根据托盘查询货位明细
        /// </summary>
        /// <param name="trayCode">托盘号</param>
        /// <returns></returns>
        internal static LocCntrRel GetCntrLoc(string trayCode)
        {
            var db = new SqlHelper<object>().GetInstance();
            var locCntr = db.Queryable<LocCntrRel>().Where(a => a.S_CNTR_CODE.Trim() == trayCode).First();
            var locList = db.Queryable<Location>().Where(p => p.S_CODE == locCntr.S_LOC_CODE
            && p.N_CURRENT_NUM == p.N_CAPACITY
            && p.S_LOCK_STATE.Trim() == "无"
            && p.S_AREA_CODE == "JXHCQ").First();
            if (locList == null)
            {
                return null;
            }
            return locCntr;
        }
        /// <summary>
        /// 任务状态转换
        /// </summary>
        /// <param name="state">状态号</param>
@@ -752,28 +716,48 @@
        /// </summary>
        /// <param name="state">配盘单状态</param>
        /// <returns></returns>
        internal static List<DistributionCntr> GetPickingListByState(string state)
        internal static List<DistributionCntr> GetPickingListByState(int state)
        {
            var db = new SqlHelper<object>().GetInstance();
            var distributionCntr = db.Queryable<DistributionCntr>().Where(a => a.S_B_STATE.Trim() == state).ToList();
            var distributionCntr = db.Queryable<DistributionCntr>().Where(a => a.N_B_STATE == state).ToList();
            return distributionCntr;
        }
        /// <summary>
        /// 配盘单状态转换
        /// </summary>
        /// <param name="state">状态号</param>
        /// <returns></returns>
        internal static string GetDistributionStateStr(int state)
        {
            var status = "";
            switch (state)
            {
                case 1: status = "已配货"; break;
                case 2: status = "出库中"; break;
                case 3: status = "已出库"; break;
                case 4: status = "分拣完成 "; break;
            }
            return status;
        }
        /// <summary>
        /// 修改配盘单状态
        /// </summary>
        /// <param name="taskState">作业状态</param>
        /// <param name="state">状态</param>
        /// <param name="trayCode">托盘号</param>
        /// <returns></returns>
        internal static bool UpdateDistributionCntrState(string state,string trayCode)
        internal static bool UpdateDistributionCntrState(int taskState, int state, string trayCode)
        {
            var db = new SqlHelper<object>().GetInstance();
            var distributionCntr = db.Queryable<DistributionCntr>().Where(a => a.S_B_STATE.Trim() == "配货完成" && a.S_CNTR_CODE == trayCode ).First();
            var distributionCntr = db.Queryable<DistributionCntr>().Where(a => a.N_B_STATE == taskState && a.S_CNTR_CODE == trayCode).First();
            if (distributionCntr != null)
            {
                distributionCntr.S_B_STATE = state;
                distributionCntr.N_B_STATE = 2;
                distributionCntr.N_B_STATE = state;
                distributionCntr.S_B_STATE = GetDistributionStateStr(state);
                return db.Updateable<DistributionCntr>(distributionCntr).UpdateColumns(it => new { it.S_B_STATE, it.N_B_STATE }).ExecuteCommand() > 0;
            }
            return false;
@@ -863,11 +847,19 @@
            return res;
        }
        internal static bool UpdateTask(WMSTask a, int state)
        {
            var db = new SqlHelper<object>().GetInstance();
            a.T_MODIFY = DateTime.Now;
            a.N_B_STATE = state;
            a.S_B_STATE = GetStateStr(state);
            return db.Updateable<WMSTask>(a).UpdateColumns(it => new { it.N_B_STATE, it.S_B_STATE, it.S_CNTR_CODE, it.S_START_LOC, it.T_MODIFY }).ExecuteCommand() > 0;
        }
        #endregion
        #region 杭氧立库出入库逻辑算法
        /// <summary>
        /// 立库入库封装算法
        /// </summary>
@@ -885,7 +877,7 @@
            {
                var db = new SqlHelper<object>().GetInstance();
                var locations = db.Queryable<Location>().Where(a => a.S_AREA_CODE == inbound.areaCode).ToList();
                if(!string.IsNullOrEmpty(inbound.roadWay.ToString()))
                if (inbound.roadWay != 0)
                {
                    locations.RemoveAll(s => s.N_ROADWAY != inbound.roadWay);
                }
@@ -1007,7 +999,7 @@
                return null; // 无可用位置
            }
            private readonly ConcurrentDictionary<string, SemaphoreSlim> _locationLocks = new ConcurrentDictionary<string, SemaphoreSlim>();
            public async Task<LocationParams> StoreItemAsync()
            {
                LocationParams location = null;
@@ -1045,7 +1037,7 @@
        }
        /// <summary>
        /// 立库出库封装算法
        /// 配盘出库封装算法
        /// </summary>
        public class DoubleDeepOutboundScheduler
        {
@@ -1063,7 +1055,7 @@
            // 生成出库任务队列(含移库任务)
            /// <summary>
            /// 立库WMS货位出库算法(运用于配盘单出库、空托出库)
            /// 立库WMS货位出库算法(运用于配盘单出库)
            /// </summary>
            /// <param name="outbound">出库参数</param>
            /// <returns></returns>
@@ -1080,7 +1072,7 @@
                    var wmsTask = WMSHelper.GetWmsTaskList("执行", outboundItem.trayCode);
                    if (wmsTask == null)
                    {
                        LogHelper.Info($"未查询到在执行中的作业:{outboundItem.trayCode}!", "杭氧");
                        //LogHelper.Info($"未查询到在执行中的作业:{outboundItem.trayCode}!", "杭氧");
                        outboundItem.opCode = "";
                    }
@@ -1104,6 +1096,7 @@
                                .Where(x => x.S_CODE == outerLoc.S_CODE)
                                .ToList()
                                .ForEach(x => x.N_LOCK_STATE = 3);
                                    var trayCode = ContainerHelper.GetLocCntr(outerLoc.S_CODE);
                                    tasks.Add(new WCSTask
                                    {
                                        S_CODE = GenerateTaskNo(),
@@ -1117,23 +1110,19 @@
                                        N_SCHEDULE_TYPE = 1,
                                        N_B_STATE = 0,
                                        S_B_STATE = WCSTask.GetStateStr(0),
                                        S_CNTR_CODE = outboundItem.trayCode,
                                        S_CNTR_CODE = trayCode.S_CNTR_CODE,
                                        N_START_LAYER = 1,
                                        N_END_LAYER = 1,
                                        N_CNTR_COUNT = 1
                                    });
                                }
                                else continue;
                            }
                            else
                            {
                                //货位为空直接生成出库任务
                                tasks.Add(CreateOutboundTask(targetLoc,outboundItem));
                            }
                        }
                        else continue;
                        else
                        {
                            continue;
                        }
                        // 2.2 生成出库任务(深位)
                        tasks.Add(CreateOutboundTask(targetLoc, outboundItem));
                    }
@@ -1142,6 +1131,7 @@
                        // 3. 非双深位直接出库
                        tasks.Add(CreateOutboundTask(targetLoc, outboundItem));
                    }
                    break;
                }
                // 4. 任务排序:移库任务优先 + 高优先级优先
                return tasks;
@@ -1161,24 +1151,24 @@
                    loc.N_ROADWAY == deepLoc.N_ROADWAY &&
                    loc.N_COL == deepLoc.N_COL &&
                    loc.N_LAYER == deepLoc.N_LAYER &&
                    loc.N_LOCK_STATE == 0
                    loc.N_LOCK_STATE == 0 || loc.N_LOCK_STATE == 5
            );
            private Location FindBestRelocationTarget(Location outerLoc)
            {
                  return _allLocations
                        .Where(loc =>
                            loc.N_ROADWAY == outerLoc.N_ROADWAY &&
                            loc.N_LOCK_STATE == 0 &&
                            loc.N_CURRENT_NUM == 0 &&
                            loc.S_CODE != outerLoc.S_CODE)
                        .OrderBy(loc => loc.N_ROW == outerLoc.N_ROW ? 1 : 0)
                        .ThenBy(loc => loc.N_COL)
                        .ThenBy(loc => Math.Abs(loc.N_LAYER - outerLoc.N_LAYER))
                        .FirstOrDefault();
                return _allLocations
                      .Where(loc =>
                          loc.N_ROADWAY == outerLoc.N_ROADWAY &&
                          loc.N_LOCK_STATE == 0 &&
                          loc.N_CURRENT_NUM == 0 &&
                          loc.S_CODE != outerLoc.S_CODE)
                      .OrderBy(loc => loc.N_ROW == outerLoc.N_ROW ? 1 : 0)
                      .ThenBy(loc => loc.N_COL)
                      .ThenBy(loc => Math.Abs(loc.N_LAYER - outerLoc.N_LAYER))
                      .FirstOrDefault();
            }
            private WCSTask CreateOutboundTask(Location loc,Outbound outbound) =>
            private WCSTask CreateOutboundTask(Location loc, Outbound outbound) =>
                new WCSTask
                {
                    S_CODE = GenerateTaskNo(),
@@ -1188,7 +1178,7 @@
                    S_END_LOC = outbound.endBit,
                    S_TYPE = outbound.taskType,
                    S_OP_CODE = outbound.opCode,
                    N_PRIORITY = 1,
                    N_PRIORITY = 0,
                    N_SCHEDULE_TYPE = 1,
                    N_B_STATE = 0,
                    S_B_STATE = WCSTask.GetStateStr(0),
@@ -1229,13 +1219,407 @@
                            _allLocations
                                .Where(x => x.S_CODE == outerLoc.S_CODE)
                                .ToList()
                                .ForEach(x => x.N_LOCK_STATE = 3);
                                .ForEach(x => x.N_LOCK_STATE = 5);
                            //标记深位的外侧货位为5
                        }
                    }
                }
            }
        }
        public class EmptyPalletOutboundScheduler
        {
            private readonly List<Location> _allLocations;
            public EmptyPalletOutboundScheduler(string areaCode)
            {
                var db = new SqlHelper<object>().GetInstance();
                _allLocations = db.Queryable<Location>()
                                 .Where(a => a.S_AREA_CODE == areaCode)
                                 .ToList();
            }
            /// <summary>
            /// 空托盘出库算法(自动寻找可用空托并生成任务)
            /// </summary>
            public List<WCSTask> GenerateEmptyPalletTasks(Outbound outbound)
            {
                var tasks = new List<WCSTask>();
                var foundPallets = 0;
                if (foundPallets < outbound.requiredCount)
                {
                    var doubleDeepPallets = FindDoubleDeepEmptyPallets();
                    foreach (var palletLoc in doubleDeepPallets)
                    {
                        if (IsDoubleDeepRow(palletLoc.N_ROW))
                        {
                            var outerLoc = FindOuterLocation(palletLoc);
                            if (outerLoc != null)
                            {
                                if (outerLoc.N_CURRENT_NUM == outerLoc.N_CAPACITY)
                                {
                                    // 优先移到深位,其次外侧
                                    var bestTarget = FindBestRelocationTarget(outerLoc);
                                    if (bestTarget != null)
                                    {
                                        //计算到了外侧把外侧的货位锁定
                                        _allLocations
                                    .Where(x => x.S_CODE == outerLoc.S_CODE)
                                    .ToList()
                                    .ForEach(x => x.N_LOCK_STATE = 3);
                                        var trayCode = ContainerHelper.GetLocCntr(outerLoc.S_CODE);
                                        tasks.Add(new WCSTask
                                        {
                                            S_CODE = GenerateTaskNo(),
                                            S_START_AREA = outerLoc.S_AREA_CODE,
                                            S_END_AREA = bestTarget.S_AREA_CODE,
                                            S_START_LOC = outerLoc.S_CODE,
                                            S_END_LOC = bestTarget.S_CODE,
                                            S_TYPE = "深位移库",
                                            S_OP_CODE = outbound.opCode,
                                            N_PRIORITY = 1,
                                            N_SCHEDULE_TYPE = 1,
                                            N_B_STATE = 0,
                                            S_B_STATE = WCSTask.GetStateStr(0),
                                            S_CNTR_CODE = trayCode.S_CNTR_CODE,
                                            N_START_LAYER = 1,
                                            N_END_LAYER = 1,
                                            N_CNTR_COUNT = 1
                                        });
                                    }
                                    else continue;
                                }
                            }
                        }
                        var tray = ContainerHelper.GetLocCntr(palletLoc.S_CODE);
                        outbound.trayCode = tray.S_CNTR_CODE;
                        tasks.Add(CreateEmptyPalletTask(palletLoc, outbound));
                        foundPallets++;
                        if (foundPallets >= outbound.requiredCount) break;
                    }
                }
                return tasks;
            }
            // 关键辅助方法
            private List<Location> FindDoubleDeepEmptyPallets() =>
                _allLocations.Where(loc =>
                    loc.N_LOCK_STATE == 0 &&
                    loc.N_CURRENT_NUM == loc.N_CAPACITY &&
                    ContainerHelper.GetLocItemRel(loc.S_CODE)
                ).OrderBy(loc => loc.N_ROW)   // 按排排序
                 .ThenBy(loc => loc.N_COL)
                 .ToList();
            private WCSTask CreateEmptyPalletTask(Location loc, Outbound outbound) =>
                new WCSTask
                {
                    S_CODE = GenerateTaskNo(),
                    S_START_AREA = loc.S_AREA_CODE,
                    S_END_AREA = outbound.endArea,
                    S_START_LOC = loc.S_CODE,
                    S_END_LOC = outbound.endBit,
                    S_TYPE = outbound.taskType,
                    S_OP_CODE = outbound.opCode,
                    N_PRIORITY = 0,
                    N_SCHEDULE_TYPE = 1,
                    N_B_STATE = 0,
                    S_B_STATE = WCSTask.GetStateStr(0),
                    S_CNTR_CODE = outbound.trayCode,
                    N_START_LAYER = 1,
                    N_END_LAYER = 1,
                    N_CNTR_COUNT = 1
                };
            // 复用原有双深位方法
            private bool IsDoubleDeepRow(int row) => row == 1 || row == 4;
            private Location FindTargetLocation(string code) =>
                _allLocations.FirstOrDefault(loc =>
                    loc.S_CODE == code && loc.N_LOCK_STATE == 0);
            //查询深位外侧的货位
            private Location FindOuterLocation(Location deepLoc) =>
                _allLocations.FirstOrDefault(loc =>
                    loc.N_ROW != deepLoc.N_ROW &&
                    loc.N_ROADWAY == deepLoc.N_ROADWAY &&
                    loc.N_COL == deepLoc.N_COL &&
                    loc.N_LAYER == deepLoc.N_LAYER &&
                    loc.N_LOCK_STATE == 0 || loc.N_LOCK_STATE == 5
            );
            private Location FindBestRelocationTarget(Location outerLoc)
            {
                return _allLocations
                      .Where(loc =>
                          loc.N_ROADWAY == outerLoc.N_ROADWAY &&
                          loc.N_LOCK_STATE == 0 &&
                          loc.N_CURRENT_NUM == 0 &&
                          loc.S_CODE != outerLoc.S_CODE)
                      .OrderBy(loc => loc.N_ROW == outerLoc.N_ROW ? 1 : 0)
                      .ThenBy(loc => loc.N_COL)
                      .ThenBy(loc => Math.Abs(loc.N_LAYER - outerLoc.N_LAYER))
                      .FirstOrDefault();
            }
            private void MarkReservedLocations(List<Outbound> outbound)
            {
                //查询已经被锁住的货位
                var lockLoc = _allLocations
                    .Where(loc => loc.N_LOCK_STATE != 0)
                    .ToList();
                //把锁住的货位标记为占用
                foreach (var item in lockLoc)
                {
                    if (IsDoubleDeepRow(item.N_ROW))
                    {
                        _allLocations
                                .Where(x => x.S_CODE == item.S_CODE)
                                .ToList()
                                .ForEach(x => x.N_LOCK_STATE = 3);
                    }
                }
                foreach (var item in outbound)
                {
                    var loc = FindTargetLocation(item.locCode);
                    if (loc != null && IsDoubleDeepRow(loc.N_ROW))
                    {
                        // 标记该深位对应的外侧货位(如果存在)
                        var outerLoc = FindOuterLocation(loc);
                        if (outerLoc != null)
                        {
                            // 标记深位的外侧货位(避免被移库占用)
                            _allLocations
                                .Where(x => x.S_CODE == outerLoc.S_CODE)
                                .ToList()
                                .ForEach(x => x.N_LOCK_STATE = 5);
                            //标记深位的外侧货位为5
                        }
                    }
                }
            }
        }
        #endregion
        #region 杭氧升降量表帮助方法
        /// <summary>
        /// MD5加密
        /// </summary>
        /// <param name="sInput"></param>
        /// <returns></returns>
        public static string GetMd5FromString(string sInput)
        {
            var lstData = Encoding.GetEncoding("utf-8").GetBytes(sInput);
            var lstHash = new MD5CryptoServiceProvider().ComputeHash(lstData);
            var result = new StringBuilder(32);
            for (int i = 0; i < lstHash.Length; i++)
            {
                result.Append(lstHash[i].ToString("x2").ToUpper());
            }
            return result.ToString();
        }
        /// <summary>
        /// 根据任务升量
        /// </summary>
        /// <param name="task"></param>
        internal static bool AddChange(WCSTask task)
        {
            var db = new SqlHelper<object>().GetInstance();
            var result = true;
            try
            {
                var itemlist = db.Queryable<CntrItemDetail>().Where(a => a.S_CNTR_CODE == task.S_CNTR_CODE).ToList();
                if (itemlist.Count > 0)
                {
                    var url = Settings.MoboxSeverUrl + "inventory/AddChange";
                    //仓库量表升量
                    var req = new AddChangeModel { op_type = 5 };
                    //库区量表升量
                    var req2 = new AddChangeModel { op_type = 6 };
                    itemlist.ForEach(a =>
                    {
                        LogHelper.Info($"填充数据");
                        LogHelper.Info($"添加仓库量表数据 仓库{task.S_END_WH} 物料编码{a.S_ITEM_CODE} 物料名称{a.S_ITEM_NAME} 数量{a.F_QTY}");
                        req.item_info.Add(new AddChangeModel.itemModel
                        {
                            wh_code = task.S_END_WH,
                            item_code = a.S_ITEM_CODE,
                            item_name = a.S_ITEM_NAME,
                            qty = a.F_QTY
                        });
                        LogHelper.Info($"添加库区量表数据 库区{task.S_END_AREA} 物料编码{a.S_ITEM_CODE} 物料名称{a.S_ITEM_NAME} 数量{a.F_QTY}");
                        req2.item_info.Add(new AddChangeModel.itemModel
                        {
                            wh_code = task.S_END_WH,
                            area_code = task.S_END_AREA,
                            item_code = a.S_ITEM_CODE,
                            item_name = a.S_ITEM_NAME,
                            qty = a.F_QTY
                        });
                    });
                    var reqData = JsonConvert.SerializeObject(req);
                    var AppKey = Settings.AppKey;
                    var AppSecret = Settings.AppSecret;
                    var ReqTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
                    //var ReqTime = GetTimeStamp(DateTime.Now.AddHours(-8), 1, 2);
                    LogHelper.Info($"加密前 AppKey={AppKey} AppSecret={AppSecret} ReqTime={ReqTime}");
                    var ReqVerify = GetMd5FromString(AppKey + AppSecret + ReqTime);
                    LogHelper.Info($"加密后 AppKey={AppKey} AppSecret={AppSecret} ReqTime={ReqTime} ReqVerify={ReqVerify} url={url} req={reqData}");
                    var res = new HttpHelper().WebPost(url, reqData, "application/json", AppKey, ReqTime, ReqVerify);
                    if (!string.IsNullOrEmpty(res))
                    {
                        LogHelper.Info($"mobox 仓库升量接口返回 {res}");
                        var moboxres = JsonConvert.DeserializeObject<moboxres>(res);
                        if (moboxres.err_code != 0)
                        {
                            result = false;
                        }
                    }
                    else
                    {
                        LogHelper.Info($"mobox 仓库升量接口返回为空");
                        result = false;
                    }
                    var reqData2 = JsonConvert.SerializeObject(req2);
                    var ReqTime2 = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
                    //var ReqTime2 = GetTimeStamp(DateTime.Now.AddHours(-8), 1, 2);
                    LogHelper.Info($"加密前 AppKey={AppKey} AppSecret={AppSecret} ReqTime={ReqTime2}");
                    var ReqVerify2 = GetMd5FromString(AppKey + AppSecret + ReqTime2);
                    LogHelper.Info($"加密后 AppKey={AppKey} AppSecret={AppSecret} ReqTime={ReqTime2} ReqVerify={ReqVerify2} url={url} req={reqData2}");
                    var res2 = new HttpHelper().WebPost(url, reqData2, "application/json", AppKey, ReqTime2, ReqVerify2);
                    if (!string.IsNullOrEmpty(res2))
                    {
                        LogHelper.Info($"mobox 库区升量接口返回 {res2}");
                        var moboxres = JsonConvert.DeserializeObject<moboxres>(res);
                        if (moboxres.err_code != 0)
                        {
                            result = false;
                        }
                    }
                    else
                    {
                        LogHelper.Info($"mobox 库区升量接口返回为空");
                        result = false;
                    }
                }
                else LogHelper.Info($"托盘{task.S_CNTR_CODE} 在容器货品明细中找不到数据");
            }
            catch (Exception ex)
            {
                LogHelper.Error($"仓库升量异常 异常信息={ex.Message}", ex);
                result = false;
            }
            return result;
        }
        /// <summary>
        /// 根据任务降量
        /// </summary>
        /// <param name="task"></param>
        internal static bool DeleteChange(WCSTask wmstask)
        {
            var db = new SqlHelper<object>().GetInstance();
            var result = true;
            try
            {
                //var wmstask = db.Queryable<WCSTask>().Where(a => a == task.S_OP_CODE).First();
                if (wmstask != null)
                {
                    var itemlist = db.Queryable<CntrItemDetail>().Where(a => a.S_CNTR_CODE == wmstask.S_CNTR_CODE).ToList();
                    if (itemlist.Count > 0)
                    {
                        var url = Settings.MoboxSeverUrl + "inventory/AddChange";
                        //仓库量表降量
                        var req = new AddChangeModel { op_type = 8 };
                        //库区量表降量
                        var req2 = new AddChangeModel { op_type = 9 };
                        itemlist.ForEach(a =>
                        {
                            LogHelper.Info($"填充数据");
                            LogHelper.Info($"减仓库量表数据 仓库{wmstask.S_START_WH} 物料编码{a.S_ITEM_CODE} 物料名称{a.S_ITEM_NAME} 数量{a.F_QTY}");
                            req.item_info.Add(new AddChangeModel.itemModel
                            {
                                wh_code = wmstask.S_START_WH,
                                item_code = a.S_ITEM_CODE,
                                item_name = a.S_ITEM_NAME,
                                qty = a.F_QTY
                            });
                            LogHelper.Info($"减库区量表数据 库区{wmstask.S_START_AREA} 物料编码{a.S_ITEM_CODE} 物料名称{a.S_ITEM_NAME} 数量{a.F_QTY}");
                            req2.item_info.Add(new AddChangeModel.itemModel
                            {
                                wh_code = wmstask.S_START_WH,
                                area_code = wmstask.S_START_AREA,
                                item_code = a.S_ITEM_CODE,
                                item_name = a.S_ITEM_NAME,
                                qty = a.F_QTY
                            });
                        });
                        var reqData = JsonConvert.SerializeObject(req);
                        var AppKey = Settings.AppKey;
                        var AppSecret = Settings.AppSecret;
                        var ReqTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
                        //var ReqTime = GetTimeStamp(DateTime.Now.AddHours(-8), 1, 2);
                        LogHelper.Info($"加密前 AppKey={AppKey} AppSecret={AppSecret} ReqTime={ReqTime}");
                        var ReqVerify = GetMd5FromString(AppKey + AppSecret + ReqTime);
                        LogHelper.Info($"加密后 AppKey={AppKey} AppSecret={AppSecret} ReqTime={ReqTime} ReqVerify={ReqVerify} url={url} req={reqData}");
                        var res = new HttpHelper().WebPost(url, reqData, "application/json", AppKey, ReqTime, ReqVerify);
                        if (!string.IsNullOrEmpty(res))
                        {
                            LogHelper.Info($"mobox 仓库降量接口返回 {res}");
                            var moboxres = JsonConvert.DeserializeObject<moboxres>(res);
                            if (moboxres.err_code != 0)
                            {
                                result = false;
                            }
                        }
                        else
                        {
                            LogHelper.Info($"mobox 仓库降量接口返回为空");
                            result = false;
                        }
                        var reqData2 = JsonConvert.SerializeObject(req2);
                        var ReqTime2 = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
                        //var ReqTime2 = GetTimeStamp(DateTime.Now.AddHours(-8), 1, 2);
                        LogHelper.Info($"加密前 AppKey={AppKey} AppSecret={AppSecret} ReqTime={ReqTime2}");
                        var ReqVerify2 = GetMd5FromString(AppKey + AppSecret + ReqTime2);
                        LogHelper.Info($"加密后 AppKey={AppKey} AppSecret={AppSecret} ReqTime={ReqTime2} ReqVerify={ReqVerify2} url={url} req={reqData2}");
                        var res2 = new HttpHelper().WebPost(url, reqData2, "application/json", AppKey, ReqTime2, ReqVerify2);
                        if (!string.IsNullOrEmpty(res2))
                        {
                            LogHelper.Info($"mobox 库区降量接口返回 {res2}");
                            var moboxres = JsonConvert.DeserializeObject<moboxres>(res);
                            if (moboxres.err_code != 0)
                            {
                                result = false;
                            }
                        }
                        else
                        {
                            LogHelper.Info($"mobox 库区降量接口返回为空");
                            result = false;
                        }
                    }
                    else LogHelper.Info($"托盘{wmstask.S_CNTR_CODE} 在容器货品明细中找不到数据");
                }
                else LogHelper.Info($"未找到任务{wmstask.S_CODE} 对应的任务");
            }
            catch (Exception ex)
            {
                LogHelper.Error($"仓库降量异常 异常信息={ex.Message}", ex);
                result = false;
            }
            return result;
        }
        #endregion
    }
}