fix: Z39 isolate module data by slug

Keep agent-bound modules in per-app schemas and prevent stale frontend routes from displaying another module's blueprint or rows.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
whm
2026-08-07 17:31:12 +08:00
parent 43dc708f1e
commit 473113682f
13 changed files with 402 additions and 207 deletions

View File

@@ -1,6 +1,6 @@
# 联调后修改意见 · 宇恒松离线(形态 B
> 初稿2026-08-01 · 修订至 **2026-08-07**+Z38 返回上一级/智能体权限)
> 初稿2026-08-01 · 修订至 **2026-08-07**+Z39 跨模块数据隔离/本地表唯一键)
> 焦点:**§0.2**(按负责方);**改代码前须先写入本意见**(见 §0.0
> 来源:宇恒 `yuhengyihao_client` ↔ 智建(生产 `aisite.yuxindazhineng.com`
> 依据:`松离线-dbsync方案-最终版.md`、`宇恒-松离线数据同步使用文档.md`
@@ -33,7 +33,8 @@
| **Z35 库名非法中文** | **智建已落实(须生产 pull** | 绑定/AttachSyncBind/heal 纠正publish 拒非法名§5.33**宇恒勿改** |
| **Z36 编辑发布无增量** | **智建已落实(须生产 pull** | 无增量 + `host_meta.module_name``host_meta_updated`;否则中文 `[NO_BLUEPRINT_DELTA]`§5.34 |
| **Z37 SyncPage LWW 403 吓人** | **智建已落实(须生产 pull** | SyncPage 无冲突队列;中性提示 + 空通道说明conflicts 403 静默§5.35 |
| **Z38 绑定即用 + 返回上一级** | **智建已落实(须生产 pull** | 绑定自动并集补齐建站权限并回传;超管公司视图全局返回平台§5.36 |
| **Z38 绑定即用 + 返回上一级** | **智建已落实(须生产 pull** | 绑定自动补齐建站权限超管公司视图全局返回平台§5.36 |
| **Z39 跨模块串数据/页面复用** | **双方已改;智建须生产 pull** | 智能体共享库内按 tenant+slug 分 Schema页面路由防竞态/旧蓝图回执、capsule、日志可核对映射和 revision§5.37 |
| **仍关注** | 用法 | 通道断了靠智建自愈;表数据靠宇恒双向指纹 / 数据恢复;不是「再点启动」 |
### 0.0 修改流程(冻结)
@@ -79,6 +80,7 @@
| **Z33** | **仅数据同步时勿因缺 app.read 进换绑2026-08-06 · 宇恒已改完)**GET /apps 403 且已 sync_bound → apps=[] 继续模块菜单并提示开通读取模块。**智建无需改**(控制台给智能体开「读取模块」即可列模块) |
| **Z34** | **换机按账号恢复2026-08-06 · 宇恒半程已改完)**:有手机号时可静默 ticket/confirm。**完整「仅宇恒 ID」见智建 Z34b已落实** |
| **Z36** | **编辑发布无增量中文取消2026-08-07 · 宇恒已改完;智建 Z36 已落实)**:宇恒侧预处理;智建 `host_meta_updated` / `[NO_BLUEPRINT_DELTA]`§5.34 |
| **Z39-YH** | **本地模块镜像表唯一键2026-08-07 · 宇恒已改完)**:表名始终包含唯一 `slug`;发布建表、自动导入、独立导入、手动新增统一传递模块展示名/slug/实体标签。见 §5.37 |
#### A. 宇恒 · 配合注意(非阻塞新开发)
@@ -104,7 +106,8 @@
| **Z35** | 绑定 `database_name=user_{id}`publish 拒绝中文库名并纠正脏数据;见 §5.33 |
| **Z36** | merge 无增量:有 `host_meta.module_name``publish_mode=host_meta_updated`;否则中文 `[NO_BLUEPRINT_DELTA]`;见 §5.34 |
| **Z37** | SyncPage 无冲突队列;中性 LWW 说明 +「该公司暂无通道」空态;`listSyncConflicts` 403 静默;见 §5.35 |
| **Z38** | 宇恒各绑定/恢复路径并集补齐 9 项建站权限;回执/Token 可验;全局返回平台;智能体列表示权限;见 §5.36 |
| **Z38** | 超管进公司后顶栏「返回平台工作台」;绑定默认可带模块读/发权或列表示权限;见 §5.36 |
| **Z39-ZJ** | 智能体共享库不再把各模块都落到 `public`;改为 `app_t{tenant}_{slug}`,长 slug 带哈希防碰撞;页面切换防旧请求覆盖;见 §5.37 |
> 产品一句:**账号已绑定 ⇒ 落点信息固定;通道没了平台自动补,终端无需手填 channel_id。**
@@ -112,7 +115,7 @@
| 优先级 | 编号 | 项 | 说明 |
|--------|------|----|------|
| — | — | Z38 已合入见 B | 当前无待开发项;运维见 B |
| — | — | **当前开发项已清** | Z38、Z39 已落实;剩 B 生产 pull 与联调验收。 |
#### B‴. 智建 · 本次明确不改Z15
@@ -128,7 +131,7 @@
| 优先级 | 项 | 说明 |
|--------|----|------|
| **P0** | **生产 pull 本批** | Z10d + fingerprint + Z14c + **Z12h** + **数据恢复** + **Z34b** + **Z35** + **Z36** + **Z37** + **Z38**`bash ./restart.sh --pull` |
| **P0** | **生产 pull 本批** | Z10d + fingerprint + Z14c + **Z12h** + **数据恢复** + **Z34b** + **Z35** + **Z36** + **Z37** + **Z38** + **Z39**`bash ./restart.sh --pull` |
| **P0** | 成员手机 | 「宇信达」绑 **`13531041944`**;勿超管号 |
| **P0** | 通道表白名单 | **勿**「填入测试默认」;形态 B 可空 |
| **P1** | 凭票 Secret | `YuhengTicket.Secret``YXD_YUHENG_TICKET_SECRET` |
@@ -1202,7 +1205,7 @@ POST /api/v1/agent/sync/channels/{id}/pull
### 5.36 【Z38 · 2026-08-07】宇恒绑定后须直接可用超管进公司须能返回上一级
**状态****智建已落实须生产 pull);宇恒已改提示文案**。
**状态****双方已落实;智建须生产 pull**。
#### 现象
@@ -1216,7 +1219,7 @@ POST /api/v1/agent/sync/channels/{id}/pull
| **绑定权限不完整** | 绑定/凭票路径 `ensureAgentSyncPerm` 目前**只保证「数据同步」**;绑定码 `RedeemBindCode` 甚至未调用该补权函数。宇恒建站技能还需模块、数据和文件权限,因此绑定虽然成功,`GET /api/v1/apps` 仍会因缺 `读取模块` 而 403。 |
| **返回按钮位置** | `PlatformTenantsPage` 有「返回平台工作台」(`exitPlatformTenant``ConsoleLayout` 顶栏在进公司后未放同款按钮,离开公司总览页后用户找不到返回。 |
#### 智建改(已落实
#### 智建改(
1. **绑定即用P0 · 必须)**:把 `ensureAgentSyncPerm` 改为语义明确的 `ensureYuhengAgentPerms`(名字可自定),以**并集追加、不得覆盖/删除已有权限**的方式保证宇恒建站权限包:
`读取模块``写入模块``发布模块``查询数据``新增数据``导入数据``上传文件``下载文件``数据同步`
@@ -1232,13 +1235,6 @@ POST /api/v1/agent/sync/channels/{id}/pull
5. **全局返回P0**`ConsoleLayout`(或所有公司侧页顶栏)在「超管 + 已 enter 某公司」时固定展示按钮 **「返回平台工作台」/「返回上一级」**,调用已有 `exitPlatformTenant`,回到平台公司总览。
6. **权限可见性P1**`AgentUsersPage` 增加「权限」列/Tag展示至少「读取模块、发布模块、数据同步」这是诊断能力**不能替代绑定自动授权**。
#### 落实记录2026-08-07
- `ensureYuhengAgentPerms` 对现有权限做并集追加,保证 9 项宇恒建站权限并激活账号,不删除已有权限;补权写库失败时绑定/换票直接失败。
- `ticket-exchange`、手机号确认、绑定码兑换、`restore-by-host` 及已绑定账号的 `client_credentials` 换票均在轮换密钥/签发 Token 前补权;恢复路由已接回,回执返回最终 `permissions`,成功文案统一为「绑定并授权完成」。
- `ConsoleLayout` 在平台超管进入公司上下文后固定显示「返回平台工作台」,任意公司侧页面均可退出管理视图。
- `AgentUsersPage` 增加权限 Tag 列,便于核对「读取模块、发布模块、数据同步」等有效权限。
#### 宇恒
已改缺权提示,明确平台授权未完成;宇恒不再把「后台人工勾选」定义为正常开通步骤。接口成功后直接继续模块操作。
@@ -1255,9 +1251,62 @@ POST /api/v1/agent/sync/channels/{id}/pull
4. 超管「管理该公司」后,无论停在智能体/同步/模块哪一页,顶栏均可一键回平台工作台。
5. 智能体列表可见权限,但用户正常使用不依赖该页面。
### 5.37 【Z39 · 2026-08-07】不同模块页面串数据本地镜像表唯一键不完整
**状态****双方已落实;智建须生产 pull 并按下述场景联调验收**。
#### 现象与证据
本次新建模块 `whm11` 后,公开页面标题虽为 `whm11`,记录列表却显示了另一模块 `whm1` 的 100 条数据,且页面结构与前一模块一致。
宇恒本机库实查:
```text
模块·whm1·数据 100 条
模块·whm11·数据 0 条
```
两张 SQLite 表名称、Schema 和行数均已分开。因此**本次 `whm11` 页面显示 100 条旧数据,不是宇恒把这两批数据写入同一张本地表**;智建须检查公开页实际请求的 slug、资源路由、物理 Schema/table 和发布缓存。
同时发现宇恒本地镜像存在一个独立的潜在碰撞点:`table_name_for_resource()``module_name`/蓝图 `meta.name` 存在时优先使用展示名,未强制把唯一 `slug` 放进表名。两个不同 slug 若展示名相同,后建模块会把“表已存在”当作成功并复用旧表。该风险不是上述 `whm1`/`whm11` 当前串读的直接原因,但必须一并修正。
> 页面布局相似本身不一定是错误:相同字段和相同需求可能生成相似模板。若页面标题、接口 base path、数据源或构建产物也沿用前一 slug则属于发布产物/缓存隔离错误。
#### 智建改(须 · P0
1. **数据路由强隔离**`/api/v1/apps/{slug}/{resource}` 从请求到 ORM/SQL 的解析键必须包含 `tenant_id + app_slug + resource`禁止只按实体名、resource`records`/`数据`)或当前默认通道选表。
2. **`schema_per_app` 真隔离**:不同 slug 必须落到不同物理 Schema/tablepublish 回执及 `agent-capsule` 应返回可核对的 `app_slug``schema_name`、resource→table 映射和 revision。
3. **生成页面不可硬编码旧 slug**:公开页的数据请求 base path 必须来自本次发布 slug切换/新建模块不得继承上一个模块的 API 地址。
4. **发布缓存隔离**:构建/部署缓存键至少包含 `tenant_id + slug + blueprint revision/hash`;不得因页面结构相似复用另一模块的静态产物或运行时配置。
5. **补诊断日志**:每次页面数据查询记录 `requested_slug → resolved_schema/table → blueprint_revision`publish 记录缓存命中键,便于直接确认串读点。
6. **禁止默认模块回退**:请求带合法 slug 但映射不存在时应明确 404/配置错误,不能回退第一模块、最近发布模块或通道上的 `app_slug`
#### 智建落实记录2026-08-07
1. **根因修复**:智能体绑定的 `database_name` 是多模块共享库;旧逻辑却强制 `database_per_app/public`,导致不同 slug 的同名实体表复用。现改为共享库内 `schema_per_app`,每个模块使用独立 `app_t{tenant}_{slug}`;旧 `public` 模块重发时升级到独立 Schema随后由双向同步重新灌入所属数据。
2. **标识符防碰撞**Schema/独立库名超过 48 字符时保留可读前缀并附加 slug 内容哈希,不再直接截断造成长 slug 碰撞。
3. **页面防串包**hash 路由加载增加请求序号,快速切换模块时旧请求不得覆盖新蓝图;加载期间卸载旧模块,且校验返回蓝图 `meta.slug` 必须等于 URL slug。蓝图/数据 GET 使用 `no-store`
4. **回执与 capsule 可核对**publish 增加 `blueprint_revision``resource_tables`agent-capsule 增加 `app_slug``schema_name`、revision 和各资源物理 table。
5. **诊断与拒绝回退**CRUD 解析后强校验 requested slug 与 resolved app 一致、Schema/table 非空;日志记录 tenant、requested slug、resource、resolved Schema/table、revision。现有 Meta 查询仍严格按 `tenant_id + slug`,不存在默认模块回退。
6. **缓存说明**:当前仓无按页面结构复用的构建产物缓存;以 tenant+slug 定位元数据并以蓝图 revision 作为诊断/响应标识HTTP 明确 `no-store`,避免旧运行时配置被复用。
#### 宇恒改(须)
1. **已完成**`local_module_mirror.table_name_for_resource()` 的隔离键始终包含规范化 `slug`;展示名仅用于可读前缀,不能替代 slug。格式为 `模块·{展示名}·{slug}·{资源}`;展示名与 slug 相同时不重复。
2. **已完成**发布建表、自动导入、独立导入、手动新增统一传递模块展示名、slug 和实体标签,避免同一模块因调用入口不同落到不同表。
3. **已保留**行级 `模块slug` 元数据;“表已存在”仅作为同名表幂等处理。字段 Schema 严格一致性校验列入后续增强,不阻塞本次唯一键修复。
#### 验收
1. 同租户建立 `whm1``whm11`,两者都定义同名资源 `数据/records`;分别导入 A、B 两组明显不同的数据。
2. 打开 `/#/app/whm1` 只能看到 A打开 `/#/app/whm11` 只能看到 B两个 API 交叉查询均不得串行。
3. 两模块使用相同展示名时,本地仍生成两张包含各自 slug 的表,导入/新增互不混写。
4. 两次发布使用相同页面结构时,页面 API base path、Schema 和构建 revision 仍分别对应各自 slug。
5. 日志可直接看到每次请求的 requested slug 与最终物理 Schema/table且不存在“找不到映射后回退默认模块”。
## 6. 联系与附件
- **待改清单(优先看)**§0.2(当前开发项已清**B 运维 pull**);配合见 **§0.3**
- **待改清单(优先看)**§0.2**当前开发项已清 · B 运维 pull****改代码前先写本意见**§0.0配合见 **§0.3**
- 方案:`松离线-dbsync方案-最终版.md`(含 2026-08-01 联调建议落地记录)
- 宇恒使用说明:`宇恒-松离线数据同步使用文档.md`(含 Z10 schema/ensure、Z13 绑定)
- 开通说明:`docs/数据同步-开通说明.md`(含 **数据恢复**