/// <summary>
        /// 操作已经完成,事务未提交时触发:同步删除下游单据数据
        /// </summary>
        /// <param name="e"></param>
        public override void EndOperationTransaction(EndOperationTransactionArgs e)
        {
            // 本单主键为字符串类型,略过
            if (this.BusinessInfo.GetForm().PkFieldType == EnumPkFieldType.STRING)
            {
                return;
            }

            // 把单据数据包,展开为按实体Key为标识存储的数据字典
            ExtendedDataEntitySet dataEntitySet = new ExtendedDataEntitySet();

            dataEntitySet.Parse(e.DataEntitys, this.BusinessInfo);

            // 对各单据体进行循环,分别扫描下游单据,逐单删除
            foreach (var entity in this.BusinessInfo.Entrys)
            {
                // 判断此实体,是否需要扫描下游单据
                if (this.IgnoreEntity(entity))
                {
                    continue;
                }

                // 取实体数据集合
                ExtendedDataEntity[] rows = dataEntitySet.FindByEntityKey(entity.Key);
                if (rows == null)
                {
                    // 容错
                    continue;
                }

                // 取实体的业务流程数据
                HashSet <long> entityIds = new HashSet <long>();
                foreach (var row in rows)
                {
                    long entityId = Convert.ToInt64(row.DataEntity[0]);
                    entityIds.Add(entityId);
                }
                BusinessFlowInstanceCollection bfInstances = this.LoadBFInstance(entity.Key, entityIds);
                if (bfInstances == null || bfInstances.Count == 0)
                {
                    // 无关联的业务流程实例,略过
                    continue;
                }

                // 从业务流程实例中,分析出本单据体的下游单据内码:按目标单据体分好组
                Dictionary <string, HashSet <long> > dctTargetEntityIds = this.GetTargetEntityIds(
                    entity, entityIds, bfInstances);

                // 对各种下游单据进行循环,逐个删除
                foreach (var targetBill in dctTargetEntityIds)
                {
                    IOperationResult deleteResult = this.DeleteTargetBill(targetBill.Key, targetBill.Value);
                    if (CheckOpResult(deleteResult) == false)
                    {
                        // 删除失败,无需继续,退出
                        return;
                    }
                }
            }
        }
        /// <summary>
        /// 分析业务流程实例,输出全部下游单据
        /// </summary>
        /// <param name="entity">上游单据体</param>
        /// <param name="entityIds">上游单据体内码</param>
        /// <param name="bfInstances">相关的业务流程实例</param>
        /// <returns>Dictioanry(下游单据表格编码, 下游单据体内码集合)</returns>
        private Dictionary <string, HashSet <long> > GetTargetEntityIds(
            Entity entity, HashSet <long> entityIds,
            BusinessFlowInstanceCollection bfInstances)
        {
            Dictionary <string, HashSet <long> > dctTargetEntityIds = new Dictionary <string, HashSet <long> >();

            IBusinessFlowService bfService      = ServiceHelper.GetService <IBusinessFlowService>();
            TableDefine          srcTableDefine = bfService.LoadTableDefine(
                this.Context, this.BusinessInfo.GetForm().Id, entity.Key);

            // 逐个实例查找本单的下游单据体内码
            foreach (var instance in bfInstances)
            {
                // 首先找到业务流程实例中,本单所在的节点
                List <RouteTreeNode> srcNodes = instance.SerarchTargetFormNodes(srcTableDefine.TableNumber);
                foreach (RouteTreeNode srcNode in srcNodes)
                {
                    if (entityIds.Contains(srcNode.Id.EId))
                    {
                        // 找到了本单所在的节点,按类别输出其下游节点:
                        foreach (RouteTreeNode targetNode in srcNode.ChildNodes)
                        {
                            if (dctTargetEntityIds.Keys.Contains(targetNode.Id.Tbl) == false)
                            {
                                dctTargetEntityIds.Add(targetNode.Id.Tbl, new HashSet <long>());
                            }
                            if (dctTargetEntityIds[targetNode.Id.Tbl].Contains(targetNode.Id.EId) == false)
                            {
                                dctTargetEntityIds[targetNode.Id.Tbl].Add(targetNode.Id.EId);
                            }
                        }
                    }
                }
            }
            return(dctTargetEntityIds);
        }