纯代码生成基础资料/单据
场景:
1、客户第三方数据保存到中间表,需要从中间表读取数据写入到星空公有云;
2、客户有2个基础资料、12张单据的数据需要同步到星空系统;
3、客户一个月的数据量大概80万条记录(单据比较特殊,只有表头信息),其中有一大半是在月底一次性同步;
开发思路:
开发方案 | 标题 | 优点 | 缺点 |
方案一 | 后台直接写数据库 | 性能最优 | 如果单据上后续调整计算逻辑,需要修改插件代码 |
方案二(本示例方案) | 建立模型调用单据保存方法 | 单据计算逻辑可以自适应,保存逻辑调整也可以自适应 | 性能没直接写数据库快 |
方案三 | 调用API接口进行保存 | (未尝试) | (未尝试) |
性能测试结果(开启5线程):
序号 | 流程名称 | 表记录基数 | 同步记录数 | 耗时(秒) | 平均(秒) | |
1 | 基础资料(新增或更新) | 无 | 3118 | 61 | 0.0196 | |
2 | 单据(只新增) | 1600万 | 384129 | 3627 | 0.0094 | |
3 | 单据(只新增) | 几十万 | 373969 | 532 | 0.0014 | |
4 | 单据(判断原单据不存在则新增) 2021-08-03更新 | 一百多万 | 415860 | 4860 | 0.0117 |
详细插件代码示例:
1、引用(不是所有必须的,先使用后删除不需要的就行)
using Kingdee.BOS; using Kingdee.BOS.App.Data; using Kingdee.BOS.Core; using Kingdee.BOS.Core.Bill; using Kingdee.BOS.Core.DynamicForm; using Kingdee.BOS.Core.DynamicForm.PlugIn; using Kingdee.BOS.Core.DynamicForm.PlugIn.Args; using Kingdee.BOS.Core.Metadata; using Kingdee.BOS.Core.Metadata.FormElement; using Kingdee.BOS.Core.Validation; using Kingdee.BOS.Orm; using Kingdee.BOS.Orm.DataEntity; using Kingdee.BOS.ServiceHelper; using MySql.Data.MySqlClient; using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Text; using System.Threading;
2、保存一张单据的入口函数,
/// <summary> /// 根据业务对象类型 /// </summary> /// <param name="ctx">星空上下文</param> /// <param name="objectTypeId">单据类型,该参数非必要,根据客户需求增加用作区分不同单据的处理</param> /// <param name="mySqlDR">数据集,该参数非必要,本例是对接mySql,结合单据类型在后面填充字段值时使用</param> /// <param name="PkValue">单据ID值,该参数非必要,外面读取单据是否已经存在</param> /// <param name="strErrorList">收集错误信息,该参数非必要,根据开发设计用作日志记录</param> /// <param name="FillBillPropertys">做了个填充字段值的方法指针</param> public void ImportBill(Context ctx, string objectTypeId, MySqlDataReader mySqlDR, Object PkValue, ref List<string> strErrorList, Action<IBillView, string, MySqlDataReader, List<string>> FillBillPropertys) { try { // 构建一个IBillView实例,通过此实例,可以方便的填写业务对象各属性 IBillView billView = CreateBillView(ctx, objectTypeId, PkValue,ref strErrorList); if (billView == null) { strErrorList.Add("构建一个IBillView实例出现异常,请再尝试一次同步。"); return; } // 新建一个空白实例 ((IBillViewService)billView).LoadData(); // 触发插件的OnLoad事件: // 组织控制基类插件,在OnLoad事件中,对主业务组织改变是否提示选项进行初始化。 // 如果不触发OnLoad事件,会导致主业务组织赋值不成功 DynamicFormViewPlugInProxy eventProxy = billView.GetService<DynamicFormViewPlugInProxy>(); eventProxy.FireOnLoad(); // 填写业务对象实例各属性 FillBillPropertys(billView, objectTypeId, mySqlDR, strErrorList); //同步赋值有问题,就直接退出 if (strErrorList.Count > 0) { return; } // 保存业务对象实例 List<ValidationErrorInfo> errorList = new List<ValidationErrorInfo>(); SaveBill(ctx, billView, OperateOption.Create(), out errorList); foreach (ValidationErrorInfo item in errorList) { strErrorList.Add(item.Message); } } catch (Exception ex) { strErrorList.Add(ex.Message); } }
3、创建单据视图(该方法基本不用调整)
/// <summary> /// 创建一个单据视图,后续将利用此视图的各种方法,设置业务对象字段值 /// </summary> /// <remarks> /// 理论上,也可以直接修改业务对象的数据包达成修改数据的目的 /// 但是,利用单据视图更具有优势: /// 1. 视图会自动触发插件,这样逻辑更加完整; /// 2. 视图会自动利用单据元数据,填写字段默认值,不用担心字段值不符合逻辑; /// 3. 字段改动,会触发实体服务规则; /// /// 而手工修改数据包的方式,所有的字段值均需要自行填写,非常麻烦 /// </remarks> private IBillView CreateBillView(Context ctx, string objectTypeId, object PkValue, ref List<string> strErrorList) { try { // 读取业务对象的元数据 FormMetadata meta = MetaDataServiceHelper.Load(ctx, objectTypeId) as FormMetadata; //业务对象整体信息 Form form = meta.BusinessInfo.GetForm(); // 动态领域模型服务提供类,通过此类,构建MVC实例 var provider = form.GetFormServiceProvider(); // 创建用于引入数据的单据view Type type = Type.GetType("Kingdee.BOS.Web.Import.ImportBillView,Kingdee.BOS.Web"); var billView = (IDynamicFormViewService)Activator.CreateInstance(type); // 开始初始化billView: // 创建视图加载参数对象,指定各种参数,如FormId, 视图(LayoutId)等 BillOpenParameter openParam = CreateOpenParameter(ctx, meta, PkValue); if (openParam == null) { return null; } billView.Initialize(openParam, provider); return billView as IBillView; } catch (Exception ex) { strErrorList.Add(ex.Message); return null; } }
4、创建视图加载参数对象(该方法基本不用调整)
/// <summary> /// 创建视图加载参数对象,指定各种初始化视图时,需要指定的属性 /// </summary> /// <param name="meta">元数据</param> /// <returns>视图加载参数对象</returns> private BillOpenParameter CreateOpenParameter(Context ctx, FormMetadata meta, object PkValue) { try { Form form = meta.BusinessInfo.GetForm(); // 指定FormId, LayoutId BillOpenParameter openParam = new BillOpenParameter(form.Id, meta.GetLayoutInfo().Id); // 数据库上下文 openParam.Context = ctx; // 本单据模型使用的MVC框架 openParam.ServiceName = form.FormServiceName; // 随机产生一个不重复的PageId,作为视图的标识 openParam.PageId = Guid.NewGuid().ToString(); // 元数据 openParam.FormMetaData = meta; // 界面状态:新增 (修改、查看) openParam.Status = OperationStatus.ADDNEW; openParam.PkValue = null; // 单据主键 if (PkValue != null && Convert.ToInt64(PkValue) > 0) { openParam.PkValue = PkValue; openParam.Status = OperationStatus.EDIT; } // 界面创建目的:普通无特殊目的 (为工作流、为下推、为复制等) openParam.CreateFrom = CreateFrom.Default; // 基础资料分组维度:基础资料允许添加多个分组字段,每个分组字段会有一个分组维度 // 具体分组维度Id,请参阅 form.FormGroups 属性 openParam.GroupId = ""; // 基础资料分组:如果需要为新建的基础资料指定所在分组,请设置此属性 openParam.ParentId = 0; // 单据类型 openParam.DefaultBillTypeId = ""; // 业务流程 openParam.DefaultBusinessFlowId = ""; // 主业务组织改变时,不用弹出提示界面 openParam.SetCustomParameter("ShowConfirmDialogWhenChangeOrg", false); // 插件 List<AbstractDynamicFormPlugIn> plugs = form.CreateFormPlugIns(); openParam.SetCustomParameter(FormConst.PlugIns, plugs); PreOpenFormEventArgs args = new PreOpenFormEventArgs(ctx, openParam); //foreach (var plug in plugs) //{// 触发插件PreOpenForm事件,供插件确认是否允许打开界面 // plug.PreOpenForm(args); //} if (args.Cancel == true) {// 插件不允许打开界面 // 本案例不理会插件的诉求,继续.... } // 返回 return openParam; } catch (Exception) { return null; } }
5、给业务对象各属性赋值,根据不同的业务对象类型(自定义的),分别调用不同方法,方便管理
/// <summary> /// 各属性赋值,填写到IBillView当前所管理的业务对象 /// </summary> /// <param name="billView"></param> private static void FillBillPropertys(IBillView billView, string objectTypeId, MySqlDataReader mySqlDR, List<string> strErrorList) { switch (objectTypeId) { case "xxx": { FillBillPropertys_xxx(billView, objectTypeId, mySqlDR, strErrorList); } break; case "yyy": case "zzz": case "PSEA_PurAdvance_buffet": { FillBillPropertys_yyyzzz(billView, objectTypeId, mySqlDR, strErrorList); } break; default: break; } #region 赋值示例 //基础资料:填写ID或者编码 //dynamicObject = this.View.Model.GetValue("F_ZHMS_OrgId") as DynamicObject; //setValue = Convert.ToString(dynamicObject["Number"]); //dynamicFormView.SetItemValueByID("FCreateOrgId", 1, 0) //dynamicFormView.SetItemValueByNumber("FCreateOrgId", "1", 0) //辅助资料:填写编码 //dynamicObject = this.View.Model.GetValue("F_ZHMS_TaxType") as DynamicObject; //setValue = Convert.ToString(dynamicObject["FNumber"]); //dynamicFormView.SetItemValueByNumber("FTaxType", setValue, 0); //分组:填写编码 //dynamicObject = this.View.Model.GetValue("F_ZHMS_MaterialGroup") as DynamicObject; //setValue = Convert.ToString(dynamicObject["Number"]); //dynamicFormView.UpdateValue("FMaterialGroup", 0, setValue); //文本:填写内容 //setValue = Convert.ToString(this.View.Model.GetValue("FDescription", thisRowIndex)); //dynamicFormView.UpdateValue("FDescription", 0, "描述(JD-001)"); // 下拉列表:填写枚举值 //setValue = Convert.ToString(this.View.Model.GetValue("FMaterialSRC", thisRowIndex)); //dynamicFormView.UpdateValue("FMaterialSRC", 0, "B"); //A、PLM;B、ERP //取默认值即可 //数量:填写值 //setValue = Convert.ToString(this.View.Model.GetValue("F_ZHMS_ZXJJ", thisRowIndex)); //dynamicFormView.UpdateValue("F_PSEA_ZXJJ", 0, setValue); //复选框:true,false //dynamicFormView.UpdateValue("FIsKFPeriod", 0, true); // 把billView转换为IDynamicFormViewService接口: // 调用IDynamicFormViewService.UpdateValue: 会执行字段的值更新事件 // 调用 dynamicFormView.SetItemValueByNumber :不会执行值更新事件,需要继续调用: // ((IDynamicFormView)dynamicFormView).InvokeFieldUpdateService(key, rowIndex); #endregion 赋值示例 }
6、具体的业务对象属性赋值示例
/// <summary> /// xxx的字段填充 /// </summary> /// <param name="billView"></param> /// <param name="objectTypeId"></param> /// <param name="mySqlDR"></param> private static void FillBillPropertys_xxx(IBillView billView, string objectTypeId, MySqlDataReader mySqlDR, List<string> strErrorList) { string setValue = string.Empty; IDynamicFormViewService dynamicFormView = billView as IDynamicFormViewService; //公司类别 setValue = Convert.ToString(mySqlDR["companyType_id"]); dynamicFormView.SetItemValueByNumber("FCreateOrgId", setValue, 0); dynamicFormView.SetItemValueByNumber("FUseOrgId", setValue, 0); CheckBaseFieldValue(billView, "FCreateOrgId", setValue, "公司类别", "创建组织", strErrorList); CheckBaseFieldValue(billView, "FUseOrgId", setValue, "公司类别", "使用组织", strErrorList); //商户编码 setValue = Convert.ToString(mySqlDR["merchant_id"]).Trim(' '); dynamicFormView.UpdateValue("FNumber", 0, setValue); //CheckTextFieldValue(billView, "FNumber", setValue, "商户编码", "商户编码", strErrorList); //商户名称 setValue = Convert.ToString(mySqlDR["merchant_name"]); dynamicFormView.UpdateValue("FName", 0, setValue); //CheckTextFieldValue(billView, "FName", setValue, "商户名称", "商户名称", strErrorList); //商户状态 FState setValue = Convert.ToString(mySqlDR["state"]); dynamicFormView.UpdateValue("FState", 0, setValue); //禁用状态 dynamicFormView.UpdateValue("FForbidStatus", 0, "A"); //数据状态 dynamicFormView.UpdateValue("FDocumentStatus", 0, "C"); }
7、调用业务对象保存操作
/// <summary> /// 保存,并显示保存结果 /// </summary> /// <param name="billView"></param> /// <returns></returns> private void SaveBill(Context ctx, IBillView billView, OperateOption saveOption, out List<ValidationErrorInfo> errorList) { errorList = new List<ValidationErrorInfo>(); // 设置FormId Form form = billView.BillBusinessInfo.GetForm(); if (form.FormIdDynamicProperty != null) { form.FormIdDynamicProperty.SetValue(billView.Model.DataObject, form.Id); } // 调用保存操作 暂存:Draft;保存:Save; IOperationResult saveResult = BusinessDataServiceHelper.Save( ctx, billView.BillBusinessInfo, billView.Model.DataObject, saveOption, "Save"); // 显示处理结果 if (saveResult == null) { //this.View.ShowErrMessage("未知原因导致保存物料失败!"); //失败提示,另外补充 return; } else if (saveResult.IsSuccess == true) {// 保存成功,直接显示 //this.View.ShowOperateResult(saveResult.OperateResult); //成功提示,另外补充 return; } //else if (saveResult.InteractionContext != null && saveResult.InteractionContext.Option.GetInteractionFlag().Count > 0) //{// 保存失败,需要用户确认问题 // InteractionUtil.DoInteraction(this.View, saveResult, saveOption, // new Action<FormResult, IInteractionResult, OperateOption>(( // formResult, opResult, option) => // { // // 用户确认完毕,重新调用保存处理 // List<ValidationErrorInfo> tempErrorList = new List<ValidationErrorInfo>(); // this.SaveBill(billView, option, out tempErrorList); // })); //} // 保存失败,显示错误信息 if (saveResult.IsShowMessage) { saveResult.MergeValidateErrors(); errorList = saveResult.ValidationErrors; //this.View.ShowOperateResult(saveResult.OperateResult); return; } }
8、调用示例(代码不完整,需要根据需要编写)
//中间表记录唯一值 string uniqueId_InterTable = string.Empty; //获取星空存在单据ID、中间表记录唯一值 object FID_Cloud = GetAomiBillFID(targetKey, ctx, mySqlDR, cloudTable, ref uniqueId_InterTable); List<string> tempErrorList = new List<string>(); //错误信息 //导入单据 ImportBill(ctx, targetKey, mySqlDR, FID_Cloud, ref tempErrorList, FillBillPropertys); //记录导入情况 SaveBillResult(tempErrorList, ref createBillNum_success, ref modifyBillNum_success, ref createBillNum_fail, ref modifyBillNum_fail, FID_Cloud); //如果成功,则添加到反写列表 if (tempErrorList.Count <= 0) { uniqueIdList_InterTable.Add(uniqueId_InterTable); } else { foreach (string item in tempErrorList) { insertSynchroFailInfoSqlList.Add(string.Format(insertSynchroFailInfoSqlModel, continueSynchroResultTableFId, uniqueId_InterTable, item)); } }
参考资料:
1、知识分享 - 如何使用纯插件创建物料并保存,来源金蝶社区:https://wenku.my7c.com/article/24828
【emoji】
纯代码生成基础资料/单据
场景:1、客户第三方数据保存到中间表,需要从中间表读取数据写入到星空公有云;2、客户有2个基础资料、12张单据的数据需要同步到星空系统...
点击下载文档
本文2024-09-16 17:21:20发表“云星空知识”栏目。
本文链接:https://wenku.my7c.com/article/kingdee-k3cloud-15176.html
您需要登录后才可以发表评论, 登录登录 或者 注册
最新文档
热门文章