二开案例.服务插件.高并发下控制单据编号重复问题

栏目:云星空知识作者:金蝶来源:金蝶云社区发布:2024-09-23浏览:1

二开案例.服务插件.高并发下控制单据编号重复问题

【应用场景】

业务单据通常会在保存操作上配置编码唯一校验器,用于控制单据编号重复的问题,但是在WebApi高并发下,由于校验器的机制问题,使得校验结果具有一定的滞后性,因此仍有可能会产生重复单号的单据。此时,依赖数据库的唯一索引控制单据编号重复,是最高效,最佳的选择。


【案例演示】

采购订单,通过在数据库新建单据编号表,并给单据编号字段设置为主键,依托数据库的主键的数据唯一性检查,进行高并发下的单据编号重复控制。


【实现步骤】

<1>编写服务插件,代码如下。

using Kingdee.BOS;
using Kingdee.BOS.App.Data;
using Kingdee.BOS.Core.DynamicForm.PlugIn;
using Kingdee.BOS.Core.DynamicForm.PlugIn.Args;
using Kingdee.BOS.Core.Metadata.FieldElement;
using Kingdee.BOS.Core.Metadata.FormElement;
using Kingdee.BOS.Orm.DataEntity;
using Kingdee.BOS.Util;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Transactions;
namespace Jac.XkDemo.BOS.App.PlugIn
{
    /// <summary>
    /// 【服务插件】高并发下控制单据编号重复问题(保存)
    /// </summary>
    [Description("【服务插件】高并发下控制单据编号重复问题(保存)"), HotUpdate]
    public class BillNoUniqueSaveServicePlugIn : AbstractOperationServicePlugIn
    {
        public override void EndOperationTransaction(EndOperationTransactionArgs e)
        {
            base.EndOperationTransaction(e);
            var billNoField = this.BusinessInfo.GetBillNoField() as BillNoField;
            if (billNoField == null)
            {
                return;
            }
            var formId = this.BusinessInfo.GetForm().Id;
            #region 先删
            string ids;
            if (this.BusinessInfo.GetForm().PkFieldType == EnumPkFieldType.STRING)
            {
                ids = string.Join(",", e.DataEntitys.Select(o => "'" + Convert.ToString(o[0]) + "'").ToArray());
            }
            else
            {
                ids = string.Join(",", e.DataEntitys.Select(o => Convert.ToString(o[0])).ToArray());
            }
            var sqlGetOldBillNo = string.Format("SELECT {2},{0} FROM {1} WHERE {2} IN ({3})", billNoField.FieldName, this.BusinessInfo.GetEntity(0).TableName, this.BusinessInfo.GetForm().PkFieldName, ids);
            DynamicObjectCollection oldBillNoObjs;
            using (var trans = new KDTransactionScope(TransactionScopeOption.Suppress))
            {
                oldBillNoObjs = DBUtils.ExecuteDynamicObject(Context, sqlGetOldBillNo);
            }
            if (oldBillNoObjs != null && oldBillNoObjs.Count > 0)
            {
                var dicOld = new Dictionary<string, string>();
                foreach (var obj in oldBillNoObjs)
                {
                    var billNo = Convert.ToString(obj[1]);
                    if (string.IsNullOrWhiteSpace(billNo))
                    {
                        continue;
                    }
                    dicOld[Convert.ToString(obj[0])] = Convert.ToString(obj[1]);
                }
                var needDeleteBillNos = new List<string>();
                foreach (var dataEntity in e.DataEntitys)
                {
                    var id = Convert.ToString(dataEntity[0]);
                    var billNo = Convert.ToString(dataEntity[billNoField.PropertyName]);
                    if (dicOld.ContainsKey(id) && dicOld[id] != billNo)
                    {
                        // 单号被修改,旧单号删除
                        needDeleteBillNos.Add(dicOld[id]);
                    }
                }
                if (needDeleteBillNos.Count > 0)
                {
                    var sqlDeleteOldBillNo = string.Format("DELETE FROM T_JAC_BILLNO_UNIQUECHECK WHERE FOBJECTID='{0}' AND FBILLNO IN ({1})", formId, string.Join(",", needDeleteBillNos.Select(o => "'" + o + "'")));
                    DBUtils.Execute(Context, sqlDeleteOldBillNo);
                }
            }
            #endregion
            #region 再增
            var sqlTemplate = "INSERT INTO T_JAC_BILLNO_UNIQUECHECK (FOBJECTID,FBILLNO) VALUES (@FOBJECTID,@FBILLNO)";
            var sqlObjs = new List<SqlObject>();
            foreach (var dataEntity in e.DataEntitys)
            {
                var billNo = dataEntity[billNoField.PropertyName] as string;
                if (!string.IsNullOrWhiteSpace(billNo))
                {
                    var sqlObj = new SqlObject(sqlTemplate, new SqlParam("@FOBJECTID", KDDbType.AnsiString, formId));
                    sqlObj.Param.Add(new SqlParam("@FBILLNO", KDDbType.String, billNo));
                    sqlObjs.Add(sqlObj);
                }
            }
            if (sqlObjs.Count > 0)
            {
                DBUtils.ExecuteBatch(Context, sqlObjs);
            }
            #endregion
        }
    }
    /// <summary>
    /// 【服务插件】高并发下控制单据编号重复问题(删除)
    /// </summary>
    [Description("【服务插件】高并发下控制单据编号重复问题(删除)"), HotUpdate]
    public class BillNoUniqueDeleteServicePlugIn : AbstractOperationServicePlugIn
    {
        public override void EndOperationTransaction(EndOperationTransactionArgs e)
        {
            base.EndOperationTransaction(e);
            var billNoField = this.BusinessInfo.GetBillNoField() as BillNoField;
            if (billNoField == null)
            {
                return;
            }
            var formId = this.BusinessInfo.GetForm().Id;
            var sqlTemplate = "DELETE FROM T_JAC_BILLNO_UNIQUECHECK WHERE FOBJECTID=@FOBJECTID AND FBILLNO=@FBILLNO";
            var sqlObjs = new List<SqlObject>();
            foreach (var dataEntity in e.DataEntitys)
            {
                var billNo = dataEntity[billNoField.PropertyName] as string;
                if (!string.IsNullOrWhiteSpace(billNo))
                {
                    var sqlObj = new SqlObject(sqlTemplate, new SqlParam("@FOBJECTID", KDDbType.AnsiString, formId));
                    sqlObj.Param.Add(new SqlParam("@FBILLNO", KDDbType.String, billNo));
                    sqlObjs.Add(sqlObj);
                }
            }
            if (sqlObjs.Count > 0)
            {
                DBUtils.ExecuteBatch(Context, sqlObjs);
            }
        }
    }
}



