ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

金蝶云星空k3cloud插件开发入门:从环境搭建到事件触发

金蝶云星空k3cloud插件开发入门:从环境搭建到事件触发 简介本资源是一份面向K/3 Cloud初学者的BOS插件开发入门指南聚焦企业个性化业务扩展场景系统讲解插件开发环境搭建、四类核心插件动态表单、服务、校验规则、表单构建的定位与应用以及BOS架构模型与开发实操要点。文档内容覆盖VS2012开发配置、SQL Server/Oracle数据库要求、IIS与Silverlight客户端适配、编译路径设置及插件注册调试全流程并附有Hello World级代码示例与关键配置注意事项。资源为单文件Word文档.docx共1个文件大小1.9MB格式规范、图文结合便于边学边练。目前已有216人学习下载适合零基础开发者快速建立BOS插件开发认知框架掌握从环境部署到简单功能扩展的完整能力链。1. 初学者为什么一上手 k3cloud 插件开发就卡在“连不上系统”和“改了代码不生效”这不是你环境没配好而是你还没摸清 k3cloud 插件开发的底层契约它不是写个独立程序扔进去就能跑的 Web 应用而是一套深度嵌入金蝶云·星空k3cloud服务端生命周期的事件驱动式扩展机制。插件本质是 .NET Framework 4.5 编译的 DLL必须通过 k3cloud 的「插件管理」界面注册、启用并由其宿主进程K3Cloud.WebApi.exe 或 K3Cloud.ServiceHost.exe动态加载——这意味着本地调试不能靠 F5 启动编译输出不能直接复制粘贴连日志都得去服务器上的K3Cloud\Logs目录翻。我见过太多某高校实训班学员花三天配 IIS、装 SQL Server、调 CORS结果第一行Log.Info(插件已加载)死活不出现在日志里最后发现只是插件未在【系统管理】→【插件管理】里点“启用”。本文不讲抽象概念只拆解一个初学者能当天复现、当天看到日志、当天触发第一个业务事件的最小闭环从新建类库项目开始到在单据保存时弹出自定义校验提示。所有步骤基于 k3cloud V7.5 SP2 及以上版本实测适配当前主流部署形态IIS SQL Server避坑点全部来自某公司内部插件开发规范文档与三年线上事故回溯。2. 用 Visual Studio 2019 创建可被 k3cloud 识别的插件项目k3cloud 对插件项目的结构、引用、签名有硬性要求。常见错误是直接新建 ASP.NET Web 项目或 .NET Core 类库——这两者在 k3cloud 宿主进程里根本无法加载。必须严格使用 .NET Framework 类库Class Library且目标框架锁定为.NET Framework 4.5.2V7.5 默认最低支持版本高版本如 4.8 虽可运行但部分反射 API 行为有差异不建议新手越级。2.1 新建项目并配置基础属性打开 Visual Studio 2019VS2022 对 .NET Framework 4.5.2 支持不稳定生产环境仍推荐 VS2019执行文件 → 新建 → 项目 → 模板Visual C# → 类库 (.NET Framework) 项目名称K3Cloud.Plugin.Demo 目标框架.NET Framework 4.5.2提示项目名无需带“K3Cloud”前缀但建议按K3Cloud.Plugin.[业务域]命名便于后期在插件管理界面识别。不要勾选“创建解决方案目录”避免路径嵌套过深导致发布失败。创建后右键项目 → 【属性】→ 【应用程序】选项卡程序集名称K3Cloud.Plugin.Demo默认命名空间K3Cloud.Plugin.Demo程序集版本1.0.0.0后续升级需同步修改k3cloud 按版本号判断是否覆盖2.2 添加 k3cloud SDK 核心引用k3cloud 不提供 NuGet 包所有 SDK 引用必须从已部署的 k3cloud 服务器上提取。登录运行 k3cloud 的 Windows 服务器定位到以下路径以默认安装为例C:\Program Files (x86)\Kingdee\K3Cloud\Server\Bin\从中拷贝以下 4 个 DLL 到本地开发机放入项目根目录下新建的Libs文件夹文件名作用说明Kingdee.BOS.dllBOS 平台核心提供元数据、上下文、服务总线访问Kingdee.BOS.Core.dllBOS 核心逻辑含实体基类、服务契约定义Kingdee.BOS.App.Data.dll数据访问层封装含DBUtils、SqlBuilder等常用工具Kingdee.BOS.Contracts.dll服务契约接口定义如IInteractorService在 VS 中右键【引用】→ 【添加引用】→ 【浏览】→ 选择这 4 个 DLL。关键动作选中每个引用 → 右侧属性窗口 → 将【复制到输出目录】设为“不复制”。k3cloud 宿主进程会从自身 Bin 目录加载这些程序集插件 DLL 中若包含副本会导致类型加载冲突System.TypeLoadException。2.3 编写最简插件入口继承PluginBase并重写OnInitializek3cloud 要求插件必须实现Kingdee.BOS.Core.DynamicPlugin.PluginBase抽象类。新建类DemoPlugin.csusing Kingdee.BOS.Core.DynamicPlugin; using Kingdee.BOS.Core.Metadata; using Kingdee.BOS.Core.Metadata.ConvertElement; using Kingdee.BOS.Core.Metadata.EntityElement; using Kingdee.BOS.Core.Metadata.FieldElement; using Kingdee.BOS.Core.Metadata.FormElement; using Kingdee.BOS.Core.Metadata.ViewElement; using Kingdee.BOS.Core.Metadata.BusinessElement; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement......## 1. 初学者为什么一上手 k3cloud 插件开发就卡在“连不上系统”和“改了代码不生效” 这不是你环境没配好而是你还没摸清 k3cloud 插件开发的底层契约它不是写个独立程序扔进去就能跑的 Web 应用而是一套深度嵌入金蝶云·星空k3cloud服务端生命周期的**事件驱动式扩展机制**。插件本质是 .NET Framework 4.5 编译的 DLL必须通过 k3cloud 的「插件管理」界面注册、启用并由其宿主进程K3Cloud.WebApi.exe 或 K3Cloud.ServiceHost.exe动态加载——这意味着本地调试不能靠 F5 启动编译输出不能直接复制粘贴连日志都得去服务器上的 K3Cloud\Logs 目录翻。我见过太多某高校实训班学员花三天配 IIS、装 SQL Server、调 CORS结果第一行 Log.Info(插件已加载) 死活不出现在日志里最后发现只是插件未在【系统管理】→【插件管理】里点“启用”。本文不讲抽象概念只拆解一个初学者能当天复现、当天看到日志、当天触发第一个业务事件的最小闭环从新建类库项目开始到在单据保存时弹出自定义校验提示。所有步骤基于 k3cloud V7.5 SP2 及以上版本实测适配当前主流部署形态IIS SQL Server避坑点全部来自某公司内部插件开发规范文档与三年线上事故回溯。 --- ## 2. 用 Visual Studio 2019 创建可被 k3cloud 识别的插件项目 k3cloud 对插件项目的结构、引用、签名有硬性要求。常见错误是直接新建 ASP.NET Web 项目或 .NET Core 类库——这两者在 k3cloud 宿主进程里根本无法加载。必须严格使用 .NET Framework 类库Class Library且目标框架锁定为 **.NET Framework 4.5.2**V7.5 默认最低支持版本高版本如 4.8 虽可运行但部分反射 API 行为有差异不建议新手越级。 ### 2.1 新建项目并配置基础属性 打开 Visual Studio 2019VS2022 对 .NET Framework 4.5.2 支持不稳定生产环境仍推荐 VS2019执行 bash 文件 → 新建 → 项目 → 模板Visual C# → 类库 (.NET Framework) 项目名称K3Cloud.Plugin.Demo 目标框架.NET Framework 4.5.2提示项目名无需带“K3Cloud”前缀但建议按K3Cloud.Plugin.[业务域]命名便于后期在插件管理界面识别。不要勾选“创建解决方案目录”避免路径嵌套过深导致发布失败。创建后右键项目 → 【属性】→ 【应用程序】选项卡程序集名称K3Cloud.Plugin.Demo默认命名空间K3Cloud.Plugin.Demo程序集版本1.0.0.0后续升级需同步修改k3cloud 按版本号判断是否覆盖2.2 添加 k3cloud SDK 核心引用k3cloud 不提供 NuGet 包所有 SDK 引用必须从已部署的 k3cloud 服务器上提取。登录运行 k3cloud 的 Windows 服务器定位到以下路径以默认安装为例C:\Program Files (x86)\Kingdee\K3Cloud\Server\Bin\从中拷贝以下 4 个 DLL 到本地开发机放入项目根目录下新建的Libs文件夹文件名作用说明Kingdee.BOS.dllBOS 平台核心提供元数据、上下文、服务总线访问Kingdee.BOS.Core.dllBOS 核心逻辑含实体基类、服务契约定义Kingdee.BOS.App.Data.dll数据访问层封装含DBUtils、SqlBuilder等常用工具Kingdee.BOS.Contracts.dll服务契约接口定义如IInteractorService在 VS 中右键【引用】→ 【添加引用】→ 【浏览】→ 选择这 4 个 DLL。关键动作选中每个引用 → 右侧属性窗口 → 将【复制到输出目录】设为“不复制”。k3cloud 宿主进程会从自身 Bin 目录加载这些程序集插件 DLL 中若包含副本会导致类型加载冲突System.TypeLoadException。2.3 编写最简插件入口继承PluginBase并重写OnInitializek3cloud 要求插件必须实现Kingdee.BOS.Core.DynamicPlugin.PluginBase抽象类。新建类DemoPlugin.csusing Kingdee.BOS.Core.DynamicPlugin; using Kingdee.BOS.Core.Metadata; using Kingdee.BOS.Core.Metadata.ConvertElement; using Kingdee.BOS.Core.Metadata.EntityElement; using Kingdee.BOS.Core.Metadata.FieldElement; using Kingdee.BOS.Core.Metadata.FormElement; using Kingdee.BOS.Core.Metadata.ViewElement; using Kingdee.BOS.Core.Metadata.BusinessElement; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement...... // ← 实际开发中请用 VS 的“智能提示”自动补全此处为示意省略 using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; namespace K3Cloud.Plugin.Demo { public class DemoPlugin : PluginBase { public override void OnInitialize(EventArgs e) { base.OnInitialize(e); // 插件初始化时执行仅在宿主进程启动时调用一次 Log.Info(DemoPlugin 已加载版本 1.0.0.0); } } }逻辑说明OnInitialize是插件生命周期第一个钩子k3cloud 启动时扫描所有已启用插件并调用此方法。Log.Info会输出到K3Cloud\Logs\BosLog\下的日期文件中如BosLog_20240520.log这是验证插件是否被识别的最直接证据。参数说明EventArgs e暂无实际用途但必须保留签名否则编译失败。2.4 设置强名称签名必需否则注册失败k3cloud 要求所有插件 DLL 必须带强名称Strong Name。右键项目 → 【属性】→ 【签名】选项卡勾选【为程序集签名】选择【新建】→ 输入密钥文件名K3Cloud.Plugin.Demo.snk→ 取消勾选【使用密码保护密钥文件】生产环境可加但新手易因密码输错导致编译失败生成后项目根目录下会出现.snk文件。此步不可跳过未签名的 DLL 在【插件管理】界面上传时会报错 “程序集未签名无法注册”。3. 在 k3cloud 管理端完成插件注册、启用与日志验证本地编译只是第一步。k3cloud 插件必须经由其 Web 管理界面完成注册才能被宿主进程加载。这个过程涉及权限控制、版本校验和热加载机制不能通过文件复制绕过。3.1 编译输出并定位 DLL 文件在 VS 中将解决方案配置设为Release目标平台为Any CPUk3cloud 宿主进程是 32 位或 64 位取决于 IIS 应用池设置Any CPU 可兼容。点击【生成】→ 【重新生成解决方案】。成功后DLL 位于K3Cloud.Plugin.Demo\bin\Release\K3Cloud.Plugin.Demo.dll注意该 DLL 必须是Release 版本。Debug 版本因包含调试符号和优化禁用在 k3cloud 宿主进程中可能触发 JIT 编译异常或性能告警。3.2 登录 k3cloud 管理端上传插件使用拥有【系统管理】权限的账号如 admin登录 k3cloud Web 端http://[服务器IP]/K3Cloud导航至【系统管理】→ 【插件管理】→ 【插件注册】点击【新增】按钮【插件名称】输入演示插件 - 基础版【插件编码】输入DEMO_BASE全大写下划线唯一标识后续代码中会用到【版本号】1.0.0.0必须与项目属性中设置的程序集版本一致【描述】初学者插件开发验证插件【文件】点击【选择文件】上传K3Cloud.Plugin.Demo.dll【启用】✅ 勾选关键不勾选则不会加载点击【保存】。若出现红色错误提示常见原因见后文「避坑」章节。3.3 重启服务并验证日志输出上传成功后必须重启 k3cloud 服务才能加载新插件k3cloud 不支持纯热加载V7.5 SP2 后仍需重启登录 k3cloud 服务器打开【服务】services.msc找到并重启以下两个服务顺序无关Kingdee K3Cloud Service HostKingdee K3Cloud Web API等待 30 秒打开日志目录C:\Program Files (x86)\Kingdee\K3Cloud\Server\Logs\BosLog\找到最新生成的BosLog_YYYYMMDD.log文件用记事本或 VS Code 打开搜索关键词DemoPlugin。若看到如下内容证明插件已成功加载2024-05-20 14:22:38,765 [INFO] DemoPlugin 已加载版本 1.0.0.0提示日志时间是服务器本地时间非浏览器时间若搜不到先确认服务是否真正重启任务管理器看进程是否存在、DLL 是否为 Release 版本、签名是否生效用sn -vf K3Cloud.Plugin.Demo.dll命令验证。4. 避坑初学者插件开发的 4 个高频翻车点与血泪解法插件开发不是写完代码就结束k3cloud 的运行时约束极强。以下问题均来自某公司内部故障库 Top 5每一条都对应真实线上事故。4.1 现象插件注册时提示 “程序集未签名无法注册”原因项目未启用强名称签名或签名后未重新编译。.snk文件存在但项目属性中未勾选【为程序集签名】。解决右键项目 → 【属性】→ 【签名】→ 确认勾选且密钥文件路径正确 → 清理解决方案Build → Clean Solution→ 重新生成。用命令行验证sn -vf bin\Release\K3Cloud.Plugin.Demo.dll返回Public key token is ...即成功。4.2 现象日志中完全搜不到DemoPlugin但插件管理里显示“已启用”原因插件 DLL 编译目标框架高于 k3cloud 支持版本如用了 .NET 4.7.2或引用了 k3cloud Bin 目录外的第三方 DLL如 Newtonsoft.Json导致宿主进程加载失败并静默跳过。解决确认目标框架为 .NET Framework 4.5.2检查引用属性中【复制到输出目录】均为“不复制”删除所有非 k3cloud SDK 的引用插件内禁止使用HttpClient、RestSharp等网络库需用BOS提供的WebApiService。4.3 现象修改代码后重新生成、上传、重启服务日志仍是旧内容原因插件管理界面中【启用】状态未重新勾选或上传时未更换版本号k3cloud 按版本号判断是否覆盖同版本号不会替换文件。解决每次修改后必须在【插件管理】中① 取消勾选【启用】→ 保存 → ② 再次编辑 → 修改【版本号】如1.0.0.1→ 重新上传 DLL → 勾选【启用】→ 保存 → 重启服务。4.4 现象插件能加载但自定义事件如单据保存完全不触发原因未在插件类上添加[Kingdee.BOS.Core.DynamicPlugin.DynamicPluginAttribute]特性或特性中FormId值拼写错误大小写敏感、多空格、少下划线。解决在DemoPlugin类声明上方添加特性示例见下一章FormId必须与 k3cloud 中实际表单 ID 完全一致可通过【系统管理】→ 【表单管理】中查看。5. 让插件真正干活监听采购申请单PUR_PurchaseApply保存事件并弹窗校验光有OnInitialize只能证明插件“活着”要体现业务价值必须绑定具体业务事件。k3cloud 提供AbstractOperationServicePlugIn和AbstractBillPlugIn两类扩展基类初学者推荐从AbstractBillPlugIn入手——它专为单据Bill设计封装了DataChanged、BeforeSave、AfterSave等标准钩子语义清晰不易出错。5.1 继承AbstractBillPlugIn并指定监听表单新建类PurchaseApplyValidator.csusing Kingdee.BOS.Core.DynamicPlugin; using Kingdee.BOS.Core.Metadata; using Kingdee.BOS.Core.Metadata.ConvertElement; using Kingdee.BOS.Core.Metadata.EntityElement; using Kingdee.BOS.Core.Metadata.FieldElement; using Kingdee.BOS.Core.Metadata.FormElement; using Kingdee.BOS.Core.Metadata.ViewElement; using Kingdee.BOS.Core.Metadata.BusinessElement; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; using Kingdee.BOS.Core............ // 同上用 VS 智能提示补全 using Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; namespace K3Cloud.Plugin.Demo { [Kingdee.BOS.Core.DynamicPlugin.DynamicPluginAttribute(PUR_PurchaseApply)] public class PurchaseApplyValidator : AbstractBillPlugIn { public override void BeforeSave(Kingdee.BOS.Core.DynamicPlugin.Args.SaveArgs e) { base.BeforeSave(e); // 获取当前单据主表数据采购申请单头 var head this.Model.DataObject as Kingdee.BOS.Core.Metadata.BusinessElement.BusinessObject.BusinessObject; if (head null) return; // 获取申请日期字段值字段名需与表单实际一致 var applyDate head[FApplyDate] as DateTime?; if (!applyDate.HasValue) throw new Exception(申请日期不能为空请填写后保存); // 获取申请人FApplicantId并校验是否为当前用户 var applicantId head[FApplicantId] as long?; var currentUserId this.Context.UserId; if (applicantId ! currentUserId) { throw new Exception(申请人必须为当前登录用户请检查单据信息); } } } }逻辑说明[DynamicPluginAttribute(PUR_PurchaseApply)]告诉 k3cloud 此插件只对采购申请单表单 IDPUR_PurchaseApply生效BeforeSave在单据点击【保存】按钮、数据写入数据库前触发适合做强校验。throw new Exception(...)会中断保存流程并在 Web 端弹出红色提示框这是 k3cloud 内置的错误处理机制。参数说明SaveArgs e包含保存上下文但初学者无需深究重点是this.Model.DataObject它代表当前正在操作的单据数据对象所有字段通过字符串键如FApplyDate访问。5.2 表单字段名怎么查别猜用官方方式确认k3cloud 所有字段名Field ID均以F开头但具体拼写必须与系统一致。错误示例把FApplyDate写成FApplydate或F_ApplyDate会导致取值为null。正确查法三步登录 k3cloud → 【系统管理】→ 【表单管理】搜索“采购申请单”点击右侧【设计】按钮在表单设计器中选中任意字段如“申请日期”→ 右侧属性面板 → 查看【标识】属性值即 Field ID提示某公司内部规范要求所有自定义字段也必须以F开头因此FApplyDate是标准命名。若查不到该字段说明你打开的是旧版表单或权限不足。5.3 重新编译、上传、启用实测校验效果按第 3 章流程重新生成项目确保PurchaseApplyValidator.cs已加入编译上传新 DLL 到【插件管理】版本号改为1.0.0.1启用插件并重启服务登录 Web 端 → 新建一张采购申请单 → 故意清空【申请日期】→ 点击【保存】预期结果页面不跳转顶部弹出红色提示框“申请日期不能为空请填写后保存”验证技巧若未弹窗先检查日志中是否有BeforeSave相关异常堆栈再确认DynamicPluginAttribute中的表单 ID 是否拼写正确最后确认该单据是否处于【编辑】状态只读单据不会触发BeforeSave。6. 进阶技巧用Log记录结构化调试信息快速定位业务逻辑问题Log.Info是插件开发的“后悔药”。很多初学者只在OnInitialize里打一行日志等业务逻辑出错时才发现无从下手。真正的调试能力在于让每条日志自带上下文、可过滤、能回溯。6.1 日志分级与结构化输出k3cloudLog类支持Debug/Info/Warn/Error四级。初学者应养成习惯Log.Debug仅开发期使用记录变量值、分支路径上线前应关闭或降级Log.Info记录关键节点如“单据保存开始”、“校验通过”Log.Warn记录非致命异常如“申请人ID为空跳过校验”Log.Error记录catch中的异常必须带ex.ToString()public override void BeforeSave(SaveArgs e) { try { Log.Info($【PUR_PurchaseApply】BeforeSave 开始单据ID: {this.Model.DataObject[FID]}); var head this.Model.DataObject as DynamicObject; if (head null) { Log.Warn(【PUR_PurchaseApply】DataObject 为空跳过校验); return; } var applyDate head[FApplyDate] as DateTime?; Log.Debug($【PUR_PurchaseApply】申请日期值: {applyDate?.ToString(yyyy-MM-dd) ?? null}); if (!applyDate.HasValue) { Log.Error(【PUR_PurchaseApply】申请日期校验失败抛出异常); throw new Exception(申请日期不能为空请填写后保存); } } catch (Exception ex) { Log.Error($【PUR_PurchaseApply】BeforeSave 异常: {ex.ToString()}); throw; // 重新抛出保证前端可见 } }关键点每条日志开头加【表单ID】标签便于用文本工具如 VS Code 的搜索批量过滤Debug日志用ToString(yyyy-MM-dd)格式化时间避免null报错Error日志必须包含完整ex.ToString()否则堆栈丢失。6.2 日志文件管理与快速检索k3cloud 默认按天生成日志但BosLog_YYYYMMDD.log文件可能达百 MB。某公司内部实践推荐场景推荐做法日常调试用 PowerShell 实时监控Get-Content C:\Program Files (x86)\Kingdee\K3Cloud\Server\Logs\BosLog\BosLog_$(Get-Date -Format yyyyMMdd).log -Wait -Tail 10问题回溯将日志复制到本地用 VS Code 打开 →CtrlShiftH全局搜索【PUR_PurchaseApply】生产环境禁用Log.Debug将Log.Info级别日志单独输出到PluginDemo.log需自定义Log4Net配置超出本文范围6.3 一个血泪教训永远不要在BeforeSave里写数据库 INSERT/UPDATE曾有某开发者为记录校验日志在BeforeSave中直接调用DBUtils.Execute插入一条日志表。结果导致事务嵌套失败——k3cloud 的单据保存本身就是一个大事务插件内再启事务会破坏一致性轻则报错重则锁表。正确做法用Log记录如需持久化改用 k3cloud 提供的BusinessDataService提交异步任务或在AfterSave中通过this.Context.Session获取已提交的单据 ID 后再操作。我带过的某高校实训班学员90% 的第一版插件都在BeforeSave里手写 SQL然后卡在事务死锁上三天。后来我们定下铁律插件内禁止任何INSERT/UPDATE/DELETE所有数据操作走 BOS 服务。这看似限制实则是帮你避开金蝶底层最黑的黑匣子。希望帮到你。本文还有配套的精品资源点击获取
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表