数据库
默认模板使用的两个 D1 数据库,以及各数据表的用途和关联关系。
默认模板使用两个独立的 Cloudflare D1 数据库。DB 保存业务数据,ANALYTICS_DB 保存第一方访问统计数据。两个数据库之间没有表关联,也不会在同一条 SQL 中跨库联表查询。
DB
| 项目 | 内容 | 说明 |
|---|---|---|
| Binding 名称 | DB | Worker 通过这个 Binding 访问业务数据库。 |
| 模板默认数据库名称 | saavo-template-db | 首次部署时,部署脚本会根据 Worker 名称重新生成。 |
| 初始化脚本 | schema/db-init.sql | 只用于初始化空数据库或手动重置数据库。 |
| 初始化标记表 | saas_user | db:migrate 通过这张表判断业务库是否已经初始化。 |
数据表
| 范围 | 数据表 | 用途 |
|---|---|---|
| 账号和认证 | saas_user、saas_session、two_factor_setup_session、email_verification_session、password_reset_session | 用户账号、登录会话、双重验证、邮箱验证和密码重置 |
| OAuth 2.0 | oauth_auth_session、oauth_client、oauth_auth_code、oauth_token | 授权请求、客户端、授权码和 Token |
| 角色和权益 | user_role_capability、user_entitlement_capability、capability_quota_event | 角色能力、权益能力和额度变化记录 |
| Reaction | event_execution、command_execution | Event 和 Command 执行记录 |
| 支付 | webhook_events、payment_customers、checkout_sessions、purchase_items、subscriptions、subscription_items、transactions、transaction_items、refunds | Webhook、Customer、Checkout、购买、订阅、交易和退款 |
| 联盟推广 | affiliate_payout_profiles、affiliate_commissions、affiliate_monthly_payouts、affiliate_monthly_payout_records | 收款资料、佣金、月度结算和付款记录 |
| 工单和内容 | ticket、newsletter_subscriber | 工单索引和邮件订阅 |
| 站内通知 | notification、user_notification | 通知内容及用户已读状态 |
实体关系
下面按业务模块分别列出主库的 ER 图。图中的关联关系来自字段含义以及 Repository 和 Service 中的实际查询,不以 SQL 是否声明 FOREIGN KEY 为准。为避免关系线过于密集,saas_user 等公共实体会重复出现在不同图中。
账号、认证和 OAuth 2.0
saas_user.invited_by_id 形成用户之间的推荐关系。登录、验证和 OAuth 2.0 相关记录分别通过 user_id、client_id、oauth_code 等字段关联用户、客户端和授权码。
角色、权益和 Reaction
角色能力、权益能力和额度变化记录都可以关联用户。能力授予记录中的 event_id 和 command_id 指向对应的 Reaction 执行记录;额度变化记录通过 Subject、来源、权益和能力等字段关联相应的权益能力记录。
支付
payment_customers 记录用户账号与支付服务商 Customer 的对应关系。Checkout、订阅和交易可以通过本地 ID 关联,也可以结合 provider、connection_id 和支付服务商提供的 ID 确认同一笔业务数据。webhook_events 保存收到的 Webhook,并由此触发支付数据同步;一条 Webhook 记录不一定只对应某张支付表中的一条记录。
联盟推广
佣金记录同时关联推广者、被推荐用户、交易和交易明细。订阅佣金通过支付服务商、支付连接和订阅 ID 查找对应的订阅;月度结算按推广者、结算月份和币种汇总佣金,无需单独保存外键字段。
工单、通知和邮件订阅
user_notification 是用户与通知之间的关联表。工单可关联提交人和处理人,其中 requester_id 小于或等于 0 时表示匿名提交,不对应 saas_user。邮件订阅只以邮箱地址保存,不与用户账号建立实体关系。
ANALYTICS_DB
| 项目 | 内容 | 说明 |
|---|---|---|
| Binding 名称 | ANALYTICS_DB | Worker 通过这个 Binding 访问访问统计数据库。 |
| 模板默认数据库名称 | saavo-template-analytics-db | 首次部署时,部署脚本会根据 Worker 名称重新生成。 |
| 初始化脚本 | schema/analytics-init.sql | 只用于初始化空数据库或手动重置数据库。 |
| 初始化标记表 | analytics_session | db:migrate 通过这张表判断统计库是否已经初始化。 |
数据表
| 数据表 | 用途 |
|---|---|
analytics_session | 访问会话的访客标识、设备信息、第一次活动时间和第一次接收时间 |
analytics_event | 页面浏览和自定义事件,以及来源、设备、地区和性能指标 |
analytics_event_property | Event 的自定义属性 |
analytics_session_trait | 会话级自定义特征 |
实体关系
analytics_event 和 analytics_session_trait 都通过 (site_id, session_id) 关联 analytics_session,analytics_event_property.event_id 关联 analytics_event.event_id。统计库中的这三组关系同时由 SQL 外键约束;其中 Event 属性和 Session Trait 会随所属记录一并删除。
字段约定
| 约定 | 说明 |
|---|---|
| 时间 | 除 SQL 明确说明外,*_at、*_start、*_end 一类字段使用 Unix 毫秒 |
| 布尔值 | D1 使用 INTEGER 保存布尔状态,通常以 1 表示是、0 表示否 |
| 金额 | *_minor 使用货币最小单位,例如人民币的分、美元的美分 |
| JSON | JSON 是 SQLite 的类型声明,实际内容仍以文本形式保存,并由应用或 CHECK 约束保证 JSON 有效 |
| 软删除 | deleted_at 通常以 0 表示未删除,非零值表示删除时间 |
| 主键 | WITHOUT ROWID 表使用声明的主键保存记录,不再额外提供隐式 rowid |
初始化数据库和更新表结构
schema/db-init.sql 和 schema/analytics-init.sql 都包含 DROP TABLE IF EXISTS,只能用于初始化空数据库或手动重置数据库,不能用于更新生产环境的表结构。
数据库迁移文件统一放在 schema/migrations。业务库文件名采用 NNNN-db-description.sql,统计库文件名采用 NNNN-analytics-description.sql。需要修改生产环境的表结构时,应新增迁移文件,再执行 npm run db:migrate:remote。
不要在生产数据库中执行初始化脚本
初始化脚本会删除已有表和数据。db:reset:local 与 db:reset:remote 也会清空数据库,只能在确定需要重建数据库时使用。