套打.常见问题.打印次数强并发控制方案

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

套打.常见问题.打印次数强并发控制方案

【场景】打印次数强并发控制方案 【背景知识】 打印次数的计算逻辑:[套打.常见问题.打印日志和打印次数更新逻辑](https://vip.kingdee.com/link/s/lQa2i) 【方案】 (0)手工增加表,设置业务对象标识和单据内码作为唯一约束,用作并发控制 ```sql --sqlserver IF NOT EXISTS (SELECT 1 FROM (SELECT NAME AS TABLE_NAME, XTYPE AS TABLE_XTYPE FROM sysobjects WHERE XTYPE = 'U' OR XTYPE = 'V') AS KSQL_USERTABLES WHERE TABLE_NAME = 'Tmp_PrintUniqueControl') BEGIN CREATE TABLE Tmp_PrintUniqueControl (FID VARCHAR (36) NOT NULL DEFAULT LOWER(NEWID()), FObjectTypeId VARCHAR (50) NOT NULL DEFAULT ' ', FKey VARCHAR (50) NOT NULL DEFAULT ' ', FUSERID BIGINT NOT NULL DEFAULT 0, FCREATEDATE DATETIME NULL DEFAULT GETDATE()) END ; IF NOT EXISTS (SELECT 1 FROM (SELECT sysobjects.NAME AS TABLE_NAME, sysindexes.NAME AS INDEX_NAME FROM sysobjects INNER JOIN sysindexes ON sysindexes.ID = sysobjects.ID) AS KSQL_INDEXES WHERE INDEX_NAME = 'UNIQUE_Tmp_PrintUniqueControl') BEGIN ALTER TABLE Tmp_PrintUniqueControl ADD CONSTRAINT UNIQUE_TEST_CONTROL UNIQUE (FObjectTypeId, FKey) END ; ``` (1)增加业务对象绑定此表,用作当打印失败时供其他用户确认检查和清理的逻辑 业务对象——基础资料,绑定BOS_ObjectType 单据内码——文本 打印时间——长日期 用户——用户 ![image.webp](/download/01004bce75600a264548b9e9d42d7db20757.webp) ![image.webp](/download/010093910cfa162045a4aee1943be6debba5.webp) (2)增加表单插件、列表插件,在打印菜单点击时加锁,打印完成收到前端指令返回时解锁 ![image.webp](/download/0100680950cf8a754813a970e8bb4dc96b25.webp) ![image.webp](/download/0100a29ca7ade0724198955a1e235f46ced2.webp) ![20240516 1617.webp](/download/01001bfcc17f48e44aeb8713667d7b5ec97c.webp) ```csharp using Kingdee.BOS; using Kingdee.BOS.App.Data; using Kingdee.BOS.Contracts; using Kingdee.BOS.Core; using Kingdee.BOS.Core.Bill.PlugIn; using Kingdee.BOS.Core.DynamicForm.PlugIn.Args; using Kingdee.BOS.Core.List.PlugIn; using Kingdee.BOS.Util; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Transactions; namespace Fk.PlugInSample.FormPlugIn { [Kingdee.BOS.Util.HotUpdate] [System.ComponentModel.Description("打印控制表单插件")] public class PrintUniqueControlBillPlugIn : AbstractBillPlugIn { PrintUniqueHandler handler; public override void OnInitialize(InitializeEventArgs e) { handler = new PrintUniqueHandler(this.Context, (msg) => this.View.ShowMessage(msg)); } /// <summary> /// 套打打印前命令 /// </summary> /// <param name="e"></param> public override void BeforeNotePrintCommand(BeforeNotePrintEventArgs e) { handler.BeforeNotePrintCommand(e); } /// <summary> /// 打印完成后事件 /// </summary> /// <param name="e"></param> public override void OnAfterPrint(AfterPrintEventArgs e) { handler.AfterPrint(e); } } [Kingdee.BOS.Util.HotUpdate] [System.ComponentModel.Description("打印控制列表插件")] public class PrintUniqueControlListPlugIn : AbstractListPlugIn { PrintUniqueHandler handler; public override void OnInitialize(InitializeEventArgs e) { handler = new PrintUniqueHandler(this.Context, (msg) => this.View.ShowMessage(msg)); } /// <summary> /// 套打打印前命令 /// </summary> /// <param name="e"></param> public override void BeforeNotePrintCommand(BeforeNotePrintEventArgs e) { handler.BeforeNotePrintCommand(e); } /// <summary> /// 打印完成后事件 /// </summary> /// <param name="e"></param> public override void OnAfterPrint(AfterPrintEventArgs e) { handler.AfterPrint(e); } } public class PrintUniqueItem { public string Fid; public string FormId; public string FKey; public override int GetHashCode() { return string.Concat(FormId, FKey).GetHashCode(); } public override bool Equals(object obj) { PrintUniqueItem rhs = obj as PrintUniqueItem; if (rhs == null) return false; return this.FormId == rhs.FormId && this.FKey == rhs.FKey; } } public class PrintUniqueHandler { /* --sqlserver IF NOT EXISTS (SELECT 1 FROM (SELECT NAME AS TABLE_NAME, XTYPE AS TABLE_XTYPE FROM sysobjects WHERE XTYPE = 'U' OR XTYPE = 'V') AS KSQL_USERTABLES WHERE TABLE_NAME = 'Tmp_PrintUniqueControl') BEGIN CREATE TABLE Tmp_PrintUniqueControl (FID VARCHAR (36) NOT NULL DEFAULT LOWER(NEWID()), FObjectTypeId VARCHAR (50) NOT NULL DEFAULT ' ', FKey VARCHAR (50) NOT NULL DEFAULT ' ', FUSERID BIGINT NOT NULL DEFAULT 0, FCREATEDATE DATETIME NULL DEFAULT GETDATE()) END ; IF NOT EXISTS (SELECT 1 FROM (SELECT sysobjects.NAME AS TABLE_NAME, sysindexes.NAME AS INDEX_NAME FROM sysobjects INNER JOIN sysindexes ON sysindexes.ID = sysobjects.ID) AS KSQL_INDEXES WHERE INDEX_NAME = 'UNIQUE_Tmp_PrintUniqueControl') BEGIN ALTER TABLE Tmp_PrintUniqueControl ADD CONSTRAINT UNIQUE_TEST_CONTROL UNIQUE (FObjectTypeId, FKey) END ; */ private readonly Context Context; private List<PrintUniqueItem> CurPrintingItem; private readonly Action<string> ShowMsgAct; public PrintUniqueHandler(Context ctx, Action<string> showMsgAct) { Context = ctx; ShowMsgAct = showMsgAct; CurPrintingItem = new List<PrintUniqueItem>(); } private void ShowMessage(string msg) { if (ShowMsgAct == null) return; ShowMsgAct(msg); } /// <summary> /// 打印前事件,增加网控 /// </summary> /// <param name="e"></param> public void BeforeNotePrintCommand(BeforeNotePrintEventArgs e) { e.ShowPreviewButton = false; if (!IsPrintOp(e.PrintType)) return; lock (this) { CheckCurPrintingExists(); if (CurPrintingItem.Count > 0) { ShowMessage("当前存在打印任务"); e.Cancel = true; return; } HashSet<PrintUniqueItem> printUniqueItems = new HashSet<PrintUniqueItem>(); foreach (var printJob in e.PrintJobs) { if (printJob == null || printJob.PrintJobItems == null) continue; string formId = printJob.FormId; foreach (var printJobItem in printJob.PrintJobItems) { string billId = printJobItem.BillId; PrintUniqueItem item = new PrintUniqueItem() { Fid = Guid.NewGuid().ToString(), FormId = formId, FKey = billId }; printUniqueItems.Add(item); } } if (!AddControl(printUniqueItems)) { ShowMessage("尝试增加单据网控异常,无法打印,请稍后重试;或打开打印冲突列表清除对应记录"); e.Cancel = true; return; } CurPrintingItem.AddRange(printUniqueItems); } } /// <summary> /// 打印完成,解除网控 /// </summary> /// <param name="e"></param> public void AfterPrint(AfterPrintEventArgs e) { if (e.NoteIDPairs != null && e.NoteIDPairs.Any()) { List<string> pkIds = CurPrintingItem.Select(x => x.Fid).ToList(); ClearByPkIds(pkIds); lock(this) { CurPrintingItem.Clear(); } } } /// <summary> /// 支持用户通过打印冲突列表删除后继续打印 /// </summary> private void CheckCurPrintingExists() { if (CurPrintingItem.Count <= 0) return; var pkIds = CurPrintingItem.Select(x=>x.Fid).Distinct().ToList(); List<SqlParam> sqlParams = new List<SqlParam>(); StringBuilder strSelect = new StringBuilder("SELECT PC.FID FROM Tmp_PrintUniqueControl PC "); if (pkIds.Count == 1) { strSelect.Append("WHERE FID = @FID"); sqlParams.Add(new SqlParam("@FID", KDDbType.AnsiString, pkIds[0])); } else if (pkIds.Count <= 20) { strSelect.AppendFormat("WHERE FID IN ({0})", string.Join(",", pkIds.Select(id => string.Format("'{0}'", id)))); } else { var tableName = StringUtils.GetSqlWithCardinality(pkIds.Count, "@FID", 2, true); strSelect.AppendFormat("INNER JOIN {0} tmpfid on tmpfid.FID = PC.FID", tableName); sqlParams.Add(new SqlParam("@FID", KDDbType.udt_varchartable, pkIds)); } HashSet<string> existIds = new HashSet<string>(); using (var dr = Kingdee.BOS.App.Data.DBUtils.ExecuteReader(Context, strSelect.ToString(), sqlParams)) { while(dr.Read()) { existIds.Add(dr.GetString("FID")); } } for (int i = CurPrintingItem.Count - 1; i >= 0; --i) { string fid = CurPrintingItem[i].Fid; if (existIds.Contains(fid)) continue; CurPrintingItem.RemoveAt(i); } } /// /// 是否打印操作 /// /// <param name="printType"></param> /// <returns></returns> private static bool IsPrintOp(string printType) { if (printType == null) return false; const string print = "print"; const string PrintMerge = "PrintMerge"; if (string.Equals(printType, print, StringComparison.OrdinalIgnoreCase) || string.Equals(printType, PrintMerge, StringComparison.OrdinalIgnoreCase)) return true; return false; } private bool AddControl(IEnumerable<PrintUniqueItem> printUniqueItems) { string strSql = "INSERT INTO Tmp_PrintUniqueControl VALUES(@FID, @FObjectTypeId, @FKey, @FUSERID, GETDATE())"; List<SqlObject> sqlObjects = new List<SqlObject>(); foreach(var printUniqueItem in printUniqueItems) { List<SqlParam> sqlParams = new List<SqlParam>(); sqlParams.Add(new SqlParam("@FID",KDDbType.AnsiString, printUniqueItem.Fid)); sqlParams.Add(new SqlParam("@FObjectTypeId", KDDbType.AnsiString, printUniqueItem.FormId)); sqlParams.Add(new SqlParam("@FKey", KDDbType.AnsiString, printUniqueItem.FKey)); sqlParams.Add(new SqlParam("@FUSERID", KDDbType.Int64, Context.UserId)); sqlObjects.Add(new SqlObject(strSql, sqlParams)); } try { using (var scope = new KDTransactionScope(TransactionScopeOption.RequiresNew)) { DBUtils.ExecuteBatch(Context, sqlObjects); scope.Complete(); } return true; } catch(Exception e) { return false; } } public void ClearTimeoutRecord() { //清理一天前的打印唯一控制 DateTime deleteTime = DateTime.Now.Subtract(TimeSpan.FromDays(1)); //按照主键删除,而非按照时间删除,避免锁表的逻辑 string strSql = "SELECT TOP 50 FID FROM Tmp_PrintUniqueControl WHERE FCREATEDATE <= @FCREATEDATE"; SqlParam sqlParam = new SqlParam("@FCREATEDATE", KDDbType.DateTime, deleteTime); List<string> pkIds = new List<string>(); while (true) { pkIds.Clear(); using (var dr = Kingdee.BOS.App.Data.DBUtils.ExecuteReader(Context, strSql, sqlParam)) { while (dr.Read()) { pkIds.Add(dr.GetString("FID")); } } if (pkIds.Count <= 0) break; ClearByPkIds(pkIds); } } /// <summary> /// 按照主键删除 /// </summary> /// <param name="pkIds"></param> private void ClearByPkIds(List<string> pkIds) { if (pkIds == null || pkIds.Count <= 0) return; pkIds = pkIds.Distinct().ToList(); List<SqlParam> sqlParams = new List<SqlParam>(); StringBuilder strDelete = new StringBuilder("DELETE PC FROM Tmp_PrintUniqueControl AS PC "); if (pkIds.Count == 1) { strDelete.Append("WHERE FID = @FID"); sqlParams.Add(new SqlParam("@FID", KDDbType.AnsiString, pkIds[0])); } else if (pkIds.Count <= 20) { strDelete.AppendFormat("WHERE FID IN ({0})", string.Join(",", pkIds.Select(id => string.Format("'{0}'", id)))); } else { var tableName = StringUtils.GetSqlWithCardinality(pkIds.Count, "@FID", 2, false); strDelete.AppendFormat("WHERE EXISTS ({0} WHERE b.FID = PC.FID)", tableName); sqlParams.Add(new SqlParam("@FID", KDDbType.udt_varchartable, pkIds)); } Kingdee.BOS.App.Data.DBUtils.Execute(Context, strDelete.ToString(), sqlParams); } } public class PrintUniqueControlClearSchedule : IScheduleService { public void Run(Context ctx, Schedule schedule) { new PrintUniqueHandler(ctx, null).ClearTimeoutRecord(); } } } ``` (3)增加执行计划,定期将1天前的打印网控清理 ![image.webp](/download/01009bdbcdb02197445da03926df5aa5c57f.webp)

套打.常见问题.打印次数强并发控制方案

【场景】打印次数强并发控制方案【背景知识】打印次数的计算逻辑:[套打.常见问题.打印日志和打印次数更新逻辑](https://vip.kingdee.com/l...
点击下载文档
确认删除?
回到顶部
客服QQ
  • 客服QQ点击这里给我发消息