百积木文档
开发指南平台应用、模块与 Bundle 开发

Timer 定义、版本创建与运行态

Bundle Timer 的机器定义、Manifest 引用、安装收敛和执行记录合同。

Timer 是 Bundle 拥有的版本化资源。作者先维护一个以 bundleId + timerKey 标识的草稿, 再创建不可变的精确 SemVer 版本,最后在 Bundle Manifest 中引用该版本。安装方不直接创建平台 Timer;Runtime 安装所选定义、调用表达式和自身凭据,沿用 enabled 与调度配置,不新增安装后的绑定步骤。外部目标缺失或属性未配置不阻止安装,触发时对应调用明确失败,见安装与资源绑定。

执行主体必须明确保存,首次安装默认安装人。升级、重放和属性配置保留原主体,不改成当前操作人。自身凭据只标识 Timer,不表示已经拥有目标方法权限;通过本包 Module 属性选择目标时更新 Timer 自身授权,清空属性撤权但保留自身身份,属性修改不重建任务或重置调度。

Timer 触发时直接请求 Gateway 的原属性路径。条件分支、Map key 和 List 索引只依据本次执行输入选择,属性当前绑定的服务由 Gateway 解析并鉴权。params 原样作为业务参数,路径信息不混入参数对象;换绑不会要求 Timer 解析外部模块或重置调度。未绑定、目标缺失或权限拒绝均返回明确错误,不补授权或转回 Backend。

机器可读定义

当前公开作者定义合同是 2.0.0:

timerKey 不在 Definition JSON 内;它是 Bundle 内稳定且不可修改的资源 Key,由命令参数提供。 Definition 只包含下列字段,未知字段会被拒绝:

{
  "name": "每日门店运营汇总",
  "description": "每天 09:00 生成并分发门店运营日报",
  "target": {
    "method": "generateDailyReport",
    "params": {
      "scope": "ALL_STORES"
    },
    "resourceLocator": {
      "type": "service_reference_expression",
      "contractVersion": "1.0.0",
      "expression": "references.executor",
      "references": {
        "executor": {
          "resourceLocator": "baijimu/flower-operation/MODULE/settings",
          "propertyName": "executor"
        }
      }
    }
  },
  "schedule": {
    "type": "CRON",
    "expression": "0 0 9 * * * *",
    "timezone": "Asia/Shanghai"
  },
  "enabled": true
}

字段规则:

  • name 为 1–200 个 Unicode 字符;description 可省略或为 null,字符串时为 1–2000 个字符;两者不能有首尾空白。
  • target.resourceLocator 使用显式标记的 ServiceReference 表达式;references 指向同一来源环境、同一 Bundle 的 Module 属性。target.method 是目标接口方法,params 保存调用参数。属性配置选择实际服务;定义不填写外部服务 businessId、凭据或 URL。
  • CRON 明确支持 5、6、7 段表达式;时区使用合法 IANA 名称。普通周期任务推荐 6 段,需要年份限制时使用 7 段。
段数字段顺序每 5 分钟示例省略字段
5分、时、日、月、星期*/5 * * * *秒固定为 0,年为 *
6秒、分、时、日、月、星期0 */5 * * * *年为 *
7秒、分、时、日、月、星期、年0 */5 * * * * *无

上述三个示例在同一时区产生相同触发时间。0 */5 * * * * 2027 仅在 2027 年触发;平台不能丢弃或忽略年份。 5 段的秒为 0,不是任意秒。定义、安装覆盖和实际调度使用同一字段扩展规则,内部按 7 个时间字段解析。 表达式采用 Rust cron 库的方言:星期 1 为周日、7 为周六,也可用 SUN 到 SAT;同时指定日和星期时两者必须都匹配。 不要把 Linux crontab 或 Quartz 的额外语法默认视为支持。段数之外仍校验字段取值、IANA 时区和最小触发间隔。

  • EVERY.everyMs 是毫秒整数,最小值为 300000。
  • AT.at 是 RFC 3339 date-time,只执行一次;完成后保持禁用,不会在下一次相同目标态收敛时重复执行。
  • 所有循环型 Timer 的相邻触发间隔不得小于 5 分钟。JSON Schema 能表达 EVERY 的下限;CRON 的间隔由 Definition 服务和 Runtime 再做语义校验。
  • enabled: false 表示资源仍随 Bundle 安装和升级物化,但运行态保持禁用;它不等价于从 Manifest 删除资源。

草稿和不可变版本

