171 lines
8.8 KiB
Markdown
171 lines
8.8 KiB
Markdown
# Approval Events
|
|
|
|
> **Prerequisite:** Read [`../SKILL.md`](../SKILL.md) first for the `event consume` essentials (commands, subprocess contract, jq usage).
|
|
|
|
## Key catalog (2)
|
|
|
|
| EventKey | Purpose |
|
|
|---|---|
|
|
| `approval.instance.status_changed_v4` | An approval instance status changed |
|
|
| `approval.task.status_changed_v4` | An approval task status changed |
|
|
|
|
Both keys use a **Custom schema**. The raw Lark schema 2.0 envelope is flattened: event metadata is exposed as `type`, `event_id`, and `timestamp`, while approval business fields are exposed at the top level.
|
|
|
|
Both keys carry a **PreConsume hook** that subscribes the current authorized user through the Approval subscription APIs before listening. The consumer intentionally does **not** unsubscribe on exit; the server-side Approval subscription relation remains until it is canceled outside `event consume`. These keys require `--as user`.
|
|
|
|
## Listener and subscription selection
|
|
|
|
At the raw CLI level, each `event consume` process accepts exactly one EventKey. `approval.instance.status_changed_v4` and `approval.task.status_changed_v4` have different output shapes, so listening to both still means two processes.
|
|
|
|
For Approval only, `subscription_type` is an optional setup param used by PreConsume to register server-side Approval subscription relations before the local listener starts. It is **not** an output field, a local event filter, or a local subscription identity. The pushed event does not say which subscription relation caused delivery, and one business event can match both relations; deduplicate with `event_id` when needed.
|
|
|
|
`subscription_type` may be omitted, a single value, a comma-separated list, or a JSON string array:
|
|
|
|
```bash
|
|
# Omitted: register both INVOLVED_APPROVAL and MANAGED_APPROVAL for this EventKey
|
|
lark-cli event consume approval.instance.status_changed_v4 --as user
|
|
|
|
# Single relation
|
|
lark-cli event consume approval.instance.status_changed_v4 \
|
|
-p subscription_type=INVOLVED_APPROVAL \
|
|
--as user
|
|
|
|
# Explicit multi-relation registration for one local consumer
|
|
lark-cli event consume approval.task.status_changed_v4 \
|
|
-p subscription_type=INVOLVED_APPROVAL,MANAGED_APPROVAL \
|
|
--as user
|
|
|
|
# JSON array form; quote it for the shell
|
|
lark-cli event consume approval.task.status_changed_v4 \
|
|
-p 'subscription_type=["INVOLVED_APPROVAL","MANAGED_APPROVAL"]' \
|
|
--as user
|
|
```
|
|
|
|
| Value | Meaning |
|
|
|---|---|
|
|
| `INVOLVED_APPROVAL` | Receive events where the current user is the approval requester or approver |
|
|
| `MANAGED_APPROVAL` | Receive events under approval definitions managed by the current user |
|
|
|
|
User-intent inference:
|
|
|
|
| User intent | EventKey(s) | `subscription_type` |
|
|
|---|---|---|
|
|
| Mentions approval instances, approval forms, approval order/status, or "instance status" | `approval.instance.status_changed_v4` | infer from relation words below |
|
|
| Mentions approval tasks, approval todo items, approver operations, or "task status" | `approval.task.status_changed_v4` | infer from relation words below |
|
|
| Says "approval status changes/events" without saying task vs instance | both EventKeys | infer from relation words below |
|
|
| Says "my approvals", "approvals involving me", "I requested/approved", "待我审批", "我发起/我参与" | requested EventKey(s) | `INVOLVED_APPROVAL` |
|
|
| Says "approvals I manage", "managed definitions", "definitions managed by me", "我管理的审批定义" | requested EventKey(s) | `MANAGED_APPROVAL` |
|
|
| Explicitly asks for both involved and managed, or says "all approval subscriptions" | requested EventKey(s), or both if EventKey is also ambiguous | omit `subscription_type`, or pass both values in one `-p` |
|
|
| Relation is ambiguous and the user wants broad coverage | requested EventKey(s), or both if EventKey is also ambiguous | omit `subscription_type` so PreConsume registers both |
|
|
|
|
If the user's wording omits the relation and broad listening is acceptable, omit `subscription_type`. Ask only when registering both relations would be materially harmful.
|
|
|
|
## Scopes & auth
|
|
|
|
| EventKey | Scope | Auth |
|
|
|---|---|---|
|
|
| `approval.instance.status_changed_v4` | `approval:instance:read` | user |
|
|
| `approval.task.status_changed_v4` | `approval:task:read` | user |
|
|
|
|
## Subscription behavior
|
|
|
|
Startup calls the endpoint for the selected EventKey:
|
|
|
|
```text
|
|
POST /open-apis/approval/v4/instances/subscription
|
|
POST /open-apis/approval/v4/tasks/subscription
|
|
```
|
|
|
|
For each resolved `subscription_type`, PreConsume sends one request body:
|
|
|
|
```json
|
|
{"subscription_type":"INVOLVED_APPROVAL"}
|
|
```
|
|
|
|
If `subscription_type` is omitted, PreConsume sends two registration requests for that EventKey: one with `INVOLVED_APPROVAL`, then one with `MANAGED_APPROVAL`. If listening to both instance and task events, run two consumers; each consumer may omit `subscription_type` to register both relations for its own EventKey.
|
|
|
|
Do not start two consumers for the same Approval EventKey merely to split `INVOLVED_APPROVAL` and `MANAGED_APPROVAL`. The server push and flattened output are keyed by EventKey and cannot be distinguished by subscription relation.
|
|
|
|
Shutdown behavior:
|
|
|
|
`event consume` does not call the Approval unsubscribe APIs when it exits. This applies to graceful exit, Ctrl+C / SIGTERM, stdin EOF, `--timeout`, and `--max-events`.
|
|
|
|
To stop future delivery for a user, cancel the Approval subscription relation outside this consumer. The unsubscribe APIs are separate operations and are not called by `event consume`.
|
|
|
|
## Output fields
|
|
|
|
Common fields:
|
|
|
|
| Field | Type | Description |
|
|
|---|---|---|
|
|
| `type` | string | Event type |
|
|
| `event_id` | string | Globally unique event ID; use for deduplication |
|
|
| `timestamp` | string (timestamp_ms) | Event delivery time in milliseconds, taken from `header.create_time` |
|
|
|
|
Instance event fields:
|
|
|
|
| Field | Type | Description |
|
|
|---|---|---|
|
|
| `approval_code` | string | Approval definition code; not a subscription dimension |
|
|
| `instance_code` | string | Approval instance code |
|
|
| `external_id` | string | Third-party approval instance id, when present |
|
|
| `status` | string enum | `PENDING`, `APPROVED`, `REJECTED`, `CANCELED`, `DELETED`, `REVERTED`, `OVERTIME_CLOSE`, `OVERTIME_RECOVER` |
|
|
| `operate_time` | string (timestamp_ms) | Status change time |
|
|
| `start_user` | object | Instance starter user IDs, omitted when unavailable |
|
|
| `start_user.open_id` | string (open_id) | Instance starter open_id, when present |
|
|
| `start_user.union_id` | string (union_id) | Instance starter union_id, when present |
|
|
| `start_user.user_id` | string (user_id) | Instance starter tenant user_id, when present |
|
|
|
|
Task event fields:
|
|
|
|
| Field | Type | Description |
|
|
|---|---|---|
|
|
| `approval_code` | string | Approval definition code; not a subscription dimension |
|
|
| `instance_code` | string | Approval instance code |
|
|
| `task_id` | string | Approval task id |
|
|
| `external_id` | string | Third-party approval external id, when present |
|
|
| `task_external_id` | string | Third-party task external id, when emitted |
|
|
| `assigned_user` | object | Task assignee or operator user IDs, omitted for automatic flows without an operator |
|
|
| `assigned_user.open_id` | string (open_id) | Task assignee or operator open_id, when present |
|
|
| `assigned_user.union_id` | string (union_id) | Task assignee or operator union_id, when present |
|
|
| `assigned_user.user_id` | string (user_id) | Task assignee or operator tenant user_id, when present |
|
|
| `status` | string enum | `REVERTED`, `PENDING`, `APPROVED`, `REJECTED`, `TRANSFERRED`, `ROLLBACK`, `DONE`, `OVERTIME_CLOSE`, `OVERTIME_RECOVER` |
|
|
| `operate_time` | string (timestamp_ms) | Status change time |
|
|
|
|
## Examples
|
|
|
|
```bash
|
|
# Stream approval instance updates broadly; registers both involved and managed relations
|
|
lark-cli event consume approval.instance.status_changed_v4 \
|
|
--as user
|
|
|
|
# Stream approval instance updates only for approvals involving the current user
|
|
lark-cli event consume approval.instance.status_changed_v4 \
|
|
-p subscription_type=INVOLVED_APPROVAL \
|
|
--as user
|
|
|
|
# Stream approval task updates for definitions managed by the current user
|
|
lark-cli event consume approval.task.status_changed_v4 \
|
|
-p subscription_type=MANAGED_APPROVAL \
|
|
--as user
|
|
|
|
# Broad approval status listening:
|
|
# run both EventKeys as separate processes; omit subscription_type so each registers both relations.
|
|
lark-cli event consume approval.instance.status_changed_v4 \
|
|
--as user > approval-instance.ndjson &
|
|
lark-cli event consume approval.task.status_changed_v4 \
|
|
--as user > approval-task.ndjson &
|
|
wait
|
|
|
|
# Listen to both involved and managed task subscriptions with one local consumer.
|
|
lark-cli event consume approval.task.status_changed_v4 \
|
|
-p subscription_type=INVOLVED_APPROVAL,MANAGED_APPROVAL \
|
|
--as user > approval-task.ndjson
|
|
|
|
# Project a compact approval-task record
|
|
lark-cli event consume approval.task.status_changed_v4 \
|
|
-p subscription_type=INVOLVED_APPROVAL \
|
|
--as user \
|
|
--jq '{event_id, task_id, status, at: .operate_time}'
|
|
```
|