/*
-- 创建单据编号唯一控制表
IF EXISTS(SELECT * FROM sys.objects WHERE name='T_JAC_BILLNO_UNIQUECHECK' AND type='U')
DROP TABLE T_JAC_BILLNO_UNIQUECHECK
CREATE TABLE T_JAC_BILLNO_UNIQUECHECK
(
FOBJECTID
VARCHAR(50)NOT NULL DEFAULT (''),
FBILLNO
NVARCHAR (50)  NOT NULL DEFAULT ('')
)
ALTER TABLE T_JAC_BILLNO_UNIQUECHECK ADD CONSTRAINT [PK_T_JAC_BILLNO_UNIQUECHECK] PRIMARY KEY CLUSTERED (FOBJECTID,FBILLNO) ON [PRIMARY]
*/

<2>拷贝插件组件到应用站点的WebSite\Bin目录下,重启IIS。


<3>登录数据中心所在数据库,执行以下SQL,创建单据编号唯一性控制表。

IF EXISTS(SELECT * FROM sys.objects WHERE name='T_JAC_BILLNO_UNIQUECHECK' AND type='U')
DROP TABLE T_JAC_BILLNO_UNIQUECHECK
CREATE TABLE T_JAC_BILLNO_UNIQUECHECK
(
FOBJECTID
VARCHAR(50)NOT NULL DEFAULT (''),
FBILLNO
NVARCHAR (50)  NOT NULL DEFAULT ('')
)
ALTER TABLE T_JAC_BILLNO_UNIQUECHECK ADD CONSTRAINT [PK_T_JAC_BILLNO_UNIQUECHECK] PRIMARY KEY CLUSTERED (FOBJECTID,FBILLNO) ON [PRIMARY]


<4>BOSIDE扩展采购订单,保存操作和删除操作分别注册服务插件,保存元数据,开发完毕。



【功能验证】

<1>登录业务站点,打开采购订单编辑界面,新增单据,录入一个已存在的单号,此时保存单据会报错。

<2>通过WebApi的方式创建单据,如果单号已存在,也会报错。

---------------------------------------------------------------------------------------------------------













【金蝶云星空BOS二次开发案例演示】https://vip.kingdee.com/article/94751030918525696


二开案例.服务插件.高并发下控制单据编号重复问题

【应用场景】业务单据通常会在保存操作上配置编码唯一校验器,用于控制单据编号重复的问题,但是在WebApi高并发下,由于校验器的机制问题,...
点击下载文档
确认删除?
回到顶部
客服QQ
  • 客服QQ点击这里给我发消息