kazelee
1 天以前 0cca5518f5babea276e37a922b930e25a4bd46eb
优化数据库事务帮助类 , 查询/日志生成类
8个文件已修改
3个文件已删除
3个文件已添加
980 ■■■■■ 已修改文件
HH.WCS.Mobox3.DSZSH.csproj 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/ApiHelper.cs 95 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/DebugController.cs 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
util/LogHelper.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
wms/DbExpr.cs 209 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
wms/DbTran.cs 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
wms/DbTranHelper.cs 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
wms/LocationHelper.cs 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
wms/LogBox.cs 139 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
wms/LogBuilder.cs 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
wms/QueryHelper.cs 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
wms/SYSHelper.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
wms/WCSHelper.cs 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
wms/WMSHelper.cs 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
HH.WCS.Mobox3.DSZSH.csproj
@@ -271,10 +271,10 @@
    <Compile Include="dispatch\HostToAGV.cs" />
    <Compile Include="util\LogHelper.cs" />
    <Compile Include="util\WebHelper.cs" />
    <Compile Include="wms\DbTranHelper.cs" />
    <Compile Include="wms\DbTran.cs" />
    <Compile Include="wms\LocationHelper.cs" />
    <Compile Include="wms\LogBuilder.cs" />
    <Compile Include="wms\QueryHelper.cs" />
    <Compile Include="wms\LogBox.cs" />
    <Compile Include="wms\DbExpr.cs" />
    <Compile Include="wms\SYSHelper.cs" />
    <Compile Include="Program.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
