服务端的TigerApi 框架,基于.NET6 2024 版本
Rodney Chen
2024-08-21 b4bd82fdbace031a81ab40963c8344d5eaa4a772
Tiger.Business/MES/Biz.WorkBatch.cs
@@ -6,7 +6,6 @@
using System.Text;
using System.Threading.Tasks;
using Rhea.Common;
using System.Net;
using System.Linq;
using Tiger.IBusiness;
using Tiger.Business.MES;
@@ -41,6 +40,7 @@
            public List<MES_WO_NODE_POST> NodePosts { get; set; }
            public List<MES_WO_NODE_DFTG> NodeDftgs { get; set; }
            public List<BAS_DEFECT_GRP> DefectGroups { get; set; }
            public List<BAS_DEFECT> Defects => DefectGroups.SelectMany(q => q.Defects).ToList();
            /// <summary>
            /// 事务锁
            /// </summary>
@@ -57,7 +57,8 @@
                LineCode = lineCode;
                WO = Biz.Db.Queryable<BIZ_MES_WO>().Where(q => q.ORDER_NO == OrderNo).IncludesAllFirstLayer().First();
                Product = Biz.Db.Queryable<BAS_ITEM>().Where(q => q.ITEM_CODE == WO.ITEM_CODE && q.AUTH_ORG == WO.AUTH_ORG).First();
                Batch = Biz.Db.Queryable<BIZ_MES_WO_BATCH>().Where(q => q.ORDER_NO == OrderNo && q.ACT_LINE == LineCode).First();
                Batch = Biz.Db.Queryable<BIZ_MES_WO_BATCH>().Where(q => q.ORDER_NO == OrderNo && q.ACT_LINE == LineCode &&
                                    (q.STATUS == BIZ_MES_WO_BATCH.STATUSs.Release.GetValue() || q.STATUS == BIZ_MES_WO_BATCH.STATUSs.Working.GetValue())).First();
                Customer = Biz.Db.Queryable<BAS_CUSTOMER>().Where(q => q.CUST_CODE == WO.CUST_CODE).First();    
                WoSNs = Biz.Db.Queryable<BIZ_MES_WO_SN>().Where(q => q.WORK_ORDER == OrderNo).ToList();
                Edges = Biz.Db.Queryable<MES_WO_EDGE>().Where(q => q.WORK_ORDER == OrderNo).ToList();
@@ -133,12 +134,13 @@
                    action.LocaleMsg = new("MES.WorkBatch.WoStatusCanNotWork", WO.ORDER_NO, Batch.STATUS.GetEnum<BIZ_MES_WO.STATUSs>().GetName());
                }
                //工单批次投入数量减去报废数量如果大于等于计划数量,则不允许生产
                if (curNode.IS_INPUT == "Y" && Batch.INPUT_QTY - Batch.SCRAP_QTY >= Batch.PLAN_QTY)
                //if (curNode.IS_INPUT == "Y" && Batch.INPUT_QTY - Batch.SCRAP_QTY >= Batch.PLAN_QTY)
                if (curNode.IS_INPUT == "Y" && WoSNs.Count(q => q.BATCH_NO == Batch.BATCH_NO) - Batch.SCRAP_QTY >= Batch.PLAN_QTY)
                {
                    action.IsSuccessed = false;
                    action.Data.SetValue(this, null);
                    action.LocaleMsg = new($"工单批次[{0}]已投入 {1},其中报废 {2},以满足计划数量[{3}],无需继续投入");
                    action.LocaleMsg = new("MES.WorkBatch.WoInputEnough", Batch.BATCH_NO, Batch.INPUT_QTY, Batch.SCRAP_QTY, Batch.PLAN_QTY);
                    action.LocaleMsg = new("MES.WorkBatch.WoInputEnough", Batch.BATCH_NO, WoSNs.Count(q => q.BATCH_NO == Batch.BATCH_NO), Batch.SCRAP_QTY, Batch.PLAN_QTY);
                }
                return action;
