
【场景】存在场景,A用户下推未保存,B用户反审核,而后A用户保存时导致的单据异常;
包括可能:网控被用户误操作移除,历史版本下推操作网控控制不准,自定义二开逻辑没有关联网控,api反审核等;
导致最终单据未审核但是存在下游的的场景
【方案】在下游单据保存操作服务插件时,强校验源单的单据状态为已审核

代码实现逻辑:根据link找到上游单据内码,在事务内去数据库查询比对一次,有必要的话可以升级成行锁
```csharp
using Kingdee.BOS;
using Kingdee.BOS.App.Data;
using Kingdee.BOS.BusinessEntity.BusinessFlow;
using Kingdee.BOS.Core.DynamicForm.PlugIn;
using Kingdee.BOS.Core.DynamicForm.PlugIn.Args;
using Kingdee.BOS.Core.Metadata;
using Kingdee.BOS.Core.Metadata.EntityElement;
using Kingdee.BOS.Orm.DataEntity;
using Kingdee.BOS.ServiceHelper;
using Kingdee.BOS.Util;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DynamicFormPlugIn.Operation
{
[Kingdee.BOS.Util.HotUpdate]
[System.ComponentModel.Description("校验源单状态 操作服务插件")]
public class ValidateSrcBillServicePlugIn : AbstractOperationServicePlugIn
{
/*
* 部分场景下下游单据保存时上游已经反审核,导致的数据异常
*
* 方案:
* 下游保存增加校验器,校验上游的状态
*/
/// <summary>
/// 事务内事件,要保证强一致应该放在BeiginOperationTransaction
/// </summary>
/// <param name="e"></param>
public override void BeginOperationTransaction(BeginOperationTransactionArgs e)
{
/*
* 根据link实体,获取源单类型和源单内码,然后获取一次单据状态,验证等于审核
*/
var billForm = this.BusinessInfo.GetForm();
if (billForm.LinkSet == null || billForm.LinkSet.LinkEntitys.Count == 0)
return;
Dictionary<string, List<int>> srcFormBillIds = new Dictionary<string, List<int>>();
// <0>收集关联源单对象和单据内码
foreach(var linkEntity in billForm.LinkSet.LinkEntitys)
{
if (linkEntity == null)
continue;
string entityKey = linkEntity.ParentEntityKey;
var entity = this.BusinessInfo.GetEntryEntity(entityKey);
if (entity == null)
continue;
for (int i = 0, sz = e.DataEntitys.Length; i < sz; ++i)
{
var billData = e.DataEntitys[i];
if (billData == null)
continue;
// <1>获取link 实体的父实体数据包
var entityObjColl = GetEntityDynamicObject(billData, entity);
for (int rowIdx = 0, rowCnt = entityObjColl.Count; rowIdx < rowCnt; ++rowIdx)
{
var entityRow = entityObjColl[rowIdx];
if (entityRow == null)
continue;
if (!entityRow.DynamicObjectType.Properties.ContainsKey(linkEntity.Key))
continue;
// <2>根据link实体 收集 源单内码 源单对象
var linkObjCol = entityRow[linkEntity.Key] as DynamicObjectCollection;
if (linkObjCol == null || linkObjCol.Count <= 0)
continue;
foreach (var linkObj in linkObjCol)
{
if (linkObj == null)
continue;
var linkSTableName = ObjectUtils.Object2String(linkObj["STableName"]);
var linkSBillid = ObjectUtils.Object2Int(linkObj["SBillId"]);
if (linkSTableName.IsNullOrEmptyOrWhiteSpace())
continue;
if (linkSBillid == 0)
continue;
if (!srcFormBillIds.ContainsKey(linkSTableName))
srcFormBillIds[linkSTableName] = new List<int>();
srcFormBillIds[linkSTableName].Add(linkSBillid);
}
}
}
}
// <3>根据源单计算
foreach (var tableKey in srcFormBillIds.Keys)
{
var tableDefine = LoadTableDefine(tableKey);
if (tableDefine == null)
continue;
string formId = tableDefine.FormId;
var metaData = MetaDataServiceHelper.Load(this.Context, formId) as FormMetadata;
if (metaData == null)
continue;
var headEntity = metaData.BusinessInfo.GetEntity(0);
if (headEntity == null || headEntity.TableName.IsNullOrEmptyOrWhiteSpace())
continue;
var docStatausKey = metaData.BusinessInfo.GetForm().DocumentStatusFieldKey;
if (docStatausKey == null || docStatausKey.IsNullOrEmptyOrWhiteSpace())
continue;
var docStatusField = metaData.BusinessInfo.GetField(docStatausKey);
if (docStatusField == null)
continue;
var distBillIds = srcFormBillIds[tableKey].Distinct().ToArray();
string pkField = metaData.BusinessInfo.GetForm().PkFieldName;
List<SqlParam> paramList = new List<SqlParam>();
var sqlBuilder = new StringBuilder();
sqlBuilder.AppendFormat("SELECT {0} FROM {1}", pkField, headEntity.TableName);
if (distBillIds.Length == 1)
{
sqlBuilder.AppendFormat(" WHERE {0} = @FID", pkField, distBillIds[0]);
paramList.Add(new SqlParam("@FID", KDDbType.Int32, distBillIds[0]));
}
else if (distBillIds.Length <= 20)
{
sqlBuilder.AppendFormat(" WHERE {0} in ({1})", pkField, string.Join(",", distBillIds));
}
else
{
var tempTb1 = StringUtils.GetSqlWithCardinality(distBillIds.Length, "@FID", 1);
paramList.Add(new SqlParam("@FID", KDDbType.udt_inttable, distBillIds));
sqlBuilder.AppendFormat(" INNER JOIN {0} t2 ON (t2.FID = t0.FSId ) ", tempTb1);
sqlBuilder.Append(" WHERE 1=1");
}
sqlBuilder.AppendFormat(" AND {0} <> 'C' ", docStatusField.FieldName);
using (var dr = DBUtils.ExecuteReader(this.Context, sqlBuilder.ToString(), paramList))
{
if (dr.Read())
{
throw new Exception(string.Format("存在单据非已审核状态,{0},{1}", formId, dr["FID"]));
}
}
}
}
/// <summary>
/// 加载表格定义对象
/// </summary>
/// <param name="tableNumber">表格编码</param>
/// <returns></returns>
private TableDefine LoadTableDefine(st