SQLResource

基于已保存的 SQL 配置动态 SQL 执行查询的 Resource,数据来源为 flowSql:run / flowSql:runById 等接口。适用于报表、统计、自定义 SQL 列表等场景。与 MultiRecordResource 不同,SQLResource 不依赖数据表,直接执行 SQL 查询,支持分页、参数绑定、模板变量({{ctx.xxx}})及结果类型控制。

继承关系:FlowResource → APIResource → BaseRecordResource → SQLResource。

创建方式ctx.makeResource('SQLResource')ctx.initResource('SQLResource')。按已保存配置执行时需 setFilterByTk(uid)(SQL 模板的 uid);调试时可用 setDebug(true) + setSQL(sql) 直接执行 SQL;RunJS 中 ctx.api 由运行环境注入。


适用场景

场景说明
报表 / 统计复杂聚合、跨表查询、自定义统计指标
JSBlock 自定义列表用 SQL 实现特殊筛选、排序或关联,并自定义渲染
图表区块保存 SQL 模板驱动图表数据源,支持分页
与 ctx.sql 的取舍需要分页、事件、响应式数据时用 SQLResource;简单一次性查询可用 ctx.sql.run() / ctx.sql.runById()

数据格式

  • getData() 根据 setSQLType() 返回不同格式:
    • selectRows(默认):数组,多行结果
    • selectRow单条对象
    • selectVar标量值(如 COUNT、SUM)
  • getMeta() 返回分页等元信息:pagepageSizecounttotalPage

SQL 配置与执行模式

方法说明
setFilterByTk(uid)设置要执行的 SQL 模板 uid(对应 runById,需先在管理端保存)
setSQL(sql)设置原始 SQL(仅调试模式 setDebug(true) 时用于 runBySQL)
setSQLType(type)结果类型:'selectVar' / 'selectRow' / 'selectRows'
setDebug(enabled)为 true 时 refresh 走 runBySQL(),否则走 runById()
run()根据 debug 调用 runBySQL()runById()
runBySQL()用当前 setSQL 的 SQL 执行(需先 setDebug(true))
runById()用当前 uid 执行已保存的 SQL 模板

参数与上下文

方法说明
setBind(bind)绑定变量。对象形式配合 :name,数组形式配合 ?
setLiquidContext(ctx)模板上下文(Liquid),用于解析 {{ctx.xxx}}
setFilter(filter)额外筛选条件(传入请求 data)
setDataSourceKey(key)数据源标识(多数据源时使用)

分页

方法说明
setPage(page) / getPage()当前页(默认 1)
setPageSize(size) / getPageSize()每页条数(默认 20)
next() / previous() / goto(page)翻页并触发 refresh

SQL 中可使用 {{ctx.limit}}{{ctx.offset}} 引用分页参数,SQLResource 会在上下文中注入 limitoffset


数据拉取与事件

方法说明
refresh()执行 SQL(runById 或 runBySQL),将结果写入 setData(data) 并更新 meta,触发 'refresh' 事件
runAction(actionName, options)调用底层接口(如 getBindrunrunById
on('refresh', fn) / on('loading', fn)刷新完成、开始加载时触发

示例

按已保存模板执行(runById)

ctx.initResource('SQLResource');
ctx.resource.setFilterByTk('active-users-report'); // 已保存的 SQL 模板 uid
ctx.resource.setBind({ status: 'active' });
await ctx.resource.refresh();
const data = ctx.resource.getData();
const meta = ctx.resource.getMeta(); // page、pageSize、count 等

调试模式:直接执行 SQL(runBySQL)

const res = ctx.makeResource('SQLResource');
res.setDebug(true);
res.setSQL('SELECT * FROM users WHERE status = :status LIMIT {{ctx.limit}}');
res.setBind({ status: 'active' });
await res.refresh();
const data = res.getData();

分页与翻页

ctx.resource.setFilterByTk('user-list-sql');
ctx.resource.setPageSize(20);
await ctx.resource.refresh();

// 翻页
await ctx.resource.next();
await ctx.resource.previous();
await ctx.resource.goto(3);

结果类型

// 多行(默认)
ctx.resource.setSQLType('selectRows');
const rows = ctx.resource.getData(); // [{...}, {...}]

// 单行
ctx.resource.setSQLType('selectRow');
const row = ctx.resource.getData(); // {...}

// 单值(如 COUNT)
ctx.resource.setSQLType('selectVar');
const total = ctx.resource.getData(); // 42

使用模板变量

ctx.defineProperty('minId', { get: () => 10 });
const res = ctx.makeResource('SQLResource');
res.setDebug(true);
res.setSQL('SELECT * FROM users WHERE id > {{ctx.minId}} LIMIT {{ctx.limit}}');
await res.refresh();

监听 refresh 事件

ctx.resource?.on?.('refresh', () => {
  const data = ctx.resource.getData();
  ctx.render(<ul>{data?.map((r) => <li key={r.id}>{r.name}</li>)}</ul>);
});
await ctx.resource?.refresh?.();

注意事项

  • runById 需先保存模板setFilterByTk(uid) 的 uid 必须是已在管理端保存的 SQL 模板 ID,可通过 ctx.sql.save({ uid, sql }) 保存。
  • 调试模式需权限setDebug(true) 时走 flowSql:run,需当前角色具备 SQL 配置权限;runById 仅需登录即可。
  • refresh 防抖:同一事件循环内多次调用 refresh() 只会执行最后一次,避免重复请求。
  • 参数绑定防注入:使用 setBind() 配合 :name / ? 占位符,避免字符串拼接导致 SQL 注入。

相关