# n8n Workflow 说明(已实施) > n8n 运行在现有 auto.hoelee.com(`bridge_hoelee` 网络)。它是**同步器,不是 SOT**。 > 更新日期 2026-08-23。 ## 已实现的三个 Workflow | Workflow | ID | 触发 | 职责 | |---|---|---|---| | **W1** Customer Provision & Status Lifecycle | `gU2ee9Eg5w1KzB5p` | NocoDB Customers webhook | provision + 整户 status lifecycle | | **W2** CustomerProduct Sync | `D9OWAI7JUjKnRdOn` | NocoDB CustomerProducts webhook | 重算该客有效 grants 并应用 | | **W3** Expiry Sync | schedule `*/5` | 定时 | 全量对账 + drift 修复 | 三个 workflow 共享同一套算法 `calculateEffectiveGrants`(`n8n/shared/effective-grants.js`, 构建时经 `gen_w1/w2/w3.js` 内联进各 Code 节点)。 ## W1 — Customer Provision & Status Lifecycle - 触发:Customers 表 after-insert / after-update(webhook `alist-customer-provision`) - 单一 Code 节点,处理 5 状态: - `active`(无 user)→ 建 role + 建 user + 重算 grants + enable - `active`(有 user)→ re-enable + 重算 grants(不恢复旧值)+ 应用 - `pending`/`suspended`/`expired`/`deleted`(有 user)→ disable + 清空 scopes - `pending`/`suspended`/`expired`/`deleted`(无 user)→ no-op - 回填 `alist_user_id` / `alist_role_id`,写 `sync_status=synced` ## W2 — CustomerProduct Sync - 触发:CustomerProducts 表 after-insert / after-update(webhook `alist-customerproduct-sync`) - 读 CP → 定位 customer → 重算该客 grants → diff AList role → 有变化才 `role/update` 整体重写 - `customer` link 为空时优雅跳过(不回写) ## W3 — Expiry Sync(全量对账) - 触发:每 5 分钟 schedule - 逐客调 `calculateEffectiveGrants` → diff → 应用 - Env 开关:`W3_DRY_RUN=true`(只打印计划)、`ALIST_AUTO_DISABLE_ON_EXPIRE=true`(scopes 变空时 disable 用户) ## 共用 env 变量 n8n Code 节点内用 `$env.VAR`(`process.env` 不可靠)。下表是本项目需要的 env: | 变量 | 说明 | |---|---| | `NOCODB_URL` / `NOCODB_TOKEN` | NocoDB 地址 + `xc-token` | | `NOCODB_TABLE_CUSTOMERS` / `..._CUSTOMERPRODUCTS` / `..._PRODUCTS` / `..._RESOURCES` / `..._ACCESSGRANTS` | 各表 model_id | | `NOCODB_LINK_PROD_RESOURCES` | Product↔Resource M2M 列 id(`c4ko4x8kwi16swg`)| | `ALIST_URL` | `http://alist-serve-resource:5244`(n8n 容器内)| | `ALIST_ADMIN_USERNAME` / `ALIST_ADMIN_PASSWORD` | AList admin 登录 | | `W3_DRY_RUN` / `ALIST_AUTO_DISABLE_ON_EXPIRE` | W3 开关 | ## 幂等规则 所有操作 = get current → compare → apply diff。重跑不产生重复 user/role/scope。 更新 scope 一律「整体重写该 role 的 `permission_scopes`」。 ## Loop 防护(重要) - n8n 只写回 bookkeeping 字段(`alist_user_id`/`alist_role_id`/`sync_status`/`sync_required`/`last_sync`/`last_sync_error`)。 - W1 用 **old/new field comparison**(NocoDB `previous_rows` vs `rows`)区分「业务变更」vs「echo」;echo 直接短路。 - `writeBack()` 值比对守卫:字段值不变就不发 PATCH,掐断 echo loop。 ## 依赖的 AList API(源码级验证,见 API_INTEGRATION.md) - 登录:`POST /api/auth/login` - User:`/api/admin/user/{list,get,create,update,delete}` - Role:`/api/admin/role/{list,get,create,update,delete}`(delete 用 `?id=` query param)