@@ -224,7 +226,7 @@
            /// 根据传入的条码过站信息和下一站的目标工序,判断条码是否能进入下一站
            /// </summary>
            /// <param name="input">本次提交的数据</param>
            /// <param name="wipSN">当前的条码过站记录</param>
            /// <param name="wipSN">当前的条码过站记录,需要导航查询生产过程记录信息</param>
            /// <param name="nextNode">要进入的目标工序</param>
            /// <returns></returns>
            public ApiAction<SubmitOutput> CanGotoNext(SubmitInput input, MES_WIP_DATA wipSN, MES_WO_NODE nextNode)
@@ -244,7 +246,7 @@
                        action.IsSuccessed = false;
                        action.Data.SetValue(this, null);
                        var nextList = Nodes.Where(q => q.IS_FIRST_NODE == "Y");
                        action.LocaleMsg = new("MES.WorkBatch.GotoNextNodeException", wipSN.SN, nextNode.NODE_NAME, string.Join(", ", nextList.Select(q => q.NODE_NAME + (q.CAN_SKIP == "Y" ? $"({T(L("MES.WorkBatch.Optional"), input.Locale)})" : ""))));
                        action.LocaleMsg = new("MES.WorkBatch.GotoNextNodeException", input.SN, nextNode.NODE_NAME, string.Join(", ", nextList.Select(q => q.NODE_NAME + (q.CAN_SKIP == "Y" ? $"({T(L("MES.WorkBatch.Optional"), input.Locale)})" : ""))));
                        return action;
                    }
                }
@@ -253,6 +255,7 @@
                {
                    var curNode = Nodes.First(q => q.ID == wipSN.NODE_ID);
                    var curSetting = NodeSets.FirstOrDefault(q => q.NODE_ID == curNode.ID);
                    var nextSetting = NodeSets.FirstOrDefault(q => q.NODE_ID == nextNode.ID);
                    //进入维修:如果目标工序是维修工序则判断当前条码在本工单有未维修的不良记录且当前工序节点有连线到维修工序,则允许进入维修工序
                    if (nextNode.Operation.OPER_TYPE == MES_OPERATION.OPER_TYPEs.Repair.GetValue()
                        && wipSN.Defects.Any(q => q.WORK_ORDER == WO.ORDER_NO && q.STATUS < MES_WIP_DFT.STATUSs.Resolved.GetValue())
@@ -279,17 +282,17 @@
                            action.IsSuccessed = false;
                            action.Data.SetValue(this, null);
                            var nextList = reflowNodes.Where(q => wipSN.REFLOW_NODE.IsNullOrEmpty() || wipSN.REFLOW_NODE == q.NODE_NAME);
                            action.LocaleMsg = new("MES.WorkBatch.ReflowToNodeException", nextNode.NODE_NAME, wipSN.SN, curNode.NODE_NAME, string.Join(", ", nextList.Select(q => q.NODE_NAME + (q.CAN_SKIP == "Y" ? $"({T(L("MES.WorkBatch.Optional"), input.Locale)})" : ""))));
                            action.LocaleMsg = new("MES.WorkBatch.ReflowToNodeException", nextNode.NODE_NAME, input.SN, curNode.NODE_NAME, string.Join(", ", nextList.Select(q => q.NODE_NAME + (q.CAN_SKIP == "Y" ? $"({T(L("MES.WorkBatch.Optional"), input.Locale)})" : ""))));
                            return action;
                        }
                    }
                    //不良品入站:如果产品有不良记录且目标工序不是维修工序且不允许不良品入站,则报错
                    else if (wipSN.Defects.Any(q => q.WORK_ORDER == WO.ORDER_NO && q.STATUS < MES_WIP_DFT.STATUSs.Resolved.GetValue()) 
                         && curSetting.ALLOW_DFT_IN != "Y" && nextNode.Operation.OPER_TYPE != MES_OPERATION.OPER_TYPEs.Repair.GetValue())
                         && nextSetting.ALLOW_DFT_IN != "Y" && nextNode.Operation.OPER_TYPE != MES_OPERATION.OPER_TYPEs.Repair.GetValue())
                    {
                        action.IsSuccessed = false;
                        action.Data.SetValue(this, null);
                        action.LocaleMsg = new("MES.WorkBatch.PleaseGotoRepair", curNode.NODE_NAME, wipSN.SN);
                        action.LocaleMsg = new("MES.WorkBatch.PleaseGotoRepair", curNode.NODE_NAME, input.SN);
                        return action;
                    }
                    //正常工序过站
