百积木文档
功能指南

创建模块

创建可安装模块,说明模块类型、属性、方法以及纳入 Bundle 的发布验证要求。

当现有模块无法覆盖业务能力时,可以创建自定义模块。源码项目可以独立存在,但模块定义 必须在 Bundle 内创建。完成后先冻结不可变版本、更新 Bundle Manifest、发布 Bundle, 再安装到目标 Runtime;完成真实调用验证后,才交给应用、智能体、技能、 后端服务或工作流使用。

先选择模块类型

类型适用场景运行方式发布前必须验证
普通业务模块CRM、审批、知识库、财务等由平台运行时承载的业务能力。模块定义、属性、方法和用户域随应用运行时部署。模块能安装到目标工作区,方法能在运行时被调用。
外部服务接入模块飞书、企微、钉钉、支付、电话通知等依赖独立服务的能力。模块调用外部服务;已有租户和凭据可以通过安装配置提供。外部鉴权、租户隔离、错误返回和调用链可追踪。
HTTP 代理模块已有稳定 HTTP 服务,只需要把接口包装成模块方法。模块方法把入参映射为 HTTP 请求,返回结果写回调用方。请求 URL、鉴权头、超时、错误返回和日志可追踪。
渠道模块微信、飞书、企微、钉钉、Webhook 等入口接入。模块保存渠道凭据和回调配置,运行时负责消息接入与事件分发。回调地址可访问,消息能进入正确工作区,事件链路有日志。

先定责任边界

如果模块依赖外部系统,先明确哪些状态由百积木保存,哪些状态由外部服务保存。不要把长期 token、租户密钥或外部实例 ID 写进页面文案或前端代码。

创建前准备

创建模块前,先确认这些信息:

  • 模块所属工作区和项目。
  • 模块名称、描述、适用用户和业务边界。
  • 需要保存的属性,例如 token、租户 ID、回调地址、默认用户域、外部实例 ID。
  • 要暴露的方法清单、每个方法的入参、出参、错误语义和调用权限。
  • 整个 Bundle 是否需要自动创建、迁移或回收平台外部资源。
  • 模块要纳入哪个 Bundle,以及模块版本和 Bundle 升级策略。

定义模块属性

模块属性用于保存安装后可配置或由服务端回写的值。属性定义需要写清字段名、类型、是否必填、默认值、是否敏感、是否由外部托管服务管理。

常见属性:

属性用途
serviceBaseUrl外部服务或代理服务的业务 API 地址。
tenantId外部系统租户标识。
accessToken外部系统访问令牌,应标记为敏感属性。
externalInstanceIdBundle 生命周期插件创建外部资源时返回的安装实例主键。
status外部实例或模块配置状态。

敏感属性只用于运行时调用,不应被页面、日志或普通接口明文展示。用户已有的租户编号可以 作为安装配置填写;自动创建的外部实例标识应由 Bundle 生命周期插件输出,再由 Bundle Service 校验并写入目标资源属性。

定义用户域

用户域用于说明哪些用户可以调用哪些方法。创建模块时至少定义默认用户域,例如 authenticated,再按业务需要拆分管理员、普通成员、外部客户等域。

设计用户域时,重点确认:

  • 普通用户能否新增、编辑、删除业务数据。
  • 管理员是否拥有配置、审核和导出权限。
  • 智能体或工作流调用模块方法时使用哪个用户身份。
  • 外部渠道消息进入后映射到哪个用户域。

获取当前访问用户

专用模块通常不需要自己实现登录态,也不应依赖前端把 userId、手机号或外部账号 ID 作为普通入参传入。页面、后端服务、智能体、工作流或渠道消息调用模块方法时,百积木运行时会把当前工作区、项目、访问用户、用户域和调用来源放在调用上下文中,模块方法应从上下文读取当前用户,再根据模块自己的用户映射关系调用企业原有系统。

如果企业原有系统使用独立账号体系,建议在模块属性或模块业务表中保存“百积木用户 ID 与企业系统账号”的映射,例如外部员工 ID、CRM 用户 ID、门店账号或部门编码。模块方法收到调用后,先用上下文中的当前用户确认百积木侧权限,再查映射关系,最后带企业系统需要的身份凭据访问原有系统。不要让页面直接传入要代操作的外部账号,除非该方法本身就是管理员代办能力,并且已经定义了对应用户域、审计日志和权限校验。

系统模块访问平台服务时的身份头

