---
url: /docs/agentbuff-stack/server-activity-events.md
---
# 工作台使用与购买事件

这是 I11 的服务端观测模块，默认关闭。它依据已经提交的任务、文件读取和积分包结算与个人 / 团队订阅账单付款事实记录事件，和[公开工具的浏览器报告](./tool-usage-events)、Cloudflare 访问统计、营销邮件同意各自独立。目前没有统计后台，也不提供浏览器自报购买金额的接口。

## 配置与升级

在 `packages/core/website.ts` 的 `analytics` 中加入：

```ts
serverEvents: {
  enabled: false,
  consentVersion: "2026-10-10.2",
  retentionDays: 30,
},
```

启用当前版本前，把目标数据库按完整顺序升级至 `0025_team_payment_activity.sql`，然后一起重建 API 与工作台。0018 新增个人选择表 `activity_consent` 和事件表 `server_activity`，0019 新增个人账单恢复检查时间；0025 新增 `team_activity_consent`，事件必须归属个人或团队之一，团队只能记录订阅付款。旧个人事件保持原数据与全局唯一键，已有站点继续追加迁移，不重写历史。旧生成站还需补齐配置字段；本任务主预览仍为 0012，此功能关闭，不读取这些可选新表。独立测试数据库的迁移验收不代表主库已升级。

不需要新供应商密钥。`consentVersion` 为 1—80 字符，`retentionDays` 为 1—90 天；默认 30 天。修改同意用途或数据范围时更新版本，旧版本不再授权新事件。个人订阅账单用途使用版本 `2026-10-10.2`，更早的个人同意需重新明确选择。本轮团队选择单独从未授权状态开始，个人用途保持该版本，不能把已有个人同意升级成团队同意。`provider` 和 `toolEvents.enabled` 不会自动开启此模块。

## 用户选择

启用后，工作台 Settings 出现 Activity reporting 卡片。初始不勾选，只有点击 Save choice 并获得服务端确认才生效；取消勾选后也要保存才能撤回。Last confirmed choice 展示最后得到确认的选择，勾选框自身不是已保存状态。失败时显示未确认，并可 Reload choice 读取实际状态，不自动重试写入。

选择属于当前个人账号，不接受客户端传入所有者。服务端重新检查账号状态、同源请求和操作限额。每次变更带当前 `revision`，旧标签页不能覆盖较新的撤回；返回冲突时先重新加载。相同选择与版本重复保存不延长同意起点。表中保留最新选择、版本、同意 / 撤回时间和版本号，不是完整同意历史档案。

公开工具页面的选择、注册账号或加入邮件名单都不授予此权限。撤回后停止新记录；已经收到的记录按保留规则清理。删除账号会级联删除这两张表中该账号的数据，不改变账本或管理审计各自的保留规则。

## 事件口径

| 事件 | 真实触发点 | 去重与限制 |
| --- | --- | --- |
| `task_started` | 原任务首次领取执行租约，第一条尝试已提交 | 每个任务一次；排队 / 提交不算开始，后续重试不增加开始次数 |
| `task_succeeded` | 当前租约的结果实际发布成功，任务状态已提交 | 每个任务一次；失败、取消或过期租约不能造成功事件 |
| `file_download_initiated` | 私有文件实际读取成功，并进入附件响应路径 | 每个文件在保留记录期间一次；普通预览不计，外部 API 的附件响应计入；不证明完整传输或用户已保存 |
| `purchase_confirmed` | 积分包结算已验证、购买的 `grantedAt` 存在且匹配原积分发放账本 | 每个购买一次；未付款的 Checkout、详情页浏览和客户端金额不构成购买 |
| `subscription_payment_confirmed` | 个人或团队订阅的当前已付账单、完整已付分配记录和实际成功 PaymentIntent / 已捕获 Charge 经服务端核查 | 每张账单一次；同一订阅的续费账单各自计数；零元试用、客户抵扣和线下 PaymentRecord 不计 |

事件唯一键是资源 ID 加事件类型。已签名回调、后台恢复与拥有者的购买详情读取共用同一记录；重复回调或确认页刷新不会重复增加购买与积分。这里没有另建前端购买上报通道。

购买保存原购买的最小货币单位金额、币种及 `livemode`。测试付款和真实付款必须分开，币种不能直接相加。这是原始购买确认，后续退款或争议不会把它改写成净收入；利润、留存、净收入归因尚未实现。订阅付款详见下一节；订阅激活、试用或打开结账页不能当作已付款。