以下命令中的 <WORKSPACE_ID> 是工作区数字 ID,<bundle> 接受 bundleId 或精确名称:

baijimu bundle timer list --workspace-id <WORKSPACE_ID> <bundle> --json
baijimu bundle timer get --workspace-id <WORKSPACE_ID> <bundle> <timerKey> --json
baijimu bundle timer create --workspace-id <WORKSPACE_ID> <bundle> --resource-key daily-report --definition @timer.json --json
baijimu bundle timer update --workspace-id <WORKSPACE_ID> <bundle> <timerKey> --definition @timer.json --json
baijimu bundle timer version create --workspace-id <WORKSPACE_ID> <bundle> <timerKey> <semanticVersion> --json

创建时用 --resource-key daily-report 指定稳定 key;后续读取、编辑和创建版本沿用该 key。list 返回对象目录(object 与 name),get 返回当前作者定义:

{
  "object": {"type": "TIMER", "timerKey": "owner-returned-timer-id"},
  "definition": {
    "name": "每日门店运营汇总",
    "description": null,
    "target": {
      "resourceLocator": {
        "type": "service_reference_expression",
        "contractVersion": "1.0.0",
        "expression": "references.executor",
        "references": {
          "executor": {
            "resourceLocator": "baijimu/flower-operation/MODULE/settings",
            "propertyName": "executor"
          }
        }
      },
      "method": "generateDailyReport",
      "params": {}
    },
    "schedule": {
      "type": "EVERY",
      "everyMs": 86400000
    },
    "enabled": true
  }
}

创建 Timer 必须显式提供 --resource-key,后续读写使用同一 Bundle 下的 timerKey,不使用数据库行 ID。作者入口校验真实 Bundle 的归属、当前用户的管理权限及转移状态。版本创建成功后,该 timerKey + semanticVersion 的内容不可修改;继续编辑草稿不会改变已有不可变版本。

失败响应与请求关联

语义校验失败返回 HTTP 200 的 CModel 失败,保留 TIMER_RESOURCE_ERROR;失败 data 提供安全的 message、 retryable、requestId,字段校验还提供 fieldPath。请求与响应的 x-request-id 关联同一日志; 格式、认证和路由拒绝也返回结构化详情,保留各自 HTTP 状态和错误码。内部异常仅通过请求标识在平台日志定位,不泄露数据库或凭据。

{
  "contractVersion": "1.0.0",
  "errorCode": "TIMER_RESOURCE_ERROR",
  "data": {
    "message": "schedule.expression: CRON必须是5、6或7段,当前为8段",
    "retryable": false,
    "requestId": "平台返回的请求标识",
    "fieldPath": "schedule.expression"
  }
}

只有操作成功并回读目标定义或精确版本一致,才证明保存或冻结完成。失败后不能用已有旧版本证明新配置生效。

加入 Bundle Manifest

在 Bundle 项目的 Git 工作树选择已经创建不可变版本的 Timer,CLI 自动保存 Owner 返回的领域身份:

baijimu bundle manifest catalog <bundle> timer --workspace-id <WORKSPACE_ID>
baijimu bundle manifest include <bundle> timer --workspace-id <WORKSPACE_ID> \
  --file baijimu.bundle.json --object-id <timerKey> --version 1.0.0
baijimu bundle manifest validate @baijimu.bundle.json
{
  "schemaVersion": "7.0.0",
  "definition": {
    "resources": [{"resourceType": "TIMER", "resourceKey": "owner-returned-timer-id", "semanticVersion": "1.0.0"}]
  }
}

示例身份只是结构说明,以目录实际返回值为准。semanticVersion 必须是精确 SemVer。 纳入清单不复制 Definition 或运行态任务;审查并提交 Git 后再创建 Bundle 版本。

安装、升级和卸载收敛

所选 Bundle 的不可变版本清单提供目标 Timer 集合,Timer Runtime Owner 提供当前实际资源。安装、升级和卸载按新鲜的 Owner 库存与明确目标生成计划;安装账本仅记录操作与审计状态。使用完整 ResourceLocator = environmentKey/bundleId/TIMER/resourceKey 区分资源:

Bundle 操作Timer 目标态
安装交付 Manifest 中的精确 Timer 定义、表达式、自身凭据及调度;保存执行主体,不要求外部目标已绑定
升级按新旧资源集合增加、更新或移除 Timer;相同 ResourceLocator 切换到新精确版本
从新 Manifest 删除从目标集合移除,停止调度并软删除运行态任务
卸载 Bundle移除该安装实例拥有的 Timer,停止调度并软删除运行态任务
enabled: false保留资源和来源身份,但运行态不调度
整个 Runtime 删除用空目标集合执行 Runtime teardown

每次安装、升级或卸载在提交 Bundle 最终状态前先执行 Timer replace 收敛。收敛失败会使当前 Bundle Operation 失败,不会把安装记录错误地提交为已完成。运行态任务保存来源 Bundle、版本、 安装实例和 ResourceLocator;这些字段由平台生成,作者不能在 Definition 或 Manifest 中填写。

查询实际状态和执行记录

workspaceId 和 applicationRuntimeId 是两种不同身份,不能互换。先从 Runtime 服务目录响应 data.applicationRuntimeId 读取目标工作区当前 Runtime ID,再查询当前已物化状态:

baijimu runtime services list --workspace-id <WORKSPACE_ID> --json
baijimu runtime app timer status --workspace-id <WORKSPACE_ID> <APPLICATION_RUNTIME_ID> --json

成功响应的 data 包含 appId、version、versionNumber、apiGatewayUrl、 enabledTimerCount 和 timers。每个运行态 Timer 都包含 id、resourceLocator、 resourceKey、resourceVersion、enabled、调度字段,以及执行授权字段 executionAuthorizationStatus、executionSubjectUserId、executionSubjectWorkspaceId 和 executionAuthorizationRevision。BOUND 表示当前活跃 ServiceReference 的来源 Timer、目标方法、 工作区和执行用户全部匹配;MISSING 表示没有活跃绑定;MISMATCHED 表示绑定存在但身份或目标不一致。 userId、createdBy、updatedBy 是配置变更审计字段,不能用来判断触发时以谁的身份执行。 排障时应从这里复制完整 resourceLocator,不要只猜 timerKey。

Bundle 安装或升级时,Runtime 把安装操作中已经校验的 executionSubjectUserId 写入独立的 ServiceReference 授权绑定,并只把不透明 Token 交给 Timer。Timer 触发时,gateway 校验该 Token, 向目标 Module 注入可信用户和工作区;Module 启动 Workflow 时,Workflow Engine 从可信请求上下文 保存实例的 executionSubjectUserId,worker 后续调用 Module 时继续使用这个实例字段。调用方传入的 metadata.requestUserId 会在持久化前删除,不能覆盖执行用户;--metadata 只应用于触发来源等可观测业务信息。

查询执行记录:

baijimu runtime app timer executions --workspace-id <WORKSPACE_ID> <APPLICATION_RUNTIME_ID> <timerId> \
  --resource-locator <environmentKey/bundleId/TIMER/resourceKey> \
  --status success --limit 50 --offset 0 --json

resourceLocator 是推荐的精确身份。为兼容既有运行态可以省略;如果同一 Runtime 中存在多个 同名 timerId,则必须提供 resourceLocator,或在旧运行态中使用 --business-id 消歧。 status 可选值为 pending、running、success、failed、timeout、cancelled。 limit 取值为 1–1000,默认 50;offset 从 0 开始。 响应 data 包含 taskId、resourceLocator、timerId、count、limit、offset 和 executions。执行项沿用 Timer Runtime 的 snake_case 审计字段,例如 execution_id、 attempt_number、scheduled_at、started_at、completed_at、response_status_code、 duration_ms、error_type 和 error_message。

两个运行态查询都要求有效的签名用户凭证,校验当前用户是 <WORKSPACE_ID> 的有效成员,并验证 <APPLICATION_RUNTIME_ID> 确实归属该工作区。Timer 被 Manifest 删除或 Bundle 卸载后不再出现在活动配置中;已产生的执行审计记录按平台保留策略保存,但活动 Timer 查询 不承诺在资源移除后仍能用原 timerId 继续定位它。

从作者合同 1.0.0 迁移

旧 target.businessId 草稿不能直接创建新版本。先在本包 Module 声明相应 Interface 属性,把 Timer 目标改为上述表达式,再创建新的 Timer 和 Bundle 版本;安装后通过属性配置选择实际服务。不要修改旧不可变版本;历史 Schema 1.0.0 保留用于读取历史定义。作者 JSON Schema 2.0.0 与平台运行时配置独立演进。属性调用路径升级由平台完成历史运行配置迁移;这不改变作者定义的 Schema 2.0.0,也不要求重新发布未修改的 Timer 定义。

本页内容