系统模块通过 /gateway-module/** 反向访问项目、数据库、托管服务等平台能力时,使用两种相互独立的身份:

  • workspace module token 证明“哪个已安装模块、哪个工作区正在调用”。
  • gateway actor 证明“这次调用代表哪个当前用户”。module token 本身不包含当前用户。

模块方法不应声明、生成、转发或覆盖平台身份头。X-Baijimu-*X-Gateway-Module-*X-Partner-* 都是平台保留命名空间;即使模块设置了这些请求头,gateway2 和 OpenResty 也会在信任边界清除。OpenResty 的普通公网代理入口会无条件删除这三个命名空间;必须先用于验签的 /gateway-module/**/partner/v1/** 会在验签完成后删除,再由平台重建允许向后端传递的头。

gateway2 发往 OpenResty 的内部验签请求包含:

请求头含义
Authorization: Bearer lc_mtok_...当前安装模块的 workspace module token。
X-Baijimu-Gateway-Actor-UserId从受信运行时上下文取得的当前用户 ID。
X-Baijimu-Gateway-Actor-WorkspaceId当前运行时工作区 ID。
X-Baijimu-Gateway-Actor-Timestampactor 断言生成时间。
X-Baijimu-Gateway-Actor-Nonce让每次断言保持唯一的随机值。
X-Baijimu-Gateway-Actor-Signature绑定用户、工作区、module token、HTTP 方法和请求 URI 的 HMAC 签名。

OpenResty 校验成功后会删除 module token、时间戳、nonce、签名以及调用者提供的三个平台保留命名空间,只向后端服务重建以下可信上下文:

请求头含义
X-Baijimu-Gateway-Actor-UserId已验签的当前用户 ID;用户权限判断只能使用这个值。
X-Baijimu-Gateway-Actor-WorkspaceId已验签且与 module token 一致的工作区 ID。
X-Gateway-Module-WorkspaceId已验证 module token 所属的工作区。
X-Gateway-Module-TokenId已验证 module token 的内部记录 ID。
X-Gateway-Module-ModuleIdmodule token 绑定的模块 ID,未绑定时为空。
X-Gateway-Module-ServiceIdmodule token 绑定的服务 ID,未绑定时为空。

只有需要“当前操作者”的接口才会收到两个 X-Baijimu-Gateway-Actor-* 头;只校验模块和工作区的接口仅收到四个 X-Gateway-Module-* 头。普通 Web 请求不会收到 X-Baijimu-*

CLI 等外部调用者使用 PAT 访问 /partner/v1/** 时,OpenResty 验证 PAT 后向后端提供:

请求头含义
X-Partner-UserIdPAT 所属用户 ID。
X-Partner-ScopesPAT 的已验证权限范围。
X-Partner-WorkspaceId工作区范围接口解析出的工作区 ID;非工作区接口不提供。
userId现有 PAT 后端兼容头,值由 OpenResty 覆盖;新服务应读取 X-Partner-UserId
workspaceId仅部分现有工作区接口使用的兼容头;新服务应读取 X-Partner-WorkspaceId

PAT 请求不会获得 gateway actor 身份,不能自行构造 X-Baijimu-Gateway-Actor-*X-Gateway-Module-*Authorization 在凭证校验后不会转发给业务后端。

不要把普通 userId 当成操作者身份

模块参数、请求体或普通 userId 请求头都属于业务输入,可能由调用者控制。平台后端在处理 /gateway-module/** 请求时,只能使用验签后的 X-Baijimu-Gateway-Actor-UserId 判断当前操作者,不能回落到普通 userId,也不能使用 userId=1 作为超级管理员兼容值。

定义模块方法

每个模块方法都要写清:

  • 方法名称和业务说明。
  • 入参字段、类型、是否必填、默认值。
  • 返回结构和失败返回。
  • 调用时需要的模块属性。
  • 是否允许智能体、工作流、后端服务或页面直接调用。
  • 是否需要幂等键、重试或超时控制。

模块方法应保持业务语义清晰,例如 createLeadsendMessagequeryOrder,不要暴露内部实现名。方法返回值要稳定,方便页面、智能体和工作流复用。

确定外部生命周期归属

模块自身不绑定整个产品的生命周期插件。模块只需要声明哪些属性用于调用外部服务,以及 这些属性是否敏感、必填或允许由安装输出写入。

如果用户已有外部账号,让用户在安装配置中填写租户编号和凭据即可。如果安装 Bundle 时 必须自动创建外部租户、数据库或许可证,再由 Bundle 顶层绑定生命周期插件。详细判断和 协议见 高级 Bundle 开发

生命周期插件需要针对整个 Bundle 验证安装、配置变更、升级、停止和卸载,尤其要确认 卸载不会误删仍被其他 Runtime 使用的外部资源。

纳入 Bundle

模块不作为独立市场商品发布。先在目标 Bundle 内创建模块定义,再从项目 Git 快照冻结 不可变模块版本,由 Bundle Manifest 引用精确版本:

baijimu bundle module create <workspace> <bundle> \
  --project-id <projectId> \
  --name <moduleName> \
  --description <description>

baijimu bundle module freeze <workspace> <bundle> <projectId> \
  --module-id <moduleId> \
  --version <semanticVersion> \
  --commit-id <commitId>

冻结模块版本不会自动修改 Bundle 或升级已安装 Runtime。必须更新 Manifest,并通过 bundle version publish、Bundle 审核和 bundle market publish 完成统一发布。

纳入 Bundle 前确认:

  • 模块定义、方法定义和用户域已经同步到版本快照。
  • 版本号语义清楚,破坏性变更使用新大版本或明确迁移说明。
  • 示例属性和默认值不会覆盖用户生产配置。
  • 安装到新工作区后能生成运行时可识别的服务名和业务 ID。
  • Bundle 版本的 resolvedLock 指向预期模块版本和安装 Artifact。

安装与验证

Bundle 发布并把模块纳入资源锁后,按真实使用路径验证:

  1. 把 Bundle 安装到目标 Runtime,由 Bundle 自动安装模块。
  2. 填写或确认模块属性,敏感属性只在配置页输入。
  3. 打开目标运行时,确认模块安装记录、服务名和版本正确。
  4. 调用每个核心方法,检查入参、返回、日志和错误信息。
  5. 让页面、后端服务、智能体或工作流各完成一次实际调用。
  6. 修改属性后再次调用,确认新配置已生效。

常见问题

问题排查顺序
安装后看不到模块先确认工作区和 Runtime,再确认 Bundle 资源锁、模块安装记录和当前账号权限。
方法调用失败先看模块属性是否完整,再看运行时日志、方法入参、外部系统返回和用户域权限。
外部实例未创建先确认 Bundle 是否声明生命周期插件,再检查插件注册、执行记录和外部服务健康状态。
属性被覆盖检查默认值、Bundle 允许的插件输出、模块版本和 Bundle 升级逻辑。
工作流调用不到模块确认模块已安装到工作区默认运行时,服务名和方法名与工作流节点一致。

本页内容