## 订阅账单付款

接收已有 `/api/auth/stripe/webhook` 的 `invoice.paid`，使用现有原始正文 SDK 验签和测试 / 真实模式门禁。默认关闭时不读新增表、不向供应商查询。开启时要求账单的订阅 ID、客户 ID 与已保存的订阅、当前个人 / 团队客户及该主体的明确同意全部匹配；个人和团队身份同时匹配时保守跳过，不推断归属。团队账单不会归到管理员或成员个人账号。

签名事件只提供核查线索。服务端重新读取账单，要求当前 paid、正数付款额、余额为零、正确币种 / 模式和有效付款时间；再分页读取该账单完整的已付分配记录，核查对应 PaymentIntent 的 succeeded、客户 / 模式 / 币种及实际收款，或旧式 Charge 的 paid / captured 与金额。同一收款来源的分配合计不能超过其实际收款，分配总额还必须等于账单 `amount_paid`。不是直接存事件载荷里的金额。

零元试用 / 全额折扣、客户余额抵扣和线下标记付款不记为新收款。存在 PaymentRecord 等未支持来源或分配不完整时保守跳过，不用部分金额冒充完整付款；超过每张账单 100 个付款分配、重复页或分页不完整时记录通用故障。本模块不会修改订阅状态、权限、额度或积分。参考 [Stripe 账单](https://docs.stripe.com/api/invoices/object)、[付款分配](https://docs.stripe.com/api/invoice-payment/object)。契约核对日期为 2026-10-10；本地 SDK 固定 API 版本为 `2026-08-26.dahlia`，Stripe Endpoint 应采用对应账单对象结构。

唯一键使用供应商账单 ID 加固定事件类型。回调与后台恢复共用该键；个人和团队工作台的确认 / 状态页面只读取原订阅状态，不发购买上报或扫描供应商历史，刷新不会增加事件。续费的另一张账单可新增另一条事件。金额是所核实账单的原始收款确认，不扣后续退款 / 争议，也不是 MRR 或净收入。测试与真实、不同币种须分开。

有效签名账单在可选统计故障时仍应答成功，不让观测故障干扰原账单流程；后台扫描供应商的当前已付账单恢复，普通工作台读取无需等待这项扫描。每个个人或团队主体两次尝试至少相隔 15 分钟，每轮后台合计最多三个主体，按上次尝试时间轮换，失败也会让出下一轮位置。每个主体最多查 1,000 张已付账单，未完整分页不假报恢复完成；已写入的部分真实事件保留并按唯一键去重。创建时间很早但最近付款的账单仍按实际 `paid_at` 与同意起点筛选，不用 created 过滤误删迟付。

恢复扫描按最新同意版本原子领取统计自己的检查时间，不占用账单操作租约；供应商历史查询暂停时，用户仍可管理账单。检查时间在开始尝试时写入，失败同样等待下一轮，旧扫描没有完成后回写时间的路径。处理过程中撤回或变更版本，最终写入再次检查当前选择；重新同意会重置待检查时间。限额、供应商延迟和保守跳过会漏报，不保证即时或完整统计。参考 [账单列表接口](https://docs.stripe.com/api/invoices/list)。

## 团队的独立选择

切换到团队后，Settings 还显示 Team payment reporting 卡片。初始不勾选，个人账号是否同意不改变这里的选择。当前 owner / admin 可以明确保存或撤回；普通成员只能查看已确认状态，已移除成员不能读取或修改。团队选择只授权该团队的新订阅收款，成员的任务、文件、积分包仍属于各自个人选择。

请求带页面看到的团队 ID、当前同意版本和 revision。服务端核对所选团队与实际会话当前团队一致，重新检查仍有效的会话、未封禁个人账号和最新成员角色；成员写操作与选择保存共用团队锁。保存等待期间被降级或移除时拒绝，旧页面不能覆盖新撤回。切换团队后丢弃未保存的勾选，查询缓存按账号和团队分别隔离。这个隐私选择没有额外要求 24 小时新鲜会话。

团队同意和最终付款插入受事务保护，供应商查询期间撤回会阻止新记录；重新同意只接纳新起点后的付款，不能补齐旧同意区间的遗漏。团队付款与个人付款共用收款核查、事件表和账单 ID / 类型唯一键，即使账单以后改绑也不会再写一条。重复回调、详情页刷新和续费恢复不发积分、不修改订阅权限。

团队选择保存最后决策者的本站账号 ID、版本、revision、同意 / 撤回时间和恢复检查时间，不是完整同意历史。该决策者离队或删除个人账号不撤销团队选择，也不删除团队付款；决策者 ID 作为历史标识保留，不存姓名或邮箱。删除团队会级联删除团队选择与团队事件，原个人事件仍按个人规则保存。

## 保存哪些数据

记录包含本站账号 ID 或团队 ID（恰好一个）、资源 ID（任务 / 文件 / 积分包使用内部 ID，订阅付款使用供应商账单 ID）、固定事件类型、同意版本、发生 / 入库时间，任务另含固定处理器 ID，购买另含上述金额、币种和测试状态。它是可关联账号或团队的第一方内部记录，不能称为匿名统计，也没有发送到外部分析供应商。

不记录输入、结果、文件名、邮箱、页面 URL、任意参数或支付原始回调。统计失败只记录通用错误名称。删除文件或任务不会删除其历史观测；观测仍受保留期限约束。上线前须把实际数据范围、站主、保留与联系方式写入自己的隐私说明；模板隐私页只是草稿。

## 故障恢复与保留

记录在业务提交后尝试。观测表不可用不会撤销已经完成的任务、积分结算或文件内容响应，但可能漏报，也会增加少量本地查询时间。

定时入口在既有文件 / 任务维护之后补查缺失的任务开始、成功和积分包确认，每类每轮最多 100 个候选。只补当前有效同意起点之后、保留期内、仍然存在的业务事实；每次插入再次检查账号和最新选择。没有用户访问也可恢复，但不保证完整或实时统计。

撤回、改变版本或重新同意发生在恢复之前时，旧同意区间内未写入的事实不会补齐。重新同意不追溯旧行为。下载没有独立持久化基础事实，失败不能后台补造。

清理每次最多删除 500 条超过保留天数的事件，按发生时间判断；维护失败或积压会延迟实际删除。模块关闭也会停止自动清理，需要站主单独安排已有数据清理。个人同意记录保留至账号删除，团队同意记录保留至团队删除。旧任务 / 购买事实超过期限不会被恢复程序重新插入；文件原记录过期后，新的实际下载请求可以产生新记录，因此该指标不能称为终身唯一下载文件数。

## 本地验收

```bash
bun --env-file .env --env-file .env.local apps/api/local/validate-server-activity.ts
```

验证器只接受本地 PostgreSQL，建立并迁移自己的一次性数据库，运行真实生产入口的 Worker、Queue 与 R2。Stripe HTTP 使用受控响应，回调由已安装 SDK 生成签名；邮件捕获且其他外部请求拒绝。退出时销毁自己创建的资源，不升级主数据库。

加 `--ui` 可在独立 `activity.localhost:21312` 预览卡片。完成选择 / 保存 / 撤回后，用验证器提示的本地结束标记收尾，它会检查最终选择和事件数量后清理。测试账号和口令只用于一次性环境，不能作为部署配置。

已有证据包括 8 项真实迁移数据库行为测试、个人选择交互，以及支付测试中的已签名结算恢复 / 确认页去重。原生验收涵盖实际执行、八并发附件下载、观测故障后任务成功与定时补齐、签名购买回调和撤回。11 项个人订阅账单行为测试和 1 项普通账单读取边界测试继续通过；`--invoices` 同时验收签名账单、八并发去重、分页收款分配和续费恢复。

本轮新增团队选择的 6 项真实数据库 / RPC 与增量迁移测试、5 项账单归属与撤回测试，工作台选择交互扩展到 7 项（含西班牙语失败确认）。`--teams` 包含已有 `--invoices`，原生验收在真实 PostgreSQL 锁等待期间降级角色，随后八并发签名回调只生成一条团队记录；统计表故障不阻断账单，实际定时入口补回续费。`--teams --ui` 验证个人和团队分别保存 / 撤回，最终均为不分享、revision 4；390 像素浅深主题与 320 像素浅主题无横向溢出。真实 Stripe / Resend 与完整 I11 外部验收继续保留在[开发计划](./development-plan)中。
