业务流.全流程跟踪.源单和下游单显示不同的原因和二开方案
【场景】采购订单下推采购入库单,采购订单下推付款申请单
采购订单的全流程图
![1679457091489.webp](/download/0100d2265faa09cf4711b5eb786447efaa6c.webp)
但是采购入库单看不到应付单,应付单看不到采购入库单
【原理】在系统的理解中,子单据只能看到当前对象的关联流程,比如看到以这个单下推的后续节点,以及跟这个单关联的上游;对于上游还做了什么其他业务是会被屏蔽调的;
如上图案例:采购订单推了入库单给库存模块的业务员,推了应付单给另一个模块的业务员;
本质上库存模块的业务员是不能关注应付单的信息(避免越权之类的场景)
目前系统上就是这么设计的;
【案例】目前仅二开实现在采购入库单,全流程跟踪能够查看应付单
实现方案:在采购入库单中,打开采购订单的全流程跟踪图;
核心逻辑
<1>找到需要打开流程图的源单内码和源单标识
<2>打开流程图
```csharp
using Kingdee.BOS;
using Kingdee.BOS.Business.Bill.Operation;
using Kingdee.BOS.BusinessEntity.BillTrack;
using Kingdee.BOS.BusinessEntity.BusinessFlow;
using Kingdee.BOS.Core;
using Kingdee.BOS.Core.Bill.PlugIn;
using Kingdee.BOS.Core.BusinessFlow.ServiceArgs;
using Kingdee.BOS.Core.DynamicForm;
using Kingdee.BOS.Core.DynamicForm.PlugIn.Args;
using Kingdee.BOS.Core.Metadata;
using Kingdee.BOS.Core.Metadata.EntityElement;
using Kingdee.BOS.Core.SqlBuilder;
using Kingdee.BOS.Orm.DataEntity;
using Kingdee.BOS.ServiceHelper;
using Kingdee.BOS.Util;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Tmp.BusinessFlow.Tool.Operation
{
[Kingdee.BOS.Util.HotUpdate]
public class TrackAllWithSpecBill : AbstractBillPlugIn
{
/*
* 根据当前操作的单据内码,找到目标单的单据内码
*/
public Context Context
{
get { return this.View.Context; }
}
/// <summary>
/// 重写查看流程图事件
/// </summary>
/// <returns></returns>
public override void BarItemClick(BarItemClickEventArgs e)
{
if(!e.BarItemKey.EqualsIgnoreCase("tb_ShowSpecBillTrack"))
{
return;
}
/*
* 根据当前单据内码,切换到目标
* <1>直接源单,根据link直接获取
* <2>跨级源单,通过下推携带到目标单;或者通过加载全流程实例获取
* https://wenku.my7c.com/article/71661049120113152?productLineId=1
* 演示 单据界面,根据link获取上有单据加载源单内码
*/
if (this.View == null || this.View.Model.DataObject == null)
return;
var billBusinessInfo = this.View.BillBusinessInfo;
DynamicObject billObj = this.View.Model.DataObject;
//<1>根据link计算源单内码和源单标识
if(billBusinessInfo.GetForm().LinkSet.LinkEntitys== null || billBusinessInfo.GetForm().LinkSet.LinkEntitys.Count <=0)
{
return;
}
var linkSetEntity = this.View.BillBusinessInfo.GetForm().LinkSet.LinkEntitys[0];
var linkEntity = billBusinessInfo.GetEntity(linkSetEntity.Key);
var entity = billBusinessInfo.GetEntity(linkSetEntity.ParentEntityKey);
List<DynamicObject> entityObjs = new List<DynamicObject>();
if(entity is HeadEntity)
{
entityObjs.Add(billObj);
}
else if(entity is EntryEntity)
{
DynamicObjectCollection entityObjColl = billObj[entity.EntryName] as DynamicObjectCollection;
if (entityObjColl == null)
return;
foreach(var entityRowObj in entityObjColl)
{
entityObjs.Add(entityRowObj);
}
}
if (entityObjs.Count <= 0)
return;
string stableId = null;
List<long> billIds = new List<long>();
foreach(var entityRow in entityObjs)
{
DynamicObjectCollection linkObjColl = entityRow[linkEntity.EntryName] as DynamicObjectCollection;
if (linkObjColl == null || linkObjColl.Count <= 0)
continue;
foreach(var linkRow in linkObjColl)
{
string sTableName = linkRow["STableName"].ToString();
if(stableId != null && sTableName != stableId)
{
continue;
}
stableId = sTableName;
long sBillId = ObjectUtils.Object2Int64(linkRow["SBillId"]);
billIds.Add(sBillId);
}
}
//static TableDefine LoadTableDefine(Context ctx, string tableNumber)
var td = BusinessFlowServiceHelper.LoadTableDefine(this.View.Context, stableId);
string formId = td.FormId;
//<2>显示业务流程图
var viewParameter = GetBillFlows(formId, billIds);
if (viewParameter == null || viewParameter.Instances.Count == 0)
{
return;
}
this.ShowBusinessFlow(viewParameter);
}
/// <summary>
/// 计算流程图
/// </summary>
/// <param name="formId"></param>
/// <param name="billIds"></param>
/// <returns></returns>
private ViewBusinessFlowParameter GetBillFlows(string formId, List<long> billIds)
{
if (formId == null || billIds == null || billIds.Count <= 0)
{
return null;
}
//目标单元数据
FormMetadata metaData = MetaDataServiceHelper.Load(this.Context, formId) as FormMetadata;
TrackAllHelper helper = new TrackAllHelper(this.Context, metaData.BusinessInfo);
var result = helper.LoadBillView(billIds);
return result;
}
private void ShowBusinessFlow(ViewBusinessFlowParameter viewParameter)
{
// 交换区数据
this.View.Session[FormConst.ConvParamKey_ViewBusinessFlowParameter] = viewParameter;
this.View.Session["FormOperation"] = null;
DynamicFormShowParameter param = new DynamicFormShowParameter();
param.FormId = FormIdConst.BOS_TrackResultForm;
param.ParentPageId = this.View.PageId;
param.OpenStyle.ShowType = TrackHelper.GetShowType(this.View);
this.View.ShowForm(param);
}
}
/// <summary>
/// 全流程跟踪帮助类
/// 把此抽出来作为公有方法,供移动,和全流程跟踪刷新使用
/// </summary>
public class TrackAllHelper
{
private BusinessInfo BillBusinessInfo;
private Context Context;
public TrackAllHelper(Context ctx, BusinessInfo businessInfo)
{
Context = ctx;
BillBusinessInfo = businessInfo;
}
/// <summary>
/// 加载单据内码关联的所有分录内码
/// </summary>
/// <param name="billIds"></param>
/// <returns></returns>
private Dictionary<string, List<Tuple<long, long>>> LoadBillIds(List<long> billIds)
{
billIds = billIds.Distinct().ToList();
Dictionary<string, List<Tuple<long, long>>> allEntityIds = new Dictionary<string, List<Tuple<long, long>>>();
//记录实体 单据头内码
List<Tuple<long, long>> billIdTuples = new List<Tuple<long, long>>();
foreach (var billId in billIds)
{
billIdTuples.Add(new Tuple<long, long>(billId, billId));
}
Entity headEntity = BillBusinessInfo.Entrys.FirstOrDefault((entity) => entity is HeadEntity);
allEntityIds.Add(headEntity.Key.ToUpperInvariant(), billIdTuples);
var pkArray = billIds.Select(x => (object)x).ToArray();
DynamicObject[] billObjs = BusinessDataServiceHelper.Load(Context, pkArray, BillBusinessInfo.GetDynamicObjectType());
if (billObjs == null || billObjs.Length <= 0)
return allEntityIds;
//记录实体 单据体内码
foreach (var entity in BillBusinessInfo.Entrys)
{
//仅支持单据体
if (entity is HeadEntity || entity is SubHeadEntity || entity is SubEntryEntity)
continue;
string strEntityPropertyName = entity.DynamicProperty.Name;
var entryIdTuples = new List<Tuple<long, long>>();
foreach (var billObj in billObjs)
{
DynamicObjectCollection objCollections = billObj[strEntityPropertyName] as DynamicObjectCollection;
if (objCollections == null || objCollections.Count <= 0)
continue;
foreach (DynamicObject row in objCollections)
{
object id = row["Id"];
if (id == null)
{
continue;
}
long entryId = Convert.ToInt64(id);
if (entryId == 0)
{
continue;
}
entryIdTuples.Add(new Tuple<long, long>(Convert.ToInt64(billObj["Id"]), entryId));
}
}
allEntityIds.Add(entity.Key.ToUpperInvariant(), entryIdTuples);
}
return allEntityIds;
}
/// <summary>
/// 获取Link实体关联的实体
/// </summary>
/// <param name="metaData"></param>
/// <returns></returns>
public static List<Entity> GetLinkEntitys(BusinessInfo billBusinessInfo)
{
List<Entity> linkEntities = new List<Entity>();
if (billBusinessInfo.GetForm().LinkSet == null || (billBusinessInfo.GetForm().LinkSet.LinkEntitys == null))
return linkEntities;
billBusinessInfo.GetForm().LinkSet.LinkEntitys.ForEach(linkEntity =>
{
string linkEntryKey = "";
Entity entity = null;
linkEntryKey = linkEntity.ParentEntityKey;
entity = billBusinessInfo.GetEntity(linkEntryKey);
if (entity != null)
{
linkEntities.Add(entity);
}
});
return linkEntities;
}
/// <summary>
/// 加载单据内码关联所有实体的分
/// </summary>
/// <param name="billIds"></param>
/// <returns></returns>
public ViewBusinessFlowParameter LoadBillView(List<long> billIds)
{
Dictionary<string, List<Tuple<long, long>>> allEntityIds = LoadBillIds(billIds);
Entity headEntity = BillBusinessInfo.Entrys.FirstOrDefault((entity) => entity is HeadEntity);
ViewBusinessFlowParameter viewParam = new ViewBusinessFlowParameter();
viewParam.BillInfo = new ViewBusinessFlowBillInfo(BillBusinessInfo.GetForm().Id);
List<BusinessFlowInstance> instances = this.LoadAllInstances(allEntityIds, headEntity, billIds);
if (instances.IsEmpty() == false)
{
var queryChangeEntityParamValue = SystemParameterServiceHelper.GetParamter(Context, 0, 0, "BF_SystemParameter", "FQueryChangeEntity");
var isEnableCrossEntityQuery = ObjectUtils.Object2Bool(queryChangeEntityParamValue);
if (isEnableCrossEntityQuery)
{
this.ChangeEntityQuery(viewParam, ref instances);
}
}
viewParam.Instances.AddRange(instances);
return viewParam;
}
/// <summary>
/// 加载单据全部实体的全部业务流程实例
/// </summary>
/// <param name="allEntityIds"></param>
/// <param name="headEntity"></param>
/// <param name="billIds"></param>
/// <param name="billInfo">同步输出本次发起联查的单据体内码</param>
/// <returns></returns>
private List<BusinessFlowInstance> LoadAllInstances(Dictionary<string, List<Tuple<long, long>>> allEntityIds, Entity headEntity, List<long> billIds, BusinessInfo info = null)
{
List<BusinessFlowInstance> instances = new List<BusinessFlowInstance>();
string formId = info == null ? BillBusinessInfo.GetForm().Id : info.GetForm().Id;
// 逐个单据体寻找其业务流程实例
foreach (var item in allEntityIds)
{
Entity entity = info == null ? BillBusinessInfo.GetEntity(item.Key) : info.GetEntity(item.Key);
List<long> entryIds = new List<long>();
if (this.IsHeadEntity(entity))
{
entryIds.AddRange(billIds);
}
else
{
item.Value.ForEach((t) => entryIds.Add(t.Item2));
}
var entityInstances = this.LoadInstancesByEntryIds(formId, entity.Key, entryIds.ToArray());
var validateInstance = this.RemoveJointlessNodes(formId, entity.Key, entryIds.ToArray(), entityInstances);
instances.AddRange(validateInstance);
}
return instances;
}
#region 加载并合并流程实例
/// <summary>
/// 改为公共方法,使业务流程图也能使用
/// </summary>
/// <param name="viewParam"></param>
/// <param name="entityInstances"></param>
private void ChangeEntityQuery(ViewBusinessFlowParameter viewParam, ref List<BusinessFlowInstance> entityInstances)
{
if (entityInstances.IsEmpty()) return;
var currentInstances = new List<BusinessFlowInstance>();
var reachedInstIds = new HashSet<string>();
var reachedNodes = new HashSet<string>();
var reachedTableName = new HashSet<string>();//搜索过的表名
foreach (var item in entityInstances)
{
currentInstances.Add(item);
}
//跨实体查询,实例之间并不存在连接或对应关系,故这里可以根据表单批量处理,而不是逐个处理
while (currentInstances.Count > 0)
{
//得到需要搜索的节点,按节点表名进行分组
var groupNodesByTbName = new Dictionary<string, List<long>>();
foreach (var instItem in currentInstances)
{
reachedInstIds.Add(instItem.Id);
List<RouteTreeNode> lstNodes = this.GetAllNodes(instItem.FirstNode); //所有的节点
foreach (RouteTreeNode node in lstNodes)
{
if (node.Id != null && !reachedNodes.Contains(node.Id.CId)) //节点没有搜索过
{
List<long> lstId = new List<long>();
if (groupNodesByTbName.ContainsKey(node.Id.Tbl))
{
lstId = groupNodesByTbName[node.Id.Tbl];
}
else
{
groupNodesByTbName[node.Id.Tbl] = lstId;
}
lstId.Add(node.Id.EId);
reachedNodes.Add(node.Id.CId);
}
}
}
currentInstances.Clear();
//对节点进行搜索
var groupNodesByTbDefine = new Dictionary<TableDefine, List<long>>();
foreach (var currentNode in groupNodesByTbName)
{
var tbDefine = BusinessFlowServiceHelper.LoadTableDefine(Context, currentNode.Key);
groupNodesByTbDefine[tbDefine] = currentNode.Value;
reachedTableName.Add(currentNode.Key);
}
foreach (var currentNode in groupNodesByTbDefine)
{
var crossInstances = this.GetInstances(currentNode, reachedTableName); //得到跨实体流程
foreach (var itemInst in crossInstances)
{
if (reachedInstIds.Contains(itemInst.Id)) continue;
currentInstances.Add(itemInst);
}
}
//存在跨实体数据则使用根节点作为焦点
if (currentInstances.Count > 0)
{
viewParam.IsRootForceFormId = true;
entityInstances.AddRange(currentInstances);
}
}
}
/// <summary>
/// 得到所有节点
/// </summary>
/// <param name="parentNote"></param>
/// <returns></returns>
private List<RouteTreeNode> GetAllNodes(RouteTreeNode parentNote, int parentDepth = 0)
{
List<RouteTreeNode> allNode = new List<RouteTreeNode>();
allNode.Add(parentNote);
foreach (var child in parentNote.ChildNodes)
{
int currDepth = parentDepth;
RecursionLimitUtils.Run(ref currDepth, 200, "TrackAllFlows->GetAllNodes");
var childs = this.GetAllNodes(child, currDepth);
allNode.AddRange(childs);
}
return allNode;
}
/// <summary>
///
/// </summary>
/// <param name="item"></param>
/// <returns></returns>
private List<BusinessFlowInstance> GetInstances(KeyValuePair<TableDefine, List<long>> item, HashSet<string> reachedTableName)
{
//构建实体key和列名集合
var tbDefine = item.Key;
var entityKeyAndColumnKey = new Dictionary<string, string>();
var businessInfo = FormMetaDataCache.GetCachedFormMetaData(Context, tbDefine.FormId).BusinessInfo;
Entity noteEntity = businessInfo.GetEntity(tbDefine.EntityKey); //节点对应的实体
var tuple = this.GetFilterCondition(businessInfo, noteEntity, item.Value);
var pkName = businessInfo.GetForm().PkFieldName;
var headTbDefine = BusinessFlowServiceHelper.LoadTableDefine(Context, tbDefine.FormId, businessInfo.Entrys[0].Key, false);
if (headTbDefine != null)
{
if (!reachedTableName.Contains(headTbDefine.TableNumber))
{
entityKeyAndColumnKey[businessInfo.Entrys[0].Key] = pkName; //单据头
}
}
//构建参数
var queryParam = new QueryBuilderParemeter();
queryParam.FormId = businessInfo.GetForm().Id;
queryParam.FilterClauseWihtKey = tuple.Item1;
queryParam.SelectItems.Add(new SelectorItemInfo(pkName));
foreach (var entry in businessInfo.Entrys)
{
if (entry is EntryEntity && entry.Key != noteEntity.Key)
{
var entryTableDefine = BusinessFlowServiceHelper.LoadTableDefine(Context, tbDefine.FormId, entry.Key, false);
if (entryTableDefine == null) continue;
if (reachedTableName.Contains(entryTableDefine.TableNumber)) continue;
var columnKey = string.Format("{0}_{1}", entry.Key, entry.EntryPkFieldName);
entityKeyAndColumnKey[entry.Key] = columnKey;
queryParam.SelectItems.Add(new SelectorItemInfo(columnKey));
}
}
List<BusinessFlowInstance> lstInstances = new List<BusinessFlowInstance>();
if (entityKeyAndColumnKey.Count > 0)
{
//得到查询数据
DynamicObjectCollection objs;
if (tuple.Item2 == null)
{
objs = QueryServiceHelper.GetDynamicObjectCollection(Context, queryParam);
}
else
{
objs = QueryServiceHelper.GetDynamicObjectCollection(Context, queryParam, new List<SqlParam>() { tuple.Item2 });
}
//构建查询节点数据
var allEntityIds = new Dictionary<string, List<Tuple<long, long>>>();
foreach (var dic in entityKeyAndColumnKey)
{
var lstTuple = new List<Tuple<long, long>>();
allEntityIds[dic.Key] = lstTuple;
foreach (var obj in objs)
{
var billId = ObjectUtils.Object2Int64(obj[pkName]);
var entityId = ObjectUtils.Object2Int64(obj[dic.Value]);
if (entityId == 0) continue;
var tItem = Tuple.Create<long, long>(billId, entityId);
if (!lstTuple.Contains(tItem))
{
lstTuple.Add(tItem);
}
}
}
var lstBillId = allEntityIds.First().Value.Select(x => x.Item1).ToList();
lstInstances = this.LoadAllInstances(allEntityIds, businessInfo.Entrys[0], lstBillId, businessInfo);
}
return lstInstances;
}
/// <summary>
/// 得到过滤条件
/// </summary>
/// <param name="businessInfo"></param>
/// <param name="entity"></param>
/// <param name="entityIds"></param>
/// <returns></returns>
private Tuple<string, SqlParam> GetFilterCondition(BusinessInfo businessInfo, Entity entity, List<long> entityIds)
{
string pkKey = "";
SqlParam param = null;
if (this.IsHeadEntity(entity))
{
pkKey = businessInfo.GetForm().PkFieldName;
}
else
{
pkKey = string.Concat(entity.Key, "_", entity.EntryPkFieldName);
}
var ids = entityIds.Distinct().ToArray();
var count = ids.Count();
var filter = string.Empty;
if (count == 1)
{
filter = string.Format(" {0} = {1} ", pkKey, ids[0]);
}
else if (count <= 20) // 批量较小,直接采用IN语句
{
filter = string.Format(" {0} IN ({1}) ", pkKey, string.Join(",", ids));
}
else // 数目较大,采用表变量
{
string paramName = "@FID";
string tSql = StringUtils.GetSqlWithCardinality(count, paramName, 1, false);
filter = string.Format(" EXISTS ( {0} WHERE b.FID = {1} ))", tSql, pkKey);
param = new SqlParam(paramName, KDDbType.udt_inttable, ids);
}
return Tuple.Create<string, SqlParam>(filter, param); ;
}
/// <summary>
/// 加载单据体关联的全部业务流程实例
/// </summary>
/// <param name="formId">单据</param>
/// <param name="entityKey">单据体</param>
/// <param name="entityIds">单据体内码</param>
/// <returns></returns>
private List<BusinessFlowInstance> LoadInstancesByEntryIds(string formId, string entityKey, long[] entityIds)
{
LoadInstancesByEntityIdArgs args = new LoadInstancesByEntityIdArgs(formId, entityKey, entityIds);
args.LoadByMasterId = true; // 加载全部流程实例
BusinessFlowInstanceCollection instances = BusinessFlowDataServiceHelper.LoadInstancesByEntityId(Context, args);
if (instances == null || instances.Count == 0)
{
return new List<BusinessFlowInstance>();
}
else
{
List<BusinessFlowInstance> list = MergeInstances(instances);
return list;
}
}
/// <summary>
/// 把业务流程实例按照MasterId进行合并
/// </summary>
/// <param name="instances"></param>
/// <returns></returns>
private List<BusinessFlowInstance> MergeInstances(BusinessFlowInstanceCollection instances)
{
List<BusinessFlowInstance> mergeInstances = new List<BusinessFlowInstance>();
// 对全部实例按照MasterId进行分组
Dictionary<string, List<BusinessFlowInstance>> dict = new Dictionary<string, List<BusinessFlowInstance>>();
foreach (var instance in instances)
{
List<BusinessFlowInstance> lst = null;
if (!dict.TryGetValue(instance.MasterId, out lst))
{
lst = new List<BusinessFlowInstance>();
dict[instance.MasterId] = lst;
}
lst.Add(instance);
}
foreach (var group in dict) // 对每组实例进行合并处理
{
List<BusinessFlowInstance> groupInstances = group.Value;
BusinessFlowInstance instance = this.JoinGroupRouteTree(groupInstances);
if (instance != null)
{
mergeInstances.Add(instance);
}
}
return mergeInstances;
}
/// <summary>
/// 对一组实例的首尾节点进行合并,合并结果返回,其他实例丢弃
/// </summary>
/// <param name="groupInstances"></param>
/// <param name="firstNodes"></param>
private BusinessFlowInstance JoinGroupRouteTree(List<BusinessFlowInstance> groupInstances)
{
//连接到首实例的子节点集合
List<RouteTreeNode> joinChildNodes = new List<RouteTreeNode>();
BusinessFlowInstance firstInstance = null;
RouteTreeNode groupFirstNode = null; // 一组实例的首节点
for (int i = 0; i < groupInstances.Count; i++) // 逐个实例,分析其首节点,是否被其他实例包含
{
BusinessFlowInstance childInstance = groupInstances[i];
RouteTreeNode childNode = childInstance.FirstNode;
if (childNode == null)
{
continue; // 这个实例无首节点,是个废实例,直接忽略
}
if (childInstance.Id.EqualsIgnoreCase(childInstance.MasterId)) // Id==MasterId
{
firstInstance = childInstance;
groupFirstNode = childNode;
continue; // 首实例,其节点不可能是其他实例的尾节点
}
for (int j = 0; j < groupInstances.Count; j++) // 对其他实例进行比较,找出本实例的直接上级,找到后,进行拼接
{
if (j == i)
{
continue; // 同实例,不比较
}
BusinessFlowInstance instanceB = groupInstances[j];
RouteTreeNode firstNodeB = instanceB.FirstNode;
if (firstNodeB == null)
{
continue;
}
this.JoinRouteTreeNode(childNode, firstNodeB, ref joinChildNodes);
}
}
//把需要连接的节点和所有子孙节点加入到主节点的子孙节点中
if (joinChildNodes.Count > 0 && groupFirstNode != null)
{
foreach (var subJoinNode in joinChildNodes)
{
groupFirstNode.AllChildNodes.Add(subJoinNode);
if (subJoinNode.AllChildNodes != null && subJoinNode.AllChildNodes.Count > 0)
{
groupFirstNode.AllChildNodes.AddRange(subJoinNode.AllChildNodes);
}
}
}
return firstInstance;
}
/// <summary>
/// 把子流程的首节点,合并到父流程的尾节点中
/// </summary>
/// <param name="childInstanceNode">子实例</param>
/// <param name="parentInstanceNode">父实例</param>
private void JoinRouteTreeNode(RouteTreeNode childInstanceNode, RouteTreeNode parentInstanceNode, ref List<RouteTreeNode> joinChildNodes)
{
// 在父实例中找子实例的首节点
List<RouteTreeNode> parentInstLastNodes = new List<RouteTreeNode>();
parentInstanceNode.SearchChildNodes(childInstanceNode.Id, ref parentInstLastNodes);
foreach (var parentInstLastNode in parentInstLastNodes) // 对匹配的尾节点循环
{
foreach (var childInstNode in childInstanceNode.ChildNodes)
{
var existNode = parentInstLastNode.ChildNodes.FirstOrDefault((item) => item.Id.CId.EqualsIgnoreCase(childInstNode.Id.CId));
if (existNode != null)
{
continue;
}
parentInstLastNode.ChildNodes.Add(childInstNode); // 把子节点,拼接到尾节点,延伸尾节点;通过此方式覆盖子实例
childInstNode.ParentNode = parentInstLastNode; // 把子节点的父节点指向到父实例的尾节点上,使首尾能够衔接;可以继续反查
joinChildNodes.Add(childInstNode);
}
}
}
#endregion 加载并合并流程实例
/// <summary>
/// 从流程实例中,移除与发起联查单据无关的路线节点
/// </summary>
/// <param name="formId"></param>
/// <param name="entityKey"></param>
/// <param name="entityIds"></param>
/// <param name="instances"></param>
/// <returns></returns>
private List<BusinessFlowInstance> RemoveJointlessNodes(string formId, string entityKey, long[] entityIds, List<BusinessFlowInstance> instances)
{
List<BusinessFlowInstance> list = new List<BusinessFlowInstance>();
if (instances == null || instances.Count == 0) return list;
string tableNumber = this.LoadTableNumber(formId, entityKey);
foreach (var instance in instances)
{
// 无首节点
if (instance.FirstNode == null) continue;
// 仅首节点:下推过,但是下游单据被删除了
if (instance.FirstNode.ChildNodes.Count == 0) continue;
_removeedNodes.Clear();
var isLeft = RemoveJointlessChildNodes(instance.FirstNode, tableNumber, entityIds);
if (!isLeft) continue;
if (_removeedNodes.Count() > 0)
{
HandlerLeftNodes(instance.FirstNode);
}
list.Add(instance);
}
return list;
}
private void HandlerLeftNodes(RouteTreeNode parendNode, int parentDepth = 0)
{
if (parendNode.ChildNodes != null && parendNode.ChildNodes.Count > 0)
{
List<RouteTreeNode> childNodes = new List<RouteTreeNode>(parendNode.ChildNodes);
foreach (var childNode in childNodes)
{
if (_removeedNodes.Contains(childNode))
{
parendNode.ChildNodes.Remove(childNode);
}
else
{
int currDepth = parentDepth;
RecursionLimitUtils.Run(ref currDepth, 200, "TrackAllFlows->HandlerLeftNodes");
HandlerLeftNodes(childNode, currDepth);
}
}
}
//这个不能注释掉,此数据在鼠标放在节点上显示控制字段的值提示时有用到。
if (parendNode.AllChildNodes != null && parendNode.AllChildNodes.Count > 0)
{
List<RouteTreeNode> childNodes = new List<RouteTreeNode>(parendNode.AllChildNodes);
foreach (var childNode in childNodes)
{
if (_removeedNodes.Contains(childNode))
{
parendNode.AllChildNodes.Remove(childNode);
}
//else
//{
// HandlerLeftNodes(childNode);
//}
}
}
}
List<RouteTreeNode> _removeedNodes = new List<RouteTreeNode>();
/// <summary>
/// 递归搜索节点及其子节点,移除全部与目标单据无关的路线
/// </summary>
/// <param name="parendNode"></param>
/// <param name="tableNumber"></param>
/// <param name="entityIds"></param>
/// <returns></returns>
/// <remarks>
/// 如果节点本身为目标单据,保留;
/// 如果节点子节点为目标单据,本节点保留,本节点的其他分支继续搜索
/// </remarks>
private bool RemoveJointlessChildNodes(RouteTreeNode parendNode, string tableNumber, long[] entityIds, int parentDepth = 0)
{
if (parendNode.Id.Tbl.EqualsIgnoreCase(tableNumber)
&& entityIds.Contains(parendNode.Id.EId))
{// 本节点为目标单据,无需往后找,本节点需要保留
return true;
}
// 本节点非目标节点,且无子节点 => 本节点需要被移除
if (parendNode.ChildNodes == null || parendNode.ChildNodes.Count == 0) return false;
// 本节点不是目标单据,则逐个子节点进行判断,如果子节点与目标单据无关,则移除,全部节点均移除后,本节点移除
List<RouteTreeNode> childNodes = new List<RouteTreeNode>(parendNode.ChildNodes);
foreach (var childNode in childNodes)
{
int currDepth = parentDepth;
//RecursionLimitUtils.Run(ref currDepth, 200, "TrackAllFlows->RemoveJointlessChildNodes");
var isRemove = RemoveJointlessChildNodes(childNode, tableNumber, entityIds, currDepth);
if (!isRemove)
{// 子节点递归搜索,未发现与本节点的关系,则移除
parendNode.ChildNodes.Remove(childNode);//只移除了父节点下的子节点,但祖先下的节点并没有移除,在跨实体的时候依然会被访问。
parendNode.AllChildNodes.Remove(childNode);
_removeedNodes.Add(childNode); //记录下已被移除的节点,等退出循环后再对所有剩下的节点循环处理
}
}
if (parendNode.ChildNodes.Count == 0)
{// 本节点的子节点已经被移除完了,说明本节点与目标单据无关,也需要被移除
return false;
}
else
{
return true; // 本节点的子节点与目标单据有关而被保留,本节点也需要保留
}
}
/// <summary>
///
/// </summary>
/// <param name="formId"></param>
/// <param name="entityKey"></param>
/// <returns></returns>
private string LoadTableNumber(string formId, string entityKey)
{
TableDefine tableDefine = BusinessFlowServiceHelper.LoadTableDefine(Context, formId, entityKey);
if (tableDefine == null)
{
return "";
}
return tableDefine.TableNumber;
}
/// <summary>
///
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
private bool IsHeadEntity(Entity entity)
{
bool isHeadEntity = (entity is HeadEntity) || (entity is SubHeadEntity);
return isHeadEntity;
}
}
}
```
【效果】
![1679457477982.webp](/download/010004c7aae802364cdbbedac6940718b5d4.webp)
RecursionLimitUtils需要引用那个包?
业务流.全流程跟踪.源单和下游单显示不同的原因和二开方案
【场景】采购订单下推采购入库单,采购订单下推付款申请单采购订单的全流程图![1679457091489.webp](/download/0100d2265faa09cf4711b5eb78...
点击下载文档
本文2024-09-16 18:32:01发表“云星空知识”栏目。
本文链接:https://wenku.my7c.com/article/kingdee-k3cloud-22763.html
您需要登录后才可以发表评论, 登录登录 或者 注册
最新文档
热门文章