api/ApiHelper.cs
@@ -26,6 +26,7 @@
        /// <returns></returns>
        public static SimpleResult GoodpackOffline(GoodpackOfflineInfo model) {
            var db = new SqlHelper<object>().GetInstance();
            var (ok, msg) = (false, string.Empty);
            var taskInfo = ETask.M满箱下线入库.Info();
            const string preLog = "API:满箱下线入库:";
@@ -38,34 +39,34 @@
                // 检查货品容器表:是否已经存在贴标机传递的待入库物料信息
                // TODO:数量,规格是否也参与比对?
                var cgDetail = db.Queryable<TN_CG_Detail>().Where(d => d.S_ITEM_CODE == model.ItemCode && d.S_BATCH_NO == model.BatchNo && d.N_ITEM_STATE == 1 && d.S_ITEM_STATE == "待检").First();
                var cgDetail = db.Queryable<TN_CG_Detail>().Where(d => d.S_ITEM_CODE == model.ItemCode && d.S_BATCH_NO == model.BatchNo
                    && d.N_ITEM_STATE == 1 && d.S_ITEM_STATE == "待检").First();
                if (cgDetail == null) {
                    return NewSimpleResult(1, preLog + $"没有在[货品明细表]中找到[物料编码='{model.ItemCode}',批次号='{model.BatchNo}']的物料!请检查:PDA扫码物料信息与贴标机传递的信息是否一致!要求:物料状态='待检'");
                }
                // 查询起点货位:数量=0
                var startLoc = QueryHelper.GetLoc(db, 0, model.StartLoc, taskInfo.StartAreas, 0).First();
                var startLoc = db.Queryable<TN_Location>().Where(DbExpr.StartLocUnbind(model.StartLoc, taskInfo.StartAreas)).First();
                if (startLoc == null) {
                    return NewSimpleResult(2, preLog + $"没有找到起点货位'{model.StartLoc}'!要求:锁状态='无',当前容器数量=0,所在库区={LogObject(taskInfo.StartAreas)}");
                    return NewSimpleResult(2, preLog + LogBox.StartLocUnbindNotFound(model.StartLoc, taskInfo.StartAreas));
                }
                // 和满托下线入库的逻辑一致,由于容器移动不会更改绑定信息,所以必须删除旧数据
                var old = WCSHelper.GetLocCntrCg(cgDetail.S_CNTR_CODE, skipCgDetail: true);
                // 绑定货位容器,起点货位当前数量=1
                var locCntrRel = WCSHelper.BindLocCntr(startLoc, cgDetail.S_CNTR_CODE);
                locCntrRel.S_CNTR_TYPE = cntrType;
                // 查询终点货位
                // Order:按货位层数,从小到大排列
                var endLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y" && taskInfo.EndAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 0).OrderBy(l => new { l.N_LAYER }).First();
                var endLoc = db.Queryable<TN_Location>().Where(DbExpr.EndLoc(areas: taskInfo.EndAreas)).OrderBy(l => new { l.N_LAYER }).First();
                if (endLoc == null) {
                    return NewSimpleResult(3, preLog + $"没有找到合适的终点货位!要求:锁状态='无',当前容器数量=0,所在库区={LogObject(taskInfo.EndAreas)}");
                    return NewSimpleResult(3, preLog + LogBox.EndLocNotFound(null, taskInfo.EndAreas));
                }
                var task = WCSHelper.BuildTaskWithLocLock(startLoc, endLoc, locCntrRel.S_CNTR_CODE, taskInfo.TaskName);
                var (ok, msg) = DbTranHelper.CreateTask(new CreateTaskObj {
                (ok, msg) = DbTran.CreateTask(new CreateTaskObj {
                    Old = old,
                    New = new LocCntrCg { LocCntrRel = locCntrRel },
                    StartLocToUpdate = startLoc,
@@ -76,6 +77,7 @@
            }
            catch (Exception ex) {
                return NewSimpleResult(ex, preLog);
            }
        }
@@ -86,6 +88,7 @@
        /// <returns></returns>
        public static SimpleResult EmptyInboundPallet(EmptyInboundInfo model) {
            var db = new SqlHelper<object>().GetInstance();
            var (ok, msg) = (false, string.Empty);
            var taskInfo = ETask.K空托入库.Info();
            const string preLog = "API:空托入库:";
@@ -93,38 +96,36 @@
            try {
                // 查询起点货位:数量=0
                var startLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y" && l.S_CODE == model.StartLoc && taskInfo.StartAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 0).First();
                var startLoc = db.Queryable<TN_Location>().Where(DbExpr.StartLocUnbind(model.StartLoc, taskInfo.StartAreas)).First();
                if (startLoc == null) {
                    return NewSimpleResult(1, preLog + $"没有找到起点货位'{model.StartLoc}'!要求:锁状态='无';当前容器数量=0;所在库区={LogObject(taskInfo.StartAreas)}");
                    return NewSimpleResult(2, preLog + LogBox.StartLocUnbindNotFound(model.StartLoc, taskInfo.StartAreas));
                }
                // 查询容器表:容器类型字段
                var cntr = db.Queryable<TN_Container>().Where(c => c.S_CODE == model.CntrCode).First();
                if (cntr == null) {
                    return NewSimpleResult(2, preLog + $"容器'{model.CntrCode}'在[容器表]中不存在,请在前台页面中维护!");
                }
                if (cntr.S_TYPE != cntrType) {
                    return NewSimpleResult(3, preLog + $"容器'{model.CntrCode}'在[容器表]中的类型为'{cntr.S_TYPE}',与输入的容器类型'{cntrType}'不同!");
                (ok, msg) = WMSHelper.CheckCntrType(model.CntrCode, cntrType, out var cntr);
                if (!ok) {
                    return NewSimpleResult(3, preLog + msg);
                }
                
                // 空箱入库时,如果存在旧的绑定数据,删除
                var old = WCSHelper.GetLocCntrCg(model.CntrCode);
                // 绑定货位容器,起点货位当前数量=1
                // 绑定货位容器
                var locCntrRel = WCSHelper.BindLocCntr(startLoc, cntr.S_CODE);
                locCntrRel.S_CNTR_TYPE = cntrType;
                // 查询终点货位
                // Order:层数从低到高,行,列
                var endLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y" && taskInfo.EndAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 0).OrderBy(l => new { l.N_LAYER, l.N_ROW, l.N_COL }).First();
                var endLoc = db.Queryable<TN_Location>().Where(DbExpr.EndLoc(areas: taskInfo.EndAreas))
                    .OrderBy(l => new { l.N_LAYER, l.N_ROW, l.N_COL }).First();
                if (endLoc == null) {
                    return NewSimpleResult(3, preLog + $"没有找到合适的终点货位!要求:锁状态='无';当前容器数量=0;所在库区={LogObject(taskInfo.EndAreas)}");
                    return NewSimpleResult(3, preLog + LogBox.EndLocNotFound(null, taskInfo.EndAreas));
                }
                // 起点终点上锁,创建任务
                var task = WCSHelper.BuildTaskWithLocLock(startLoc, endLoc, locCntrRel.S_CNTR_CODE, taskInfo.TaskName);
                var (ok, msg) = DbTranHelper.CreateTask(new CreateTaskObj {
                (ok, msg) = DbTran.CreateTask(new CreateTaskObj {
                    Old = old,
                    New = new LocCntrCg { LocCntrRel = locCntrRel },
                    StartLocToUpdate = startLoc,
@@ -144,6 +145,7 @@
        /// <returns></returns>
        public static SimpleResult EmptyInboundGoodpack(EmptyInboundInfo model) {
            var db = new SqlHelper<object>().GetInstance();
            var (ok, msg) = (false, string.Empty);
            var taskInfo = ETask.K空箱入库.Info();
            const string preLog = "API:空箱入库:";
@@ -151,18 +153,15 @@
            try {
                // 查询起点货位:数量=0
                var startLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y").Where(l => l.S_CODE == model.StartLoc && taskInfo.StartAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 0).First();
                var startLoc = db.Queryable<TN_Location>().Where(DbExpr.StartLocUnbind(model.StartLoc, taskInfo.StartAreas)).First();
                if (startLoc == null) {
                    return NewSimpleResult(1, preLog + $"没有找到起点货位'{model.StartLoc}'!要求:锁状态='无';当前容器数量=0;所在库区={LogObject(taskInfo.StartAreas)}");
                    return NewSimpleResult(2, preLog + LogBox.StartLocUnbindNotFound(model.StartLoc, taskInfo.StartAreas));
                }
                // 查询容器表:容器类型字段
                var cntr = db.Queryable<TN_Container>().Where(c => c.S_CODE == model.CntrCode).First();
                if (cntr == null) {
                    return NewSimpleResult(1, $"容器'{model.CntrCode}'在[容器表]中不存在,请在前台页面中维护!");
                }
                if (cntr.S_TYPE != cntrType) {
                    return NewSimpleResult(2, preLog + $"容器'{model.CntrCode}'在[容器表]中的类型是'{cntr.S_TYPE},不是'{cntrType}'!");
                (ok, msg) = WMSHelper.CheckCntrType(model.CntrCode, cntrType, out var cntr);
                if (!ok) {
                    return NewSimpleResult(3, preLog + msg);
                }
                // 空箱入库时,如果存在旧的绑定数据,删除
@@ -174,14 +173,15 @@
                // 查询终点货位
                // Order:层数从低到高,行,列
                var endLoc = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y").Where(l => taskInfo.EndAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 0).OrderBy(l => new { l.N_LAYER, l.N_ROW, l.N_COL }).First();
                var endLoc = db.Queryable<TN_Location>().Where(DbExpr.EndLoc(areas: taskInfo.EndAreas))
                    .OrderBy(l => new { l.N_LAYER, l.N_ROW, l.N_COL }).First();
                if (endLoc == null) {
                    return NewSimpleResult(3, preLog + $"没有找到合适的终点货位!要求:锁状态='无';当前容器数量=0;所在库区={LogObject(taskInfo.EndAreas)}");
                }
                var task = WCSHelper.BuildTaskWithLocLock(startLoc, endLoc, locCntrRel.S_CNTR_CODE, taskInfo.TaskName);
                var (ok, msg) = DbTranHelper.CreateTask(new CreateTaskObj {
                (ok, msg) = DbTran.CreateTask(new CreateTaskObj {
                    Old = old,
                    New = new LocCntrCg { LocCntrRel = locCntrRel },
                    StartLocToUpdate = startLoc,
@@ -202,6 +202,7 @@
        /// <returns></returns>
        public static SimpleResult EmptyOnlinePallet(EmptyOnlinePalletInfo model) {
            var db = new SqlHelper<object>().GetInstance();
            var (ok, msg) = (false, string.Empty);
            var taskInfo = ETask.K空托上线出库.Info();
            const string preLog = "API:空托上线出库:";
@@ -209,12 +210,9 @@
            try {
                // 查询容器表:容器类型字段
                var cntr = db.Queryable<TN_Container>().Where(c => c.S_CODE == model.CntId).First();
                if (cntr == null) {
                    return NewSimpleResult(1, $"容器'{model.CntId}'在[容器表]中不存在,请在前台页面中维护!");
                }
                if (cntr.S_TYPE != cntrType) {
                    return NewSimpleResult(2, preLog + $"容器'{model.CntId}'在[容器表]中的类型是'{cntr.S_TYPE},不是'{cntrType}'!");
                (ok, msg) = WMSHelper.CheckCntrType(model.CntId, cntrType, out var cntr);
                if (!ok) {
                    return NewSimpleResult(1, preLog + msg);
                }
                var needUpdateContainer = false;
@@ -251,7 +249,7 @@
                    cntr = null;
                }
                var (ok, msg) = DbTranHelper.CreateTask(new CreateTaskObj {
                (ok, msg) = DbTran.CreateTask(new CreateTaskObj {
                    ContainerToUpdate = cntr,
                    StartLocToUpdate = startLoc,
                    EndLocToUpdate = endLoc,
@@ -271,18 +269,17 @@
        /// <returns></returns>
        public static SimpleResult EmptyOnlineGoodpack(EmptyOnlineGoodpackInfo model) {
            var db = new SqlHelper<object>().GetInstance();
            var (ok, msg) = (false, string.Empty);
            var taskInfo = ETask.K空箱上线出库.Info();
            const string preLog = "API:空箱上线出库:";
            const string cntrType = "好运箱";
            try {
                var cntr = db.Queryable<TN_Container>().Where(c => c.S_CODE == model.CntId).First();
                if (cntr == null) {
                    return NewSimpleResult(1, preLog + $"容器'{model.CntId}'在[容器表]中不存在,请在前台页面中维护!");
                }
                if (cntr.S_TYPE != cntrType) {
                    return NewSimpleResult(2, preLog + $"容器'{model.CntId}'在[容器表]中的类型='{cntr.S_TYPE}',不是'{cntrType}'!");
                // 查询容器表:容器类型字段
                (ok, msg) = WMSHelper.CheckCntrType(model.CntId, cntrType, out var cntr);
                if (!ok) {
                    return NewSimpleResult(3, preLog + msg);
                }
                var startLoc = db.Queryable<TN_Location>().LeftJoin<TN_Loc_Container>((l, c) => l.S_CODE == c.S_LOC_CODE).Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y").Where((l, c) => taskInfo.StartAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 1 && c.S_CNTR_CODE == model.CntId && c.S_CNTR_TYPE == cntrType).First();
@@ -298,7 +295,7 @@
                var task = WCSHelper.BuildTaskWithLocLock(startLoc, endLoc, model.CntId, taskInfo.TaskName);
                var (ok, msg) = DbTranHelper.CreateTask(new CreateTaskObj {
                (ok, msg) = DbTran.CreateTask(new CreateTaskObj {
                    StartLocToUpdate = startLoc,
                    EndLocToUpdate = endLoc,
                    TaskToInsert = task,
@@ -317,6 +314,7 @@
        /// <returns></returns>
        public static SimpleResult QualifiedBack(QualifiedBackInfo model) {
            var db = new SqlHelper<object>().GetInstance();
            var (ok, msg) = (false, string.Empty);
            var taskInfo = ETask.C抽检合格回库.Info();
            const string preLog = "API:抽检合格回库";
@@ -351,7 +349,7 @@
                var cntId = locCntrRel.S_CNTR_CODE;
                var task = WCSHelper.BuildTaskWithLocLock(startLoc, endLoc, cntId, taskInfo.TaskName);
                var (ok, msg) = DbTranHelper.CreateTask(new CreateTaskObj {
                (ok, msg) = DbTran.CreateTask(new CreateTaskObj {
                    CgDetailToUpdate = cgDetail,
                    StartLocToUpdate = startLoc,
                    EndLocToUpdate = endLoc,
@@ -371,6 +369,7 @@
        /// <returns></returns>
        public static SimpleResult UnqualifiedShift(UnqualifiedShiftInfo model) {
            var db = new SqlHelper<object>().GetInstance();
            var (ok, msg) = (false, string.Empty);
            var taskInfo = ETask.C抽检不合格移库.Info();
            const string preLog = "API:抽检不合格移库:";
@@ -407,7 +406,7 @@
                var task = WCSHelper.BuildTaskWithLocLock(startLoc, endLoc, locCntrRel.S_CNTR_CODE, taskInfo.TaskName);
                var (ok, msg) = DbTranHelper.CreateTask(new CreateTaskObj {
                (ok, msg) = DbTran.CreateTask(new CreateTaskObj {
                    CgDetailToUpdate = cgDetail,
                    StartLocToUpdate = startLoc,
                    EndLocToUpdate = endLoc,
@@ -427,6 +426,7 @@
        /// <returns></returns>
        public static SimpleResult RestBack(RestBackInfo model) {
            var db = new SqlHelper<object>().GetInstance();
            var (ok, msg) = (false, string.Empty);
            var taskInfo = ETask.W尾料回库.Info();
            const string preLog = "API:尾料回库:";
@@ -450,7 +450,7 @@
                var task = WCSHelper.BuildTaskWithLocLock(startLoc, endLoc, locCntrRel.S_CNTR_CODE, taskInfo.TaskName);
                var (ok, msg) = DbTranHelper.CreateTask(new CreateTaskObj {
                (ok, msg) = DbTran.CreateTask(new CreateTaskObj {
                    StartLocToUpdate = startLoc,
                    EndLocToUpdate = endLoc,
                    TaskToInsert = task,
@@ -469,6 +469,7 @@
        /// <returns></returns>
        public static SimpleResult FinishedOutbound(FinishedOutboundInfo model) {
            var db = new SqlHelper<object>().GetInstance();
            var (ok, msg) = (false, string.Empty);
            var taskInfo = ETask.C成品胶出库.Info();
            const string preLog = "API:成品胶出库:";
api/DebugController.cs
@@ -17,6 +17,7 @@
using static HH.WCS.Mobox3.DSZSH.api.ApiModel;
using static HH.WCS.Mobox3.DSZSH.api.OtherModel;
using static HH.WCS.Mobox3.DSZSH.core.Monitor;
using static HH.WCS.Mobox3.DSZSH.util.Config;
namespace HH.WCS.Mobox3.DSZSH.api {
    /// <summary>
@@ -214,6 +215,22 @@
                message = "success"
            };
        }
        [HttpPost]
        public string TestDb() {
            var db = new SqlHelper<object>().GetInstance();
            var startAreas = new List<string> { "HJQ", "CKQ" };
            //var locationQuery = db.Queryable<TN_Location>().Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y" && startAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 1).LeftJoin<TN_Loc_Container>((l, c) => l.S_CODE == c.S_LOC_CODE).Where((l, c) => c.S_CNTR_CODE == "a" && c.S_CNTR_TYPE == "b");
            //LogHelper.Info(locationQuery.ToSqlString());
            ////var location = locationQuery.First();
            //var newLocationQuery = DbQuery.StartLoc(db, "a", startAreas);
            //LogHelper.Info(newLocationQuery.ToSqlString());
            //locationQuery = db.Queryable<TN_Location>().LeftJoin<TN_Loc_Container>((l, c) => l.S_CODE == c.S_LOC_CODE).Where(l => l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.C_ENABLE == "Y" && startAreas.Contains(l.S_AREA_CODE) && l.N_CURRENT_NUM == 1).Where((l, c) => c.S_CNTR_CODE == "a" && c.S_CNTR_TYPE == "b");
            var locationQuery = db.Queryable<TN_Location>().Where(DbExpr.EndLoc(areas: startAreas));
            LogHelper.Info(locationQuery.ToSqlString());
            return locationQuery.ToSqlString();
        }
    }
    public class GoodpackToProdLineInfo {
util/LogHelper.cs
@@ -14,6 +14,7 @@
    {
        public static Dictionary<string, ILogger> loggers = new Dictionary<string, ILogger>();
        #region 初始方法
        public static void Debug(string message, string name = "") {
            ILogger logger = null;
            if (loggers.Keys.Contains(name)) {
@@ -70,6 +71,7 @@
                logger.Error($"{message}{ex.StackTrace}");
            }
        }
        #endregion
        #region 自定义方法
        public static void Warn(string msg, string preLog = "") {
wms/DbExpr.cs
New file
@@ -0,0 +1,209 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using HH.WCS.Mobox3.DSZSH.models;
using SqlSugar;
namespace HH.WCS.Mobox3.DSZSH.wms {
    /// <summary>
    /// 数据库 [ 查询表达式 ] 构造类
    /// </summary>
    public class DbExpr {
        /// <summary>
        /// 通用货位筛选表达式
        /// </summary>
        /// <remarks><code><![CDATA[
        /// l => l.C_ENABLE == "Y"
        ///   && l.N_LOCK_STATE == @lock && l.S_LOCK_STATE == TN_Location.GetLockStateStr(@lock)
        ///   && l.S_CODE == name
        ///   && areas.Contains(l.S_AREA_CODE)
        ///   && l.N_CURRENT_NUM == curNum
        /// ]]></code></remarks>
        /// <param name="lock"></param>
        /// <param name="name"></param>
        /// <param name="areas"></param>
        /// <param name="curNum"></param>
        /// <returns></returns>
        public static Expression<Func<TN_Location, bool>> LocFilter(int @lock = 0, string name = "", List<string> areas = null, int curNum = 0) {
            Expression<Func<TN_Location, bool>> expr = l => l.C_ENABLE == "Y"; // 已启用
            if (@lock >= 0) {
                Expression<Func<TN_Location, bool>> newExpr = l => l.N_LOCK_STATE == @lock && l.S_LOCK_STATE == TN_Location.GetLockStateStr(@lock);
                expr = Expression.Lambda<Func<TN_Location, bool>>(Expression.AndAlso(expr.Body, newExpr.Body), expr.Parameters[0]);
            }
            if (!string.IsNullOrEmpty(name)) {
                Expression<Func<TN_Location, bool>> newExpr = l => l.S_CODE == name;
                expr = Expression.Lambda<Func<TN_Location, bool>>(Expression.AndAlso(expr.Body, newExpr.Body), expr.Parameters[0]);
            }
            if (areas != null && areas.Count == 0) {
                Expression<Func<TN_Location, bool>> newExpr = l => areas.Contains(l.S_AREA_CODE);
                expr = Expression.Lambda<Func<TN_Location, bool>>(Expression.AndAlso(expr.Body, newExpr.Body), expr.Parameters[0]);
            }
            if (curNum >= 0) {
                Expression<Func<TN_Location, bool>> newExpr = l => l.N_CURRENT_NUM == curNum;
                expr = Expression.Lambda<Func<TN_Location, bool>>(Expression.AndAlso(expr.Body, newExpr.Body), expr.Parameters[0]);
            }
            return expr;
        }
        /// <summary>
        /// 起点货位 ( 未绑定 )
        /// </summary>
        /// <remarks><code><![CDATA[
        /// l => l.C_ENABLE == "Y"
        ///   && l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无"
        ///   && l.S_CODE == name
        ///   && areas.Contains(l.S_AREA_CODE)
        ///   && l.N_CURRENT_NUM == 0
        /// ]]></code></remarks>
        /// <param name="name"></param>
        /// <param name="areas"></param>
        /// <returns></returns>
        public static Expression<Func<TN_Location, bool>> StartLocUnbind(string name = "", List<string> areas = null) {
            Expression<Func<TN_Location, bool>> expr =
                l => l.C_ENABLE == "Y" && l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.N_CURRENT_NUM == 0;
            if (!string.IsNullOrEmpty(name)) {
                Expression<Func<TN_Location, bool>> newExpr = l => l.S_CODE == name;
                expr = Expression.Lambda<Func<TN_Location, bool>>(Expression.AndAlso(expr.Body, newExpr.Body), expr.Parameters[0]);
            }
            if (areas != null && areas.Count > 0) {
                Expression<Func<TN_Location, bool>> newExpr = l => areas.Contains(l.S_AREA_CODE);
                expr = Expression.Lambda<Func<TN_Location, bool>>(Expression.AndAlso(expr.Body, newExpr.Body), expr.Parameters[0]);
            }
            return expr;
        }
        /// <summary>
        ///
        /// </summary>
        /// <remarks><code><![CDATA[
        /// l => l.C_ENABLE == "Y"
        ///   && l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无"
        ///   && l.S_CODE == name
        ///   && areas.Contains(l.S_AREA_CODE)
        ///   && l.N_CURRENT_NUM == 0
        /// ]]></code></remarks>
        /// <param name="name"></param>
        /// <param name="areas"></param>
        /// <returns></returns>
        public static Expression<Func<TN_Location, bool>> EndLoc(string name = "", List<string> areas = null) {
            Expression<Func<TN_Location, bool>> expr =
                l => l.C_ENABLE == "Y" && l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == "无" && l.N_CURRENT_NUM == 0;
            if (!string.IsNullOrEmpty(name)) {
                Expression<Func<TN_Location, bool>> newExpr = l => l.S_CODE == name;
                expr = Expression.Lambda<Func<TN_Location, bool>>(Expression.AndAlso(expr.Body, newExpr.Body), expr.Parameters[0]);
            }
            if (areas != null && areas.Count > 0) {
                Expression<Func<TN_Location, bool>> newExpr = l => areas.Contains(l.S_AREA_CODE);
                expr = Expression.Lambda<Func<TN_Location, bool>>(Expression.AndAlso(expr.Body, newExpr.Body), expr.Parameters[0]);
            }
            return expr;
        }
    }
    #region 旧查询类 ( 搁置 )
    /// <summary>
    /// [ 数据库查询 ] 帮助类
    /// </summary>
    /// <remarks><b>[ 搁置 ]</b> 类与方法暂时设为私有<br/><b>[ 理由 ]</b> 引用外部 db 存在风险 ; 不适用于联表查询场景 , 不够灵活</remarks>
    class DbQuery {
        /// <summary>
        /// 构建 [ 货位 ] 查询表达式 : 筛选条件 : 锁状态 / 名称 / 货区 / 当前容器数量 ( 默认筛选已启用货位 )
        /// </summary>
        /// <param name="db">调用区域的 SqlSugarClient</param>
        /// <param name="lock">锁状态 ( 小于 0 时忽略 )</param>
        /// <param name="name">货位名称 ( 为 null 或 "" 时忽略 )</param>
        /// <param name="areas">所在库区列表 ( 为 null 或 [] 时忽略 )</param>
        /// <param name="curNum">当前数量 ( 小于 0 时忽略 )</param>
        /// <returns></returns>
        static ISugarQueryable<TN_Location> Loc(SqlSugarClient db, int @lock, string name, List<string> areas, int curNum) {
            var query = db.Queryable<TN_Location>().Where(l => l.C_ENABLE == "Y"); // 已启用
            if (@lock >= 0) {
                query = query.Where(l => l.N_LOCK_STATE == @lock && l.S_LOCK_STATE == TN_Location.GetLockStateStr(@lock));
            }
            if (!string.IsNullOrEmpty(name)) {
                query = query.Where(l => l.S_CODE == name);
            }
            if (areas != null && areas.Count != 0) {
                query = query.Where(l => areas.Contains(l.S_AREA_CODE));
            }
            if (curNum >= 0) {
                query = query.Where(l => l.N_CURRENT_NUM == curNum);
            }
            return query;
        }
        /// <summary>
        /// 构建 [ 起点货位 ( 绑定前 ) ] 查询表达式 : 筛选条件 : 名称 / 货区 ( 默认筛选 : 已启用 / 未上锁 / 当前容器数量 = 0 )
        /// </summary>
        /// <param name="db">调用区域的 SqlSugarClient</param>
        /// <param name="name">货位名称 ( 为 null 或 "" 时忽略 )</param>
        /// <param name="areas">所在库区列表 ( 为 null 或 [] 时忽略 )</param>
        /// <returns></returns>
        static ISugarQueryable<TN_Location> StartLocUnbind(SqlSugarClient db, string name, List<string> areas) {
            var query = db.Queryable<TN_Location>().Where(l => l.C_ENABLE == "Y" && l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == TN_Location.GetLockStateStr(0) && l.N_CURRENT_NUM == 0); // 已启用,未上锁,当前容器数量=0(初始未绑定容器)
            if (!string.IsNullOrEmpty(name)) {
                query = query.Where(l => l.S_CODE == name);
            }
            if (areas != null && areas.Count != 0) {
                query = query.Where(l => areas.Contains(l.S_AREA_CODE));
            }
            return query;
        }
        /// <summary>
        /// 构建 [ 起点货位 ( 绑定后 ) ] 查询表达式 : 筛选条件 : 名称 / 货区 ( 默认筛选 : 已启用 / 未上锁 / 当前容器数量 = 1 )
        /// </summary>
        /// <param name="db">调用区域的 SqlSugarClient</param>
        /// <param name="name">货位名称 ( 为 null 或 "" 时忽略 )</param>
        /// <param name="areas">所在库区列表 ( 为 null 或 [] 时忽略 )</param>
        /// <returns></returns>
        static ISugarQueryable<TN_Location> StartLoc(SqlSugarClient db, string name, List<string> areas) {
            var query = db.Queryable<TN_Location>().Where(l => l.C_ENABLE == "Y" && l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == TN_Location.GetLockStateStr(0) && l.N_CURRENT_NUM == 1); // 已启用,未上锁,当前容器数量=1(已绑定容器)
            if (!string.IsNullOrEmpty(name)) {
                query = query.Where(l => l.S_CODE == name);
            }
            if (areas != null && areas.Count != 0) {
                query = query.Where(l => areas.Contains(l.S_AREA_CODE));
            }
            return query;
        }
        /// <summary>
        /// 构建 [ 终点货位 ] 查询表达式 : 筛选条件 : 名称 / 货区 ( 默认筛选 : 已启用 / 未上锁 / 当前容器数量 = 0 )
        /// </summary>
        /// <param name="db">调用区域的 SqlSugarClient</param>
        /// <param name="name">货位名称 ( 为 null 或 "" 时忽略 )</param>
        /// <param name="areas">所在库区列表 ( 为 null 或 [] 时忽略 )</param>
        /// <returns></returns>
        static ISugarQueryable<TN_Location> EndLoc(SqlSugarClient db, string name, List<string> areas) {
            var query = db.Queryable<TN_Location>().Where(l => l.C_ENABLE == "Y" && l.N_LOCK_STATE == 0 && l.S_LOCK_STATE == TN_Location.GetLockStateStr(0) && l.N_CURRENT_NUM == 0); // 已启用,未上锁,当前容器数量=0(未绑定容器)
            if (!string.IsNullOrEmpty(name)) {
                query = query.Where(l => l.S_CODE == name);
            }
            if (areas != null && areas.Count != 0) {
                query = query.Where(l => areas.Contains(l.S_AREA_CODE));
            }
            return query;
        }
    }
    #endregion
}
wms/DbTran.cs
New file
@@ -0,0 +1,122 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using HH.WCS.Mobox3.DSZSH.models;
using HH.WCS.Mobox3.DSZSH.util;
using Newtonsoft.Json;
using SqlSugar;
namespace HH.WCS.Mobox3.DSZSH.wms {
    /// <summary>
    /// 数据库 [ 事务处理 ] 帮助类
    /// </summary>
    public class DbTran {
        /// <summary>
        /// 数据库事务处理 ( 创建任务 )
        /// </summary>
        /// <remarks><b>[ 要求 ]</b> obj ≠ null<br/>
        /// <b>[ 注意 ]</b> 所有的数据库操作 , 都会在对象 = null 时选择跳过 , 且不会报错 , 除了 TaskToInsert ;
        /// <br/>如果存在某个必须完成的数据库操作 , 需在调用时确保该对象 ≠ null</remarks>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static (bool, string) CreateTask(CreateTaskObj obj) {
            var db = new SqlHelper<object>().GetInstance();
            using (var tran = db.Ado.UseTran()) {
                // 更新[容器表](业务需要)
                if (obj.ContainerToUpdate != null && db.Updateable(obj.ContainerToUpdate).UpdateColumns(c => new { c.S_SPEC, c.S_SOURCE, c.T_MODIFY }).ExecuteCommand() <= 0) {
                    return (false, $"更新[容器][规格(物料编码)]失败!!数据:\n\n{JsonConvert.SerializeObject(obj.ContainerToUpdate)}\n");
                }
                // 更新[容器货品明细](业务需要)
                if (obj.CgDetailToUpdate != null && db.Updateable(obj.CgDetailToUpdate).UpdateColumns(it => new { it.N_ITEM_STATE, it.S_ITEM_STATE, it.T_MODIFY }).ExecuteCommand() <= 0) {
                    tran.RollbackTran();
                    return (false, $"更新[容器货品明细][物料状态]失败!!物料号='{obj.CgDetailToUpdate}',物料状态=>'{obj.CgDetailToUpdate.S_ITEM_STATE}'");
                }
                #region 删除[旧]容器绑定信息
                if (obj.Old != null) {
                    if (obj.Old.CgDetail != null && db.Deleteable(obj.Old.CgDetail).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return (false, $"删除[旧容器货品明细]失败!!数据:\n\n{JsonConvert.SerializeObject(obj.Old.CgDetail)}\n");
                    }
                    if (obj.Old.LocCntrRel != null && db.Deleteable(obj.Old.LocCntrRel).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return (false, $"删除[旧货位容器绑定]失败!!数据:\n\n{JsonConvert.SerializeObject(obj.Old.LocCntrRel)}\n");
                    }
                    if (obj.Old.Location != null && db.Updateable(obj.Old.Location).UpdateColumns(l => new { l.N_CURRENT_NUM, l.T_MODIFY }).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return (false, $"更新[旧货位][当前容器数量]失败!!货位='{obj.Old.Location.S_CODE}',数量=>{obj.Old.Location.N_CURRENT_NUM}");
                    }
                }
                #endregion
                #region 添加[新]容器绑定信息
                if (obj.New != null) {
                    if (obj.New.CgDetail != null && db.Insertable(obj.New.CgDetail).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return (false, $"插入[新容器货品明细]失败!!数据:\n\n{JsonConvert.SerializeObject(obj.New.CgDetail)}\n");
                    }
                    if (obj.New.LocCntrRel != null && db.Insertable(obj.New.LocCntrRel).ExecuteCommand() <= 0) {
                        tran.RollbackTran();
                        return (false, $"插入[旧货位容器绑定]失败!!数据:\n\n{JsonConvert.SerializeObject(obj.New.LocCntrRel)}\n");
                    }
                    // 新货位信息通常就是 StartLoc , 在 StartLocToUpdate 中处理即可
                }
                #endregion
                #region 创建任务 + 货位锁
                if (obj.StartLocToUpdate != null && db.Updateable(obj.StartLocToUpdate).UpdateColumns(it => new { it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY, it.N_CURRENT_NUM, }).ExecuteCommand() <= 0) {
                    tran.RollbackTran();
                    return (false, $"更新[起点货位][锁状态]失败!!起点='{obj.StartLocToUpdate.S_CODE}',锁状态=>'{obj.StartLocToUpdate.S_LOCK_STATE}'");
                }
                if (obj.EndLocToUpdate != null && db.Updateable(obj.EndLocToUpdate).UpdateColumns(it => new { it.N_LOCK_STATE, it.S_LOCK_STATE, it.S_LOCK_OP, it.T_MODIFY, }).ExecuteCommand() <= 0) {
                    tran.RollbackTran();
                    return (false, $"更新[终点货位][锁状态]失败!!终点='{obj.EndLocToUpdate.S_CODE}',锁状态=>'{obj.EndLocToUpdate.S_LOCK_STATE}'");
                }
                if (obj.TaskToInsert == null) {
                    tran.RollbackTran();
                    throw new ArgumentException("数据库事务处理:待插入的[任务]为空!!"); // 待插入的任务不存在,直接抛出异常
                }
                if (db.Insertable(obj.TaskToInsert).ExecuteCommand() <= 0) {
                    tran.RollbackTran();
                    return (false, $"生成任务'{obj.TaskToInsert.S_TYPE}'失败!!任务号='{obj.TaskToInsert.S_CODE}',容器号='{obj.TaskToInsert.S_CNTR_CODE}',起点='{obj.TaskToInsert.S_START_LOC}',终点='{obj.TaskToInsert.S_END_LOC}'");
                }
                #endregion
                // 提交数据库更改
                tran.CommitTran();
                return (true, $"生成任务'{obj.TaskToInsert.S_TYPE}'成功!!任务号='{obj.TaskToInsert.S_CODE}',容器号='{obj.TaskToInsert.S_CNTR_CODE}',起点='{obj.TaskToInsert.S_START_LOC}',终点='{obj.TaskToInsert.S_END_LOC}'");
            }
        }
    }
    public class CreateTaskObj {
        #region 起点货位 / 终点货位 / 任务
        public TN_Location StartLocToUpdate { get; set; } = null;
        public TN_Location EndLocToUpdate { get; set; } = null;
        public TN_Task TaskToInsert { get; set; } = null;
        #endregion
        #region 容器绑定信息 ( 旧 / 新 )
        /// <summary>
        /// [ 旧 ] 容器绑定信息 ( 货位 / 货位容器 / 容器货品 )
        /// </summary>
        public LocCntrCg Old { get; set; } = null;
        /// <summary>
        /// [ 新 ] 容器绑定信息 ( 货位 / 货位容器 / 容器货品 )
        /// </summary>
        public LocCntrCg New { get; set; } = null;
        #endregion
        public TN_Container ContainerToUpdate { get; set; } = null;
        public TN_CG_Detail CgDetailToUpdate { get; set; } = null;
    }
}
wms/DbTranHelper.cs
File was deleted
wms/LocationHelper.cs
@@ -77,25 +77,19 @@
        /// <param name="loc"></param>
        /// <param name="isEmpty">是否属于人工空托区到空托缓存库区</param>
        /// <returns></returns>
        internal static string GetAgvSite(string loc, bool isEmpty = false)
        {
        internal static string GetAgvSite(string loc, bool isEmpty = false) {
            var site = "0";
            if (_locationDict.Keys.Contains(loc.Trim()) && !isEmpty)
            {
            if (_locationDict.Keys.Contains(loc.Trim()) && !isEmpty) {
                var Location = _locationDict[loc.Trim()];
                site = Location.S_AGV_SITE;
            }
            else
            {
            else {
                var Location = GetLoc(loc.Trim());
                if (Location != null)
                {
                if (Location != null) {
                    site = Location.S_AGV_SITE;
                    if (isEmpty)
                    {
                        if (Location.N_CURRENT_NUM == 0)
                        {
                    if (isEmpty) {
                        if (Location.N_CURRENT_NUM == 0) {
                            site = Location.S_AGV_SITE;
                        }
                    }
@@ -104,8 +98,7 @@
            return site;
        }
        internal static TN_Location GetLoc(string code)
        {
        internal static TN_Location GetLoc(string code) {
            var db = new SqlHelper<object>().GetInstance();
            return db.Queryable<TN_Location>().Where(a => a.S_CODE.Trim() == code).First();
        }
wms/LogBox.cs
New file
@@ -0,0 +1,139 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;
using HH.WCS.Mobox3.DSZSH.models;
using Newtonsoft.Json;
namespace HH.WCS.Mobox3.DSZSH.util {
    /// <summary>
    /// [ 日志信息 ] 构造类
    /// </summary>
    public static class LogBox {
        /// <summary>
        /// <c>"没有找到起点货位!!要求:锁状态='@lock';货位名称='name';所在库区=[areas];当前容器数量=curNum;"</c>
        /// </summary>
        /// <remarks><b>[ 注意 ]</b> 原则上至少有 1 个限制条件</remarks>
        /// <param name="lock">锁状态 ( 小于 0 时忽略 )</param>
        /// <param name="name">货位名称 ( 为 null 或 "" 时忽略 )</param>
        /// <param name="areas">所在库区列表 ( 为 null 或 [] 时忽略 )</param>
        /// <param name="curNum">当前数量 ( 小于 0 时忽略 )</param>
        /// <returns></returns>
        public static string StartLocNotFound(int @lock, string name, List<string> areas, int curNum) {
            var res = "没有找到起点货位!!要求:";
            if (@lock >= 0) {
                res += $"锁状态='{TN_Location.GetLockStateStr(@lock)}';";
            }
            if (!string.IsNullOrEmpty(name)) {
                res += $"货位名称='{name}';";
            }
            if (areas != null && areas.Count != 0) {
                res += $"所在库区={JsonConvert.SerializeObject(areas)};";
            }
            if (curNum >= 0) {
                res += $"当前容器数量={curNum};";
            }
            return res;
        }
        /// <summary>
        /// <c>"没有找到起点货位!!要求:锁状态='无';货位名称='name';所在库区=[areas];当前容器数量={bind?1:0};"</c>
        /// </summary>
        /// <param name="name">货位名称 ( 为 null 或 "" 时忽略 )</param>
        /// <param name="areas">所在库区列表 ( 为 null 或 [] 时忽略 )</param>
        /// <param name="bind">货位是否已绑定 , 默认为 true , 此时设置 [ 当前容器数量 ] = 1 , 否则设置为 0</param>
        /// <returns></returns>
        public static string StartLocNotFound(string name, List<string> areas, bool bind = true) {
            var res = "没有找到起点货位!!要求:锁状态='无'";
            if (!string.IsNullOrEmpty(name)) {
                res += $"货位名称='{name}';";
            }
            if (areas != null && areas.Count != 0) {
                res += $"所在库区={JsonConvert.SerializeObject(areas)};";
            }
            res += $"当前容器数量={(bind ? 1 : 0)};";
            return res;
        }
        /// <summary>
        /// <c>"没有找到起点货位!!要求:锁状态='无';货位名称='name';所在库区=[areas];当前容器数量=0;"</c>
        /// </summary>
        /// <param name="name">货位名称 ( 为 null 或 "" 时忽略 )</param>
        /// <param name="areas">所在库区列表 ( 为 null 或 [] 时忽略 )</param>
        /// <returns></returns>
        public static string StartLocUnbindNotFound(string name, List<string> areas) {
            var res = "没有找到起点货位!!要求:锁状态='无'";
            if (!string.IsNullOrEmpty(name)) {
                res += $"货位名称='{name}';";
            }
            if (areas != null && areas.Count != 0) {
                res += $"所在库区={JsonConvert.SerializeObject(areas)};";
            }
            res += $"当前容器数量=0;";
            return res;
        }
        /// <summary>
        /// <c>"没有找到终点货位!!要求:锁状态='@lock';货位名称='name';所在库区=[areas];当前容器数量=curNum;"</c>
        /// </summary>
        /// <remarks><b>[ 注意 ]</b> 原则上至少有 1 个限制条件</remarks>
        /// <param name="lock">锁状态 ( 小于 0 时忽略 )</param>
        /// <param name="name">货位名称 ( 为 null 或 "" 时忽略 )</param>
        /// <param name="areas">所在库区列表 ( 为 null 或 [] 时忽略 )</param>
        /// <param name="curNum">当前数量 ( 小于 0 时忽略 )</param>
        /// <returns></returns>
        public static string EndLocNotFound(int @lock, string name, List<string> areas, int curNum) {
            var res = "没有找到终点货位!!要求:";
            if (@lock >= 0) {
                res += $"锁状态='{TN_Location.GetLockStateStr(@lock)}';";
            }
            if (!string.IsNullOrEmpty(name)) {
                res += $"货位名称='{name}';";
            }
            if (areas != null && areas.Count != 0) {
                res += $"所在库区={JsonConvert.SerializeObject(areas)};";
            }
            if (curNum >= 0) {
                res += $"当前容器数量={curNum};";
            }
            return res;
        }
        /// <summary>
        /// <c>"没有找到终点货位!!要求:锁状态='无';货位名称='name';所在库区=[areas];当前容器数量=curNum;"</c>
        /// </summary>
        /// <param name="name">货位名称 ( 为 null 或 "" 时忽略 )</param>
        /// <param name="areas">所在库区列表 ( 为 null 或 [] 时忽略 )</param>
        /// <param name="curNum">当前数量 ( 小于 0 时忽略 )</param>
        /// <returns></returns>
        public static string EndLocNotFound(string name = "", List<string> areas = null, int curNum = 0) {
            var res = "没有找到终点货位!!要求:锁状态='无';";
            if (!string.IsNullOrEmpty(name)) {
                res += $"货位名称='{name}';";
            }
            if (areas != null && areas.Count != 0) {
                res += $"所在库区={JsonConvert.SerializeObject(areas)};";
            }
            if (curNum >= 0) {
                res += $"当前容器数量={curNum};";
            }
            return res;
        }
    }
}
wms/LogBuilder.cs
File was deleted
wms/QueryHelper.cs
File was deleted
wms/SYSHelper.cs
@@ -10,7 +10,7 @@
namespace HH.WCS.Mobox3.DSZSH.wms {
    /// <summary>
    /// 序列号生成帮助类 (名称/命名空间为历史遗留问题)
    /// [ 序列号生成 ] 帮助类
    /// </summary>
    internal class SYSHelper {
        private static object locker = new object();
wms/WCSHelper.cs
@@ -11,6 +11,9 @@
using System.Threading.Tasks;
namespace HH.WCS.Mobox3.DSZSH.wms {
    /// <summary>
    /// [ 通用业务 ] 帮助类
    /// </summary>
    internal class WCSHelper {
        internal static string GenerateTaskNo() {
            var id = SYSHelper.GetSerialNumber("任务号", "TN");
@@ -52,30 +55,7 @@
            return loc == null ? "0" : loc.S_AGV_SITE;
        }
        /// <summary>
        /// <!--弃用|理由:不灵活,涉及业务过于具体-->检查容器类型是否正确
        /// </summary>
        /// <param name="cntrCode"></param>
        /// <param name="cntrType"></param>
        /// <param name="errMsg"></param>
        /// <returns></returns>
        private static bool CheckCntrType(string cntrCode, string cntrType, out string errMsg) {
            var db = new SqlHelper<object>().GetInstance();
            errMsg = string.Empty;
            var cntr = db.Queryable<TN_Container>().Where(c => c.S_CODE == cntrType).First();
            if (cntr == null) {
                errMsg = $"容器'{cntrCode}'在[容器表]中不存在,请在前台页面中维护!";
                return false;
            }
            if (cntr.S_TYPE != cntrType) {
                errMsg = $"容器'{cntrCode}'在[容器表]中的类型是'{cntr.S_TYPE},不是'{cntrType}'!";
                return false;
            }
            return true;
        }
        #region 容器相关信息 查询 / 绑定
        /// <summary>
        /// 根据 [ 容器号 ] , 查询当前容器关联的 [ 货位 ] [ 货位容器 ] [ 容器货品 ] 信息 ( 只查询 1 条 )
        /// </summary>
@@ -154,12 +134,14 @@
            loc.T_MODIFY = DateTime.Now;
            return locCntrRel;
        }
        }
        #endregion
        #region 起点 / 终点货位锁 + 创建任务
        /// <summary>
        /// 起点出库锁
        /// </summary>
        /// <remarks>要求: loc != null; 锁状态='无';</remarks>
        /// <remarks><b>[ 要求 ]</b> loc ≠ null ; 锁状态='无' ;</remarks>
        /// <param name="loc"></param>
        /// <param name="lockSource"></param>
        public static void LockStartLoc(TN_Location loc, string lockSource = "") {
@@ -178,7 +160,7 @@
        /// <summary>
        /// 终点入库锁
        /// </summary>
        /// <remarks>要求 : loc ≠ null ; 锁状态='无' ;</remarks>
        /// <remarks><b>[ 要求 ]</b> loc ≠ null ; 锁状态='无' ;</remarks>
        /// <param name="loc"></param>
        /// <param name="lockSource"></param>
        public static void LockEndLoc(TN_Location loc, string lockSource = "") {
@@ -197,13 +179,13 @@
        /// <summary>
        /// 创建任务
        /// </summary>
        /// <remarks>要求 : <br/>(1) startLoc / endLoc ≠ null<br/>(2) startLoc / endLoc 存在 S_CODE , S_AREA_CODE 字段</remarks>
        /// <param name="startLoc">起点货位:至少提供:S_CODE,S_AREA_CODE</param>
        /// <param name="endLoc">终点货位:至少提供:S_CODE,S_AREA_CODE</param>
        /// <remarks><b>[ 要求 ]</b> startLoc / endLoc ≠ null ; startLoc / endLoc 存在 S_CODE , S_AREA_CODE 字段</remarks>
        /// <param name="startLoc">起点货位 : 至少提供:S_CODE , S_AREA_CODE</param>
        /// <param name="endLoc">终点货位 : 至少提供:S_CODE , S_AREA_CODE</param>
        /// <param name="cntId">容器号</param>
        /// <param name="type">任务类型(名称)</param>
        /// <param name="type">任务类型 ( 名称 )</param>
        /// <param name="pri">优先级</param>
        /// <param name="agv">AGV类型</param>
        /// <param name="agv">AGV 类型</param>
        /// <returns></returns>
        public static TN_Task BuildTask(TN_Location startLoc, TN_Location endLoc, string cntId, string type, int pri = 3, int agv = 1) {
            TN_Task task = new TN_Task() {
@@ -227,13 +209,13 @@
        /// <summary>
        /// 创建任务 + 货位锁
        /// </summary>
        /// <remarks>要求 : <br/>(1) startLoc / endLoc ≠ null<br/>(2) startLoc / endLoc 存在 S_CODE , S_AREA_CODE 字段</remarks>
        /// <remarks><b>[ 要求 ]</b> (1) startLoc / endLoc ≠ null ; (2) startLoc / endLoc 存在 S_CODE , S_AREA_CODE 字段</remarks>
        /// <param name="startLoc">起点货位 : 至少提供 : S_CODE , S_AREA_CODE</param>
        /// <param name="endLoc">终点货位 : 至少提供 : S_CODE , S_AREA_CODE</param>
        /// <param name="cntId">容器号</param>
        /// <param name="type">任务类型(名称)</param>
        /// <param name="type">任务类型 ( 名称 )</param>
        /// <param name="pri">优先级</param>
        /// <param name="agv">AGV类型</param>
        /// <param name="agv">AGV 类型</param>
        /// <returns></returns>
        public static TN_Task BuildTaskWithLocLock(TN_Location startLoc, TN_Location endLoc, string cntId, string type, int pri = 3, int agv = 1) {
            var task = BuildTask(startLoc, endLoc, cntId, type, pri);
@@ -241,7 +223,8 @@
            LockEndLoc(endLoc, task.S_CODE);
            return task;
        }
        }
        #endregion
        internal static bool CheckActionRecordExist(string no, int code) {
            var db = new SqlHelper<TN_Task_Action>().GetInstance();
wms/WMSHelper.cs
@@ -13,7 +13,28 @@
using static HH.WCS.Mobox3.DSZSH.api.ApiModel;
namespace HH.WCS.Mobox3.DSZSH.wms {
    /// <summary>
    /// [ 具体业务 ] 帮助类
    /// </summary>
    public class WMSHelper {
        /// <summary>
        /// 检查容器类型
        /// </summary>
        /// <param name="cntrCode"></param>
        /// <param name="cntrType"></param>
        /// <param name="cntr"></param>
        /// <returns></returns>
        public static (bool, string) CheckCntrType(string cntrCode, string cntrType, out TN_Container cntr) {
            var db = new SqlHelper<object>().GetInstance();
            cntr = db.Queryable<TN_Container>().Where(c => c.S_CODE == cntrCode).First();
            if (cntr == null) {
                return (false, $"容器'{cntrCode}'在[容器表]中不存在,请在前台页面中维护!!");
            }
            if (cntr.S_TYPE != cntrType) {
                return (false, $"容器'{cntrCode}'在[容器表]中的类型为'{cntr.S_TYPE}',与输入的容器类型'{cntrType}'不同!!");
            }
            return (true, "检查容器类型成功!!");
        }
    }
}