Skip to content
签到签到

第 6 章:B 端中后台系统

本章定位

B 端系统是权限、复杂表格、配置化表单、工作流和审计的组合。本章要求从 schema 和状态机视角设计系统,而不是继续堆叠页面。

必须掌握清单

必须掌握

  • 能区分 RBAC、数据权限和按钮权限。

    相关答案: RBAC 根据用户、角色和权限判断能否执行某类操作;数据权限进一步限定能操作哪些记录;按钮权限只是后端授权结果在 UI 上的展示。前端隐藏按钮不能替代服务端鉴权与授权。

    具体例子: “销售”角色拥有查看客户权限,但数据范围只能是本人负责的客户;页面可以隐藏“删除”按钮,用户即使手工调用删除接口,服务端仍必须重新校验权限。

  • 能设计 schema 驱动的表格和表单。

    相关答案: schema 应描述字段、组件、校验、布局和联动等稳定元数据,渲染器负责把声明转换为 UI;复杂业务逻辑仍应放在可测试的函数或组件中,不能把 schema 变成难以维护的脚本语言。

    具体例子: 用户和订单页面共用表单引擎,各自提供字段 schema;“省份变化后刷新城市选项”通过受控联动函数实现,而富文本编辑器保留为显式自定义组件。

  • 能处理复杂编辑器的脏状态和撤销。

    相关答案: 脏状态表示当前可持久化数据与最近保存基线存在差异,应由可比较状态推导;撤销需要记录命令、补丁或状态历史,并明确哪些临时 UI 状态不进入历史。保存成功后要更新基线。

    具体例子: 流程编辑器移动节点时记录一条可逆 patch,Ctrl+Z 应用反向 patch;缩放画布不算业务修改,而修改节点名称会触发离开页面确认。

  • 能设计工作流审批和审计日志。

    相关答案: 审批流应把状态、允许的转换、参与者和条件显式建模,并由服务端保证转换合法;审计日志是只追加的事实记录,至少包含主体、动作、对象、时间、前后值和原因。

    具体例子: 报销单只能由 draft 提交到 pending,审批人再转为 approvedrejected;每次转换都记录操作者、意见和字段差异,前端据当前状态显示可用操作。

  • 能为长列表和大表单做性能边界设计。

    相关答案: 先确定数据规模、交互频率和响应目标,再选择分页、虚拟化、分区订阅和延迟校验。虚拟化解决 DOM 数量,不解决昂贵计算或一次下载全部数据;大表单也应避免任一字段变化导致全表重渲染。

    具体例子: 十万行记录使用服务端筛选分页,当前页再做行虚拟化;两百字段表单按字段订阅状态,并把跨字段校验放到防抖或提交阶段。

原理讲解

RBAC 决定用户能不能执行某个操作,数据权限决定用户能看见哪些数据,按钮权限是 UI 层对权限的投影。前端展示权限不能替代后端校验。

schema 驱动是把字段、校验、布局和联动写成可配置数据。这样相同表单引擎可以服务不同业务,也能减少重复组件。复杂编辑器的脏状态应该基于初始值和当前值的结构化比较,而不是简单布尔值。

审计日志需要记录主体、动作、对象、时间和变更原因。它不是为了打印日志,而是为了回答“谁在什么时候改了什么”。

代码示例或模板

ts
type FieldSchema = {
  key: string;
  label: string;
  required?: boolean;
};

type FormSchema = {
  fields: FieldSchema[];
  permission: "read" | "write";
};

function renderForm(schema: FormSchema): string {
  if (schema.permission === "read") {
    return schema.fields.map((field) => field.label).join("、");
  }

  return schema.fields
    .map((field) => `${field.label}${field.required ? "*" : ""}`)
    .join("、");
}

这个例子只展示 schema 如何同时驱动只读和可写状态,实际表单还需要联动、校验和布局。

AI Agent 概念对照

  • 工具审批 UI 对应权限系统。
  • 工具状态对应工作流状态机。
  • 会话审计对应 B 端审计日志。
  • 危险操作确认对应按钮权限和风险提示。

面试追问

1. RBAC 和数据权限分别在哪里控制?

查看深度解析

标准回答: 服务端是最终控制点:RBAC 判断主体能否执行某动作,数据权限把查询和写入限制到允许的资源范围。前端只负责根据授权结果改善体验,不能靠隐藏按钮或过滤已返回数据保证安全。

原理展开: 功能权限通常在路由或服务入口校验,数据权限应进入查询条件和对象级写入检查;两者还要结合租户边界。

工程示例: 销售角色可查看客户,但查询自动加负责人或部门条件;修改客户时服务端再次检查目标记录归属,前端仅隐藏无权操作。

常见误区: 接口先返回全量数据再由前端过滤,敏感信息已经泄露;或有角色就默认拥有同租户全部数据。

继续追问: 权限变化后缓存怎么办?

回答方向: 权限版本进入缓存键或变更时主动失效,敏感写操作始终实时校验,不信任过期前端状态。

2. 为什么大型表单更适合 schema 驱动?

查看深度解析

标准回答: 大型表单有大量重复的字段、校验、权限、布局和联动元数据,schema 能统一描述并由引擎复用,降低页面复制和规则漂移。但复杂交互仍应使用显式组件与可测试函数。

原理展开: schema 的价值是声明稳定变化点,不是把所有业务逻辑改写成配置语言;需定义版本、扩展槽和错误边界。

工程示例: 同一客户 schema 可渲染新增、编辑和只读模式,字段权限由上下文决定;富文本协同编辑器通过 custom renderer 接入。

常见误区: 在 JSON 中塞字符串表达式和任意脚本,结果失去类型、调试和安全性。

继续追问: schema 如何做跨字段联动?

回答方向: 用声明依赖加受控函数注册表,构建依赖图并检测循环;不要让字段直接通过全局变量互相修改。

3. 虚拟表格快速滚动时如何保持选择状态?

查看深度解析

标准回答: 选择状态必须按稳定业务 row key 存在表格数据模型或外部 store 中,不能依赖当前渲染行索引或 DOM。虚拟行卸载和复用时,根据 key 重新派生选中状态。

原理展开: 虚拟化只保留视口节点,索引会因排序、过滤和分页变化;全选还需定义是当前页、当前筛选结果还是全部数据集。

工程示例: 使用 Set<OrderId> 保存已选 ID;服务端全选采用“筛选条件 + 排除 ID”模型,避免把百万个 ID 放进浏览器。

常见误区: 使用数组下标当 key,滚动复用后勾选跑到另一行;或切换筛选后悄悄保留不可见选择。

继续追问: 数据刷新后已选记录被删除怎么办?

回答方向: 与最新数据或服务端校验选择集,移除失效 ID并提示;批量提交时服务端仍要返回逐项结果。

4. 工作流审批和普通状态机有什么区别?

查看深度解析

标准回答: 工作流审批以状态机为基础,但还增加参与者、权限、条件路由、并行会签、超时、委托、撤回和审计等组织语义。普通状态机只关心状态与合法转换,不一定处理人和流程治理。

原理展开: 审批的转换必须由服务端原子执行并记录原因;前端根据服务端返回的可用动作渲染,不能自行推导最终权限。

工程示例: 报销从 pending 进入 finance_review,金额超过阈值时增加总监节点,所有审批人通过后才 approved。

常见误区: 用一个 status 字段加大量 if 表达所有流程,缺少版本和历史,流程变化后旧单据无法解释。

继续追问: 流程定义升级如何处理在途实例?

回答方向: 实例绑定定义版本;新实例用新版本,在途实例继续旧版或执行显式迁移并留下审计记录。

5. 审计日志应该记录哪些关键信息?

查看深度解析

标准回答: 至少记录谁在何时、通过什么入口、对哪个对象执行了什么动作、结果如何,以及必要的变更前后值、原因、请求/trace ID 和策略版本。日志应只追加、防篡改并控制敏感字段。

原理展开: 审计目标是可追责和重建事实,不等于普通调试日志;必须使用服务端可信身份和时间,定义保留、访问与脱敏策略。

工程示例: 权限变更记录管理员 ID、目标用户、旧角色、新角色、工单原因、IP、request ID 和成功状态,但不记录令牌或完整隐私字段。

常见误区: 只写“更新成功”,无法回答改了什么;或保存完整请求体导致密码、密钥进入长期日志。

继续追问: 批量操作怎么记录?

回答方向: 记录批次级事件和逐项结果,用共同 operation ID 关联;避免一条巨大不可查询日志,也不能只记总体成功。

实践任务

实现一个 schema 驱动的只读表单或权限表格,加入数据权限过滤和操作审计字段。

验收标准

  • 同一个 schema 能渲染只读和可写状态。
  • 前端隐藏按钮不等于后端权限通过。
  • 能说明审计日志的关键字段。

关联材料