Skip to content

[approvals][console] 「待我审批」有三个并存入口、三套不同 UI —— 其中 sys_approval_request 原生视图是无决策动作的开发者原始表 #7213

Description

@baozhoutao

在应用项目(v17.0.0-rc.5,Console + plugin-approvals)实测中发现:同一个「我有待审批的事」在 Console 里同时存在三个入口,三套完全不同的 UI、不同的词表、不同的能力,其中一个入口是没有任何决策动作的开发者原始数据表。终端用户无法判断哪个才是"正确"的入口。

下面按入口逐一列事实(同一条待审批记录,同一个账号)。


入口 A —— 首页「待办事项 → N 条待审批」→ 审批中心

路由:/_console/apps/setup/system/approvals

  • 标题「审批中心 / 查看并处理审批请求」,tabs:待我审批(8) | 我发起的 | 全部,带搜索 + 流程/对象筛选 + 排序。
  • 列表列:审批事项(流程节点中文名,如「QIF 质量信息反馈单审批 / 质检部部长审批」)、记录(业务编号 + 对象中文名)、申请人、状态(待审批)、提交时间。
  • 点行开右侧抽屉:记录摘要字段 + 节点进度条(① 质检部部长审批 → ② 多部门并行会审 → ③ 工厂长审批)+ 「等待以下审批人」+ 审批动态 + 回复框 + 决策按钮 + 「上一条 / 下一条」翻页。

这是三者中唯一完整、且面向业务语义的一套。但它挂在 setup 应用(系统设置)下面 —— 而日常审批是业务用户的高频操作,业务角色一般不会开放系统设置应用的访问权;也没有办法把这个页面挂到业务应用自己的导航里。

入口 B —— 首页待办里的通知 →(业务对象)记录页

路由:/_console/apps/<app>/<object>/record/<id>

  • 记录页头出现「批准 / 驳回」按钮;页头字段区有「审批状态 = 审批中」。
  • 阶段条是流程节点:① 质检部部长 | ② 多部门并行会审 | ③ 工厂长 | 归档 | 审批驳回。
  • 详情区另有「审批中·可编辑」「撤回审批」。

第三套视觉与信息组织,和入口 A 的抽屉不共用摘要/决策组件。

入口 C —— 账户应用 → 审批请求 → 「我的待办」

列表路由:/_console/apps/com.objectstack.account/sys_approval_request/view/my_pending

这是 sys_approval_request 的原生对象列表,呈现的全是开发者视角的原始值:

# 来源 对象 记录 ID 当前步骤 提交人
1 flow:qif_approval os_..._qif -2kQ8hM_wiI5xPrC lv1_qc_director marchtian

点进记录页 /_console/apps/com.objectstack.account/sys_approval_request/record/areq_<uuid>:

  • 标题是 flow:qif_approval · -2kQ8hM_wiI5xPrC;
  • 状态显示 待处理;阶段条是请求状态:待处理 | 已批准 | 已拒绝 | 已撤回 | 已退回修改;
  • 字段:请求 ID = areq_…、当前步骤索引 = 0、FLOW RUN = run_<uuid>、FLOW NODE = lv1_qc_director;
  • 「待审批人」渲染成裸用户 ID(airUFxCipivl1VYMACC…),同 [approvals] 转签审计默认 comment 落裸用户 ID("<from_id> → <to_id>"),审批动态里看不出谁转给谁 #4365 的形态;
  • 整页没有批准 / 驳回 / 任何决策入口 —— 用户从「待我审批」点进来后只能原路退出。

汇总的问题

  1. 入口分裂:同一件事有 3 个并存入口(首页待办、setup 下的审批中心、账户应用的审批请求),命名各异(「审批中心」/「待我审批」/「审批请求 · 我的待办」/「待办事项」),没有一个是被平台明确指定的"正确入口"。
  2. 唯一完整的入口在管理后台里:审批中心位于 setup 应用,业务用户通常没有该应用的访问权;也无法挂到业务应用导航。
  3. sys_approval_request 的原生视图直接面向终端用户暴露:显示流程 id / 对象 API 名 / 记录 ID / 节点 id / 裸用户 ID,且记录页无决策动作,是一条死路。如果这张表定位为系统内部实例表,它不应该出现在终端用户的「待我审批」导航里;如果它要面向用户,就得渲染人类可读标签并带上决策动作。
  4. 词表不统一:同一条请求,审批中心显示 待审批,sys_approval_request 显示 待处理;阶段条一处是流程节点、一处是请求状态,同样的横条控件表达两种完全不同的轴。
  5. 计数分散:首页「8 条待审批」、审批中心 待我审批 8、铃铛 9+,用户侧同时看到多个不一致的数字。

期望(方向,不指定实现)

  • 收敛出单一「我的审批待办」入口,并允许挂载到任意业务应用的导航,而不是只存在于 setup;
  • 三处(待办卡片 / 审批中心抽屉 / 业务记录页头)复用同一套「记录摘要 + 节点进度 + 决策」组件;
  • 状态、阶段的词表跨入口统一;
  • sys_approval_request 的可见性与呈现按上面第 3 点明确定位。

环境

  • @objectstack/*@17.0.0-rc.5,Console 预览版,zh-CN。
  • 来自应用项目的实测;截图在内部工单里,需要的话可以补贴(对象/流程名已在上文脱敏为通用形式)。

PM epic registration (pm:epic) — ✅ CLOSED OUT 2026-08-10

(Note: the two route examples in 入口 B / 入口 C above now spell their placeholders as HTML entities so this body survives GitHub's sanitizer on rewrite; the wording is otherwise unchanged from the original report.)

Activity

  1. os-zhuang commented on Aug 10, 2026

    @os-zhuang
    Contributor

    Triage + v17 plan (maintainer-directed, 2026-08-10)

    Maintainer directive (chat, 2026-08-10, quoted verbatim): 「按照你的建议继续,相关任务作为子任务,开始派发并开发。」

    Code-level findings behind the plan (verified on both repos' origin/main today):

    • Entry A (Approvals Inbox) is a bespoke objectui console page (apps/console/src/pages/system/ApprovalsInboxPage.tsx) whose relative route system/approvals already resolves under any app (AppContent.tsx:261 passes systemRoutes as both extraRoutes and extraRoutesNoApp). It is "under setup" only because every hardcoded entry link says setup; no navigation metadata points at it, and the component registry has no approvals:* key.
    • Entry C is exactly one metadata declaration: platform-objects/src/apps/account.app.ts:100-108 (type:'object' → sys_approval_request/my_pending) in an app with no requiredPermissions. Its record page has no decision actions because the 8 server-declared actions gate on record.viewer.can_act || record.viewer.can_override, and the viewer block is only attached by the approvals REST path (approval-service.ts attachViewers()), never by the generic data API.
    • The vocabulary split is structural: three independent i18n sources with no shared keys (plugin-approvals object bundle 待处理/我的待办; platform-objects nav 待我审批; objectui approvalsInbox.* 审批中心/待审批). The en object bundle even ships raw enum values (pending: "pending").
    • The bell badge is unread notification topics + pending approvals while the home card and inbox tab show approvals only — hence 8 vs 9+.

    Boundaries with the sibling issues: objectui#2762 (queued) polishes the bespoke page in place; objectui#2763 (approved epic, on hold until after v17) rebuilds the whole surface on standard SDUI renderers. This issue therefore converges entries, vocabulary, and positioning at the metadata level only — no new investment in the bespoke page, and the approvals:inbox component-registry key is the stable indirection the v18 rebuild will simply repoint (zero nav-metadata churn at that time).

    Sub-issues (this issue stays open as the coordination node):

    # Scope Repo Order
    #7231 Register approvals:inbox component-registry key; de-hardcode Home entry link; keep system/approvals deep links objectui round 1 (dispatched)
    #7232 Unify status vocabulary in the plugin-approvals i18n bundles (zh 待处理→待审批, view label; humanize en) objectstack round 1 (dispatched)
    #7234 Account-app nav → approvals:inbox component; Setup inbox entry; docs for mounting in business apps objectstack round 2, Blocked-by: #7231
    #7233 Bell badge breakdown (notifications vs approvals) objectui queued, independent

    Entry B (record-page header decisions) intentionally unchanged in v17 — it is the in-context surface, and component unification is #2763 B1/B2. sys_approval_request stays an admin/diagnostic raw table under Setup (manage_platform_settings); attaching viewer on the generic data API is deliberately deferred to #2763 A1.

    End-to-end console behavior for #7234 additionally lands with the next console pin bump (standing chore, not a rider).


    Generated by Claude Code

  2. os-zhuang commented on Aug 10, 2026

    @os-zhuang
    Contributor

    Triage note — three observations, no label action taken:

    1. Truncation dual-read: the REST body read ends mid-sentence inside 入口 B's route; the rendered page is complete (entries A/B/C, 汇总的问题, 期望, 环境). The two reads do NOT break at the same place ⇒ reading-end artifact, not sanitizer truncation. The stored body is intact and needs no repair.
    2. Claim state: assigned minutes after filing — in flight. Per the state model this seat applies no queue labels to a claimed card and does not touch the claim. Skip-by-design.
    3. Routing observation, for whenever the claim resolves into scoped work: the face spans the console rendering side (approval center UI, entry C's raw sys_approval_request view — objectui), plugin-approvals (decision actions / notification deep-links), and packages/platform-objects (the setup / account app mounts), with an embedded product call (which entry is canonical; whether business apps can mount the approval center). If it returns to the backlog unclaimed, it is a contract-first split candidate, not a single dispatch.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  3. os-zhuang commented on Aug 10, 2026

    @os-zhuang
    Contributor

    Epic complete — all five sub-issues merged

    Every entry reported here has been converged at the metadata level, and the bundled console now carries it. Closing this parent as the coordination node.

    Sub-issue What shipped PR
    #7231 approvals:inbox registered in the component registry; Home's approvals card stops routing every user through setup objectstack-ai/objectui#4071
    #7232 One vocabulary across the bundles (zh 待审批 / 待我审批, en humanized), plus a cross-package parity pin so the layers cannot drift apart again #7271
    #7233 The bell badge's N notifications + M pending approvals breakdown, unclamped, so 9+ is explainable objectstack-ai/objectui#4073
    #7234 The account app's Approvals entry now opens the inbox component; Setup gains an Approvals Inbox entry above the raw tables; docs teach mounting it in any app #7298
    #7268 Console pin 09987b680d53 → 8aad9fd50b16, so the bundled console actually carries the above #7308

    Against the five problems as reported

    1. Entry fragmentation — one canonical approver surface, the Approvals Inbox, reachable from the account app (every authenticated user), from Setup (admins), and mountable in any business app with a single nav entry naming approvals:inbox. The registry key is the indirection, so nav metadata never spells a console path.
    2. The complete entry lived in the admin app — no longer: it was never actually bound to setup (system/approvals is mounted app-agnostically), only every link to it was hardcoded. Those are fixed, and the mountable entry is documented in content/docs/automation/approvals.mdx.
    3. sys_approval_request exposed raw to end users — removed from the account app; it remains under Setup → Approvals behind manage_platform_settings as the admin/diagnostic surface, which is what an engine-owned table is good for. The docs now say why it can never be the approver surface: its decision actions gate on record.viewer.can_act, and viewer is attached only on the approvals REST path.
    4. Vocabulary — aligned, and pinned across packages.
    5. Counts — the bell's total is now shown as its two addends, matching the number Home and the inbox tab display.

    Entry B converged too, and by a route this epic did not plan. The pin in #7268 carries objectui 99782f961 — "Run sys_approval_request's server-declared decision actions on the business record page, and retire the hard-coded two-button approval path" (objectui#3055). The bespoke record-page Approve/Reject path this issue reported as the third UI is retired upstream, onto the same server-declared actions the inbox uses. So all three entries converged, not two.

    Residuals — recorded, none blocking

    Boundaries kept

    objectui#2762 (polish of the bespoke page) and objectui#2763 (the approved SDUI rebuild, on hold until after v17) remain their own tracks. Nothing here started #2763's work: when it lands, it repoints approvals:inbox and no navigation metadata churns — which is the reason the registry key exists.


    Generated by Claude Code

  4. baozhoutao commented on Aug 10, 2026

    @baozhoutao
    ContributorAuthor

    Browser verification of the converged surface — done, and it holds

    The close-out above records browser verification as "the honest gap". It has now been run against main (88154bee1) with the vendored console rebuilt at the current pin (.objectui-sha = 8aad9fd50b16), on examples/app-showcase with a fresh seeded DB, zh-CN, dev admin.

    Confirmed live, all five reported problems:

    Still not verified, and it is the one thing left from the close-out's list: a real non-admin session. The seeded instance had no second account with a usable password and none was created. Server-side the account app declares no requiredPermissions and the entry gates only on requiresService, so a non-admin should reach it — but that is inference, not measurement.

    Residuals, updated:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions