Files
hoelee 80eb2b71be docs: architecture, permissions, admin guide + refresh schema/workflows
- New ARCHITECTURE.md (3-layer model + data flow + lifecycle matrix)
- New PERMISSIONS.md (grant calculation rules + enforcement)
- New ADMIN_GUIDE.md (華文极简:Customer/CustomerProduct 授权 + Product/Resource 管理)
- Rewrite N8N_WORKFLOWS.md from WF1-6 (obsolete) to actual W1/W2/W3
- Update DATABASE_SCHEMA.md: fix status enum (drop 'disabled'), add 5-status semantics
- Update README.md doc index
2026-08-23 07:30:41 +08:00

73 lines
3.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 架构说明(Architecture)
> 本文解释系统的分层职责与数据流。更新日期 2026-08-23。
## 三层职责(核心心智模型)
| 层 | 载体 | 职责 | 谁改它 |
|---|---|---|---|
| **Account lifecycle** | `Customers.status` | 账户整体状态开关(active / pending / suspended / expired / deleted)| 管理员手工 |
| **Product entitlement** | `CustomerProducts` + `Products` + `Resources` | 「这个客户买了哪些产品 → 能访问哪些路径」+ 每个产品的到期时间 | 管理员手工 |
| **Effective permission** | AList Role `permission_scopes` | 最终生效的路径授权(由 n8n 从上面两层计算出来,写到 AList)| **仅 n8n**,管理员不直接碰 |
```
Customers.status (account-level:整户开关)
+
CustomerProducts (product entitlement:买了什么、何时到期)
+
Product → Resources (产品包含哪些路径)
+
CustomerProduct.expires_at
↓
calculateEffectiveGrants (n8n 共享算法,单一事实源)
↓
AList Role.permission_scopes (effective/materialized permission)
```
## 关键原则
1. **`Customers.status` 不进入权限计算**。它只决定「整户开关」:
- `active` → 允许计算并应用 grants
- `pending` / `suspended` / `expired` / `deleted` → AList user disabled + scopes 清空(账户级封禁)
- 权限的「细粒度」永远来自 CustomerProducts 的到期时间,不由 status 替代。
2. **AccessGrants 是预留的物化表,当前不经 n8n 写入**。将来(W4)可用于 reconciliation 日志/物化审计,但现在不作为 source-of-truth,也不参与计算。
3. **n8n 是同步器,不是 SOT**。Source of Truth = NocoDB;AList 只做 enforcement。
## 数据流
```
管理员在 NocoDB 改 Customers.status 或 CustomerProducts
│ (NocoDB webhook:after insert / after update)
▼
n8n W1 (Customer webhook) ── 处理整户 status lifecycle
n8n W2 (CustomerProduct webhook) ── 重算该客有效 grants 并应用
n8n W3 (每 5 分钟 schedule) ── 全量对账:重算所有客户,修 drift
│
▼
AList Admin API(role/update 写 permission_scopes;user/update 写 disabled)
```
- **W1** 单一 Code 节点:provision + status lifecycle(5 状态)+ 幂等 write-back。
- **W2** 单一 Code 节点:CustomerProduct 变更 → 重算该客 grants → diff → 应用。
- **W3** 单一 Code 节点:全量 sweep,逐客调共享算法,diff 后应用;支持 dry-run / auto-disable 开关。
三个 workflow 共用同一个 `calculateEffectiveGrants`(`n8n/shared/effective-grants.js`,构建时内联),保证三处算法零复制、零漂移。
## Status lifecycle(W1)
| status | AList user | scopes | 说明 |
|---|---|---|---|
| `pending`(无 user)| —(不建)| — | 纯标签,customer 未正式开通 |
| `pending`/`suspended`/`expired`/`deleted`(有 user)| disabled | 清空 | 账户级封禁,soft,保留历史 |
| `active`(无 user)| 新建 + enabled | 计算应用 | 首次开通 |
| `active`(有 user)| re-enable | 重算 + 应用 | 恢复/续期,永不恢复旧值 |
| 任意 `X → active` | enable | 重算(仅当前有效)| resume,忽略已过期产品 |
## Loop 防护
- n8n 只写回 bookkeeping 字段(`alist_user_id`/`alist_role_id`/`sync_status`/`sync_required`/`last_sync`/`last_sync_error`)。
- W1 用 **old/new field comparison**(NocoDB webhook 的 `previous_rows` vs `rows`)区分「管理员业务变更」vs「自己的 write-back echo」:echo 直接短路,零 PATCH。
- `writeBack()` 有值比对守卫:字段值没变就不发 PATCH,从物理上掐断 echo loop。