@@ -310,7 +313,7 @@
                            action.IsSuccessed = false;
                            action.Data.SetValue(this, null);
                            var nextList = nextNodes.Where(q => NodeSets.Any(s => s.NODE_ID == q.ID && s.IS_ACTIVE == "Y" && s.CAN_SKIP != "Y"));
                            action.LocaleMsg = new("MES.WorkBatch.GotoNextNodeException", wipSN.SN, nextNode.NODE_NAME, string.Join(", ", nextList.Select(q => q.NODE_NAME + (q.CAN_SKIP == "Y" ? $"({T(L("MES.WorkBatch.Optional"), input.Locale)})" : ""))));
                            action.LocaleMsg = new("MES.WorkBatch.GotoNextNodeException", input.SN, nextNode.NODE_NAME, string.Join(", ", nextList.Select(q => q.NODE_NAME + (q.CAN_SKIP == "Y" ? $"({T(L("MES.WorkBatch.Optional"), input.Locale)})" : ""))));
                            return action;
                        }
                        //如果当前工序没有必须执行的后续工序,则在前置工序查找还有没有后续工序没完成的工序,有则尝试执行
@@ -339,7 +342,7 @@
                                action.IsSuccessed = false;
                                action.Data.SetValue(this, null);
                                var nextList = nextNodes.Where(q => NodeSets.Any(s => s.NODE_ID == q.ID && s.IS_ACTIVE == "Y"));
                                action.LocaleMsg = new("MES.WorkBatch.GotoNextNodeException", wipSN.SN, nextNode.NODE_NAME, string.Join(", ", nextList.Select(q => q.NODE_NAME + (q.CAN_SKIP == "Y" ? $"({T(L("MES.WorkBatch.Optional"), input.Locale)})" : ""))));
                                action.LocaleMsg = new("MES.WorkBatch.GotoNextNodeException", input.SN, nextNode.NODE_NAME, string.Join(", ", nextList.Select(q => q.NODE_NAME + (q.CAN_SKIP == "Y" ? $"({T(L("MES.WorkBatch.Optional"), input.Locale)})" : ""))));
                                return action;
                            }
                        }
@@ -353,7 +356,7 @@
            /// 添加节点的下一个可执行节点
            /// </summary>
            /// <param name="parent"></param>
            /// <param name="wipSN"></param>
            /// <param name="wipSN">当前的条码过站记录,需要导航查询生产过程记录信息</param>
            private List<MES_WO_NODE> GetNextNodes(MES_WO_NODE parent, MES_WIP_DATA wipSN)
            {
                var result = new List<MES_WO_NODE>();
@@ -365,7 +368,7 @@
                foreach (var next in nextNodes)
                {
                    //查找有没有前置工序找不到已良品过站的历史记录,若有则不允许继续执行
                    if (!next.PrepNodeIDs.Any(id => !wipSN.History.Any(h => h.WORK_ORDER == WO.ORDER_NO && h.NODE_ID == id && h.IsFinished)))
                    if (!next.PrepNodeIDs.Any(id => !wipSN.History.Any(h => h.WORK_ORDER == WO.ORDER_NO && h.NODE_ID == id && h.IsFinished)) || parent.IS_FIRST_NODE == "Y")
                    {
                        var setting = NodeSets.FirstOrDefault(q => q.NODE_ID == next.ID);
                        //后续工序是启用的,则添加
@@ -385,6 +388,66 @@
            }
            /// <summary>
            /// 根据传入的条码返回下一站可进入进入的目标工序
            /// </summary>
            /// <param name="wipSN">当前的条码过站记录,需要导航查询生产过程记录和生产不良记录信息</param>
            /// <returns></returns>
            public List<MES_WO_NODE> GetNextNodes(MES_WIP_DATA wipSN)
            {
                var result = new List<MES_WO_NODE>();
                //条码在本工单第一次过站
                if (wipSN.NODE_ID.IsNullOrEmpty())
                {
                    result.AddRange(Nodes.Where(q => q.IS_FIRST_NODE == "Y"));
                }
                //条码已有过站记录
                else
                {
                    var curNode = Nodes.First(q => q.ID == wipSN.NODE_ID);
                    //条码在维修工序,返回可回流工序
                    if (curNode.Operation.OPER_TYPE == MES_OPERATION.OPER_TYPEs.Repair.GetValue())
                    {
                        //查找所有可以回流的工序
                        var reflowNodes = Nodes.Where(q => Edges.Any(e => e.SRC_NODE == curNode.ID && e.TGT_NODE == q.ID)
                                                                    && NodeSets.Any(s => s.NODE_ID == q.ID && s.IS_ACTIVE == "Y")).ToList();
                        result.AddRange(reflowNodes.Where(q => wipSN.REFLOW_NODE.IsNullOrEmpty() || wipSN.REFLOW_NODE == q.NODE_NAME));
                    }
                    else
                    {
                        //往下查找条码当前工序的下一个可执行工序
                        var nextNodes = GetNextNodes(curNode, wipSN);
                        //如果当前工序没有必须执行的后续工序,则尝试在前置工序查找还有没有后续工序没完成的工序,有则加入
                        if (!nextNodes.Any(q => NodeSets.Any(s => s.NODE_ID == q.ID && s.IS_ACTIVE == "Y" && s.CAN_SKIP != "Y")))
                        {
                            //在前置工序查找还有没有后续工序没完成的前置工序
                            var prepIDs = curNode.PrepNodeIDs.Where(id =>
                                                                 Edges.Any(e => e.SRC_NODE == id && !wipSN.History.Any(h => h.WORK_ORDER == WO.ORDER_NO && h.NODE_ID == e.TGT_NODE && h.IsFinished))
                                                                 ).ToList();
                            foreach (var prepID in prepIDs)
                            {
                                //如果连线的目标工序的前置工序都已完成,则把连线的目标工序添加到可执行工序列表
                                var prep = Nodes.First(q => q.ID == prepID);
                                var next = GetNextNodes(prep, wipSN);
                                nextNodes.AddRange(next);
                            }
                        }
                        result.AddRange(nextNodes);
                        //如果当前条码是不良则只返回允许不良进站的工序和工序连接的维修工序
                        if (wipSN.Defects.Any(q => q.WORK_ORDER == WO.ORDER_NO && q.STATUS < MES_WIP_DFT.STATUSs.Resolved.GetValue()))
                        {
                            result.RemoveAll(q => NodeSets.Any(s => s.NODE_ID == q.ID && s.ALLOW_DFT_IN != "Y"));
                            //加入工序连接的维修工序
                            var repairNodes = Nodes.Where(q => Edges.Any(e => e.SRC_NODE == curNode.ID && e.TGT_NODE == q.ID) && q.Operation.OPER_TYPE == MES_OPERATION.OPER_TYPEs.Repair.GetValue());
                            result.AddRange(repairNodes);
                        }
                    }
                }
                return result;
            }
            /// <summary>
            /// 根据岗位编码返回工序不良代码
            /// </summary>
            /// <param name="postCode"></param>