mirror of
https://github.com/nocobase/nocobase.git
synced 2026-10-24 23:59:02 +08:00
feat: sql resource
This commit is contained in:
@@ -22,7 +22,7 @@ import { FlowEngine } from './flowEngine';
|
||||
import { FlowI18n } from './flowI18n';
|
||||
import { JSRunner, JSRunnerOptions } from './JSRunner';
|
||||
import { FlowModel, ForkFlowModel } from './models';
|
||||
import { APIResource, BaseRecordResource, MultiRecordResource, SingleRecordResource } from './resources';
|
||||
import { APIResource, BaseRecordResource, MultiRecordResource, SingleRecordResource, SQLResource } from './resources';
|
||||
import { FlowExitException, resolveDefaultParams, resolveExpressions } from './utils';
|
||||
|
||||
type Getter<T = any> = (ctx: FlowContext) => T | Promise<T>;
|
||||
@@ -618,19 +618,22 @@ export class FlowRuntimeContext<
|
||||
) {
|
||||
super();
|
||||
this.addDelegate(this.model.context);
|
||||
const ResourceMap = { APIResource, BaseRecordResource, SingleRecordResource, MultiRecordResource };
|
||||
this.defineMethod('useResource', (className: 'APIResource' | 'SingleRecordResource' | 'MultiRecordResource') => {
|
||||
if (model['resource']) {
|
||||
return;
|
||||
}
|
||||
const R = ResourceMap[className];
|
||||
if (!R) {
|
||||
throw new Error(`Resource class ${className} not found in ResourceMap`);
|
||||
}
|
||||
const resource = new R() as APIResource;
|
||||
resource.setAPIClient(this.api);
|
||||
model['resource'] = resource;
|
||||
});
|
||||
const ResourceMap = { APIResource, BaseRecordResource, SingleRecordResource, MultiRecordResource, SQLResource };
|
||||
this.defineMethod(
|
||||
'useResource',
|
||||
(className: 'APIResource' | 'SingleRecordResource' | 'MultiRecordResource' | 'SQLResource') => {
|
||||
if (model['resource']) {
|
||||
return;
|
||||
}
|
||||
const R = ResourceMap[className];
|
||||
if (!R) {
|
||||
throw new Error(`Resource class ${className} not found in ResourceMap`);
|
||||
}
|
||||
const resource = new R() as APIResource;
|
||||
resource.setAPIClient(this.api);
|
||||
model['resource'] = resource;
|
||||
},
|
||||
);
|
||||
this.defineProperty('resource', {
|
||||
get: () => model['resource'] || model.context['resource'],
|
||||
cache: false,
|
||||
|
||||
@@ -12,4 +12,5 @@ export * from './baseRecordResource';
|
||||
export * from './flowResource';
|
||||
export * from './multiRecordResource';
|
||||
export * from './singleRecordResource';
|
||||
export * from './sqlResource';
|
||||
//
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* This file is part of the NocoBase (R) project.
|
||||
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
|
||||
* Authors: NocoBase Team.
|
||||
*
|
||||
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
|
||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||
*/
|
||||
|
||||
import { observable } from '@formily/reactive';
|
||||
import _ from 'lodash';
|
||||
import { BaseRecordResource } from './baseRecordResource';
|
||||
|
||||
export class SQLResource<TData = any> extends BaseRecordResource<TData> {
|
||||
protected _data = observable.ref<TData>(null);
|
||||
protected _meta = observable.ref<Record<string, any>>({});
|
||||
private refreshTimer: NodeJS.Timeout | null = null;
|
||||
|
||||
// 请求配置 - 与 APIClient 接口保持一致
|
||||
protected request = {
|
||||
// url: null as string | null,
|
||||
method: 'post' as string,
|
||||
params: {} as Record<string, any>,
|
||||
data: {} as Record<string, any>,
|
||||
headers: {} as Record<string, any>,
|
||||
};
|
||||
|
||||
protected buildURL(action?: string): string {
|
||||
return `flowSql:${action || 'run'}`;
|
||||
}
|
||||
|
||||
setType(type: 'selectRows' | 'selectRow' | 'selectVar') {
|
||||
this.request.data.type = type;
|
||||
return this;
|
||||
}
|
||||
|
||||
setFilterByTk(filterByTk: any) {
|
||||
this.request.data.uid = filterByTk;
|
||||
return this;
|
||||
}
|
||||
|
||||
setFilter(filter: Record<string, any>) {
|
||||
this.request.data.filter = filter;
|
||||
return this;
|
||||
}
|
||||
|
||||
setSQL(sql: string) {
|
||||
this.request.data.sql = sql;
|
||||
return this;
|
||||
}
|
||||
|
||||
setBind(bind: Record<string, any> | Array<any>) {
|
||||
this.request.data.bind = bind;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在同一个事件循环内多次调用 refresh 方法时,只有最后一次调用会生效。避免触发多次相同的接口请求。
|
||||
* @returns
|
||||
*/
|
||||
async refresh(): Promise<void> {
|
||||
// 清除之前的定时器,确保只有最后一次调用生效
|
||||
if (this.refreshTimer) {
|
||||
clearTimeout(this.refreshTimer);
|
||||
}
|
||||
|
||||
// 设置新的定时器,在下一个事件循环执行
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
this.refreshTimer = setTimeout(async () => {
|
||||
try {
|
||||
this.loading = true;
|
||||
const { data, meta } = await this.runAction<TData, any>('run', {
|
||||
method: 'post',
|
||||
...this.getRefreshRequestOptions(),
|
||||
});
|
||||
this.setData(data).setMeta(meta);
|
||||
this.emit('refresh');
|
||||
this.loading = false;
|
||||
resolve();
|
||||
} catch (error) {
|
||||
this.loading = false;
|
||||
reject(error instanceof Error ? error : new Error(String(error)));
|
||||
} finally {
|
||||
this.refreshTimer = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ export class PluginCollectionSQLServer extends Plugin {
|
||||
this.app.acl.allow('flowSql', 'run', 'loggedIn');
|
||||
this.app.resourceManager.registerActionHandlers({
|
||||
'flowSql:run': async (ctx, next) => {
|
||||
const { uid, sql, type, filter, bind } = ctx.action.params.values || {};
|
||||
const { uid, sql, type = 'selectRows', filter, bind } = ctx.action.params.values || {};
|
||||
if (!uid) {
|
||||
ctx.throw(400, 'UID is required');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user