Taruvi Refine providers(前端)
这是一个面向 Taruvi 的单一前端技能,覆盖一切运行在浏览器中的内容:接线 @taruvi/refine-providers、针对 Taruvi 的所有 Refine hook 用法、生产可用的列表/仪表盘/下拉/上传 UX、认证流程以及访问控制。
如果任务是准备资源或编写 Python 函数体,请改用 taruvi-app-developer。
Provider 映射
| Provider | Refine key | 用途 |
|---|---|---|
| dataProvider(client) | default | 数据表 CRUD、过滤、分页、聚合、图 |
| storageDataProvider(client) | storage | 文件/对象上传、列表、下载、删除 |
| appDataProvider(client) | app | 函数/分析执行、角色、设置、密钥(通过 useCustom / useList / useOne) |
| userDataProvider(client) | user | 用户 CRUD |
| authProvider(client) | authProvider prop | 登录、登出、令牌刷新、身份、权限 |
| accessControlProvider(client) | accessControlProvider prop | 为 useCan / CanAccess 做批量 Cerbos 检查 |
Hook 支持矩阵与 meta 选项见 overview.md。
已废弃——不要使用:functionsDataProvider、analyticsDataProvider。请改用 appDataProvider + useCustom,并设置 meta.kind: "function" 或 meta.kind: "analytics"。
工作流
- 阅读一次 overview.md——安装、客户端设置、完整 provider/hook 映射。
- 打开任务相关参考(下表)。
- 确认已安装包当前未废弃的 API。不要因为存在旧示例,就把新代码发布在已废弃的接口上。
- 应用生产级默认值(下一节)。
- 在报告完成前,为每个角色走查 UI——确认列表操作、创建按钮和菜单项正确显示/隐藏。
参考索引
| 任务 | 阅读 |
|---|---|
| 数据库 CRUD、过滤、聚合、图 | database-provider.md |
| 文件上传、批量删除、元数据、过滤 | storage-provider.md |
| 函数执行、分析、角色、设置、密钥 | app-provider.md |
| 用户 CRUD | user-provider.md |
| 登录、登出、令牌流程、身份 | auth-provider.md |
| useCan、CanAccess、带前缀的 ACL 资源 | access-control-provider.md |
| TypeScript 类型、废弃迁移 | types-and-utilities.md |
生产级默认值
除非用户明确缩小范围,否则每个前端交付物都必须满足以下要求。它们不是建议。
列表 / 表格
- 默认后端分页。默认 pageSize 为 10;提供 10 / 20 / 50 / 100 选项。
- 服务端搜索、过滤、排序——下推到 provider filters / sorters,绝不在 React 中对已获取的行重新应用。
- 可见的搜索输入 + 相关过滤控件(状态、部门、日期范围)——搜索防抖 300–500ms。
- 每页一个主搜索控件(不要用独立的页面搜索在同一字段上重复 DataGrid 快速过滤)。
- MUI DataGrid → 使用 useDataGrid。手写 useList + 组件状态是已记录在案的例外,不是默认做法。
仪表盘 / KPI / 图表
- 默认:通过 useList 配合 meta.aggregate: ["count"] 使用数据表聚合 + groupBy(注意是数组——aggregate: "count" 会静默失败)。对于跨表需求,数据提供器支持 populate 与聚合一起使用——先试这个;对已 populate 的外键字段做聚合通常就够了。
- 分析查询是例外,不是默认。只有在 (a) 源是外部数据库或 (b) SQL 确实无法通过数据提供器表达(带分组聚合的多表连接、窗口函数、递归 CTE 等)时,才使用 appDataProvider + useCustom({ meta: { kind: "analytics" } })。注册查询是单独的后端往返(taruvi-app-developer);任何单个 useList 配合 meta.aggregate + meta.populate 能完成的事情都应避免它。
- 绝不要把完整行集拉进 React 来推导汇总指标。
- 图查询必须设置显式 depth。
- having 仅在 groupBy 之后有效。聚合前过滤请使用 filters。
网络支持的下拉
- 使用 Autocomplete(或等价的 typeahead),不要静态 Select。
- 分页后端查询(默认选项 pageSize 10),防抖搜索下推到 provider filters。不要对一次性加载做客户端过滤。
文件上传 / 附件
- 默认多文件 UX。报告每个文件的成功/失败——绝不使用全有或全无的聚合。
- 分块批上传到 ≤10 个文件 / ≤100MB;分块批删除到 ≤100 个路径。
- 配额是提示性的——显示为警告,而不是上传阻断。
- 覆盖时警告——上传会静默替换现有路径。
- 在 bucket 级别设置可见性;单对象覆盖优先于 bucket 默认。
批量操作
- 单资源使用后端批量操作(updateMany、deleteMany);多资源级联使用无服务器函数。
- 清楚显示选择范围(选中行 vs 过滤集),展示部分失败详情,使受影响查询失效。
通知
- 使用已接线的 Refine notificationProvider(useNotification)。不要添加并行的 toast/snackbar 系统。
契约规则(强制)
函数执行
useCustom({
url: "<function-slug>",
method: "post",
dataProviderName: "app",
payload: { /* inputs */ },
meta: { kind: "function" },
});
payload 承载输入——不是 values,不是 config.payload。
分析执行
同样形状,使用 meta: { kind: "analytics" }。
访问控制
- useCan / CanAccess 资源使用带前缀的 ACL 字符串——datatable:employees、function:employee-terminate、query:hrms-dashboard-summary。绝不使用 params.entityType,绝不使用裸资源名。
- 在检查前将 UI 动作映射到规范 Cerbos 动作:list / show → read,edit → update,create → create,delete → delete,execute 用于函数/查询。
- 验证运行时 payload——/check/resources 的 resource.kind 必须与请求的资源字符串完全匹配。
- 不要手动防抖或节流 useCan——accessControlProvider 已经通过 DataLoader 批量处理。
客户端设置
const client = new Client({
apiKey: import.meta.env.VITE_TARUVI_API_KEY,
appSlug: import.meta.env.VITE_TARUVI_APP_SLUG,
apiUrl: import.meta.env.VITE_TARUVI_API_URL,
});
<Refine
dataProvider={{
default: dataProvider(client),
storage: storageDataProvider(client),
app: appDataProvider(client),
user: userDataProvider(client),
}}
notificationProvider={useNotificationProvider}
authProvider={authProvider(client)}
accessControlProvider={accessControlProvider(client)}
/>
环境变量使用框架的浏览器暴露前缀(VITE_、REACT_APP_、NEXT_PUBLIC_*)。绝不要硬编码。
值得在此提示的坑
不够明显的那些。各领域的具体坑见参考文档。
- 401 vs 403——401 = 会话过期(重新登录)。403 = 禁止(显示拒绝)。把 403 当作 401 会导致无限重新登录循环。
- 角色变更后的 _cachedUser——身份缓存在模块本地,只在 logout() 时清除。管理员角色变更后,强制重新登录或使 ["identity"] 和 ["access-control"] 查询键失效。
- 异步函数使用 useCustom——异步返回 task_id,不是结果。要么保持函数同步(<30s),要么用单独的状态检查函数接入轮询模式。
- 缺少 dataProviderName——useCustom / useCreate 没有正确的 dataProviderName 会路由到 default,返回令人困惑的 "resource not found" 错误。函数/分析始终设置 "app",bucket 始终设置 "storage"。
- 预填表单字段——在带默认值的 TextFields 上设置 InputLabelProps: { shrink: true },避免标签重叠。
- aggregate 必须是数组——aggregate: "count" 会静默失败。使用 aggregate: ["count"]。
- 对后端数据做客户端过滤——获取一页行并在 React 中过滤是正确性和可扩展性 bug。始终把状态下推到 provider 查询。