Skip to content

Commit 0460127

Browse files
committed
feat: 添加商业智能插件,支持数据集定义和查询功能,扩展插件架构
1 parent 4e85ae9 commit 0460127

File tree

7 files changed

+158
-21
lines changed

7 files changed

+158
-21
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { ManifestSchema, ObjectStackManifest } from '@objectstack/spec';
2+
3+
const BiPlugin: ObjectStackManifest = {
4+
id: 'com.objectstack.bi',
5+
name: 'Business Intelligence Plugin',
6+
version: '1.0.0',
7+
type: 'plugin',
8+
description: 'Provides BI capabilities, dataset definitions, and chart rendering.',
9+
10+
// Register Capabilities
11+
contributes: {
12+
kinds: [
13+
{
14+
id: 'bi.dataset',
15+
globs: ['**/*.dataset.json', '**/*.dataset.ts'],
16+
description: 'BI Dataset Definition'
17+
},
18+
{
19+
id: 'bi.dashboard',
20+
globs: ['**/*.bi-dash.json'],
21+
description: 'Advanced BI Dashboard'
22+
}
23+
]
24+
},
25+
26+
// Lifecycle Entry Point
27+
// in a real scenario, this would be a path or module name
28+
extensions: {
29+
runtime: {
30+
entry: './src/index.ts'
31+
}
32+
}
33+
};
34+
35+
export default BiPlugin;

examples/plugin-bi/package.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"name": "@objectstack/plugin-bi",
3+
"version": "1.0.0",
4+
"main": "src/index.ts",
5+
"license": "MIT",
6+
"scripts": {
7+
"build": "tsc"
8+
},
9+
"dependencies": {
10+
"@objectstack/spec": "workspace:*"
11+
},
12+
"devDependencies": {
13+
"typescript": "^5.0.0"
14+
}
15+
}

examples/plugin-bi/src/index.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
2+
export class BiEngine {
3+
constructor() {
4+
console.log('[BI Plugin] Engine Initialized');
5+
}
6+
7+
registerDataset(path: string) {
8+
console.log(`[BI Plugin] Registered dataset: ${path}`);
9+
}
10+
11+
runQuery(query: string) {
12+
console.log(`[BI Plugin] Running Query: ${query}`);
13+
return { result: 'Mock Data' };
14+
}
15+
}
16+
17+
/**
18+
* Plugin Lifecycle Hook
19+
* (Simulated interface)
20+
*/
21+
export async function onEnable(context: any) {
22+
console.log('[BI Plugin] Enabling BI Plugin...');
23+
24+
// Register Service
25+
const engine = new BiEngine();
26+
if (context.services) {
27+
context.services.register('bi.engine', engine);
28+
}
29+
30+
console.log('[BI Plugin] Services registered.');
31+
}

examples/plugin-bi/tsconfig.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"extends": "../../tsconfig.json",
3+
"compilerOptions": {
4+
"outDir": "./dist",
5+
"rootDir": "./src"
6+
},
7+
"include": ["src/**/*", "objectstack.config.ts"]
8+
}

examples/server/src/index.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,25 @@ app.get('/api/v1/meta/:type', (c) => {
3333
'objects': 'object',
3434
'apps': 'app',
3535
'flows': 'flow',
36-
'reports': 'report'
36+
'reports': 'report',
37+
'plugins': 'plugin',
38+
'kinds': 'kind'
3739
};
3840
const type = typeMap[typePlural] || typePlural;
3941

4042
const items = SchemaRegistry.listItems(type);
4143

4244
// Optional: Summary transformation based on type
4345
const summaries = items.map((item: any) => ({
46+
id: item.id, // Some items use ID (plugins)
4447
name: item.name,
4548
label: item.label,
49+
type: item.type,
4650
icon: item.icon,
4751
description: item.description,
4852
// Add dynamic links
4953
...(type === 'object' ? { path: `/api/v1/data/${item.name}` } : {}),
50-
self: `/api/v1/meta/${typePlural}/${item.name}`
54+
self: `/api/v1/meta/${typePlural}/${item.name || item.id}`
5155
}));
5256

5357
return c.json({ data: summaries });
@@ -66,7 +70,9 @@ app.get('/api/v1/meta/:type/:name', (c) => {
6670
'objects': 'object',
6771
'apps': 'app',
6872
'flows': 'flow',
69-
'reports': 'report'
73+
'reports': 'report',
74+
'plugins': 'plugin',
75+
'kinds': 'kind'
7076
};
7177
const type = typeMap[typePlural] || typePlural;
7278

examples/server/src/kernel/registry.ts

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,31 @@
1-
import { ServiceObject, App } from '@objectstack/spec';
1+
import { ServiceObject, App, ObjectStackManifest } from '@objectstack/spec';
22

33
/**
44
* Global Schema Registry
55
* Unified storage for all metadata types (Objects, Apps, Flows, Layouts, etc.)
66
*/
77
export class SchemaRegistry {
8-
// Nested Map: Type -> Name -> MetadataItem
8+
// Nested Map: Type -> Name/ID -> MetadataItem
99
private static metadata = new Map<string, Map<string, any>>();
1010

1111
/**
1212
* Universal Register Method
13+
* @param type The category of metadata (e.g., 'object', 'app', 'plugin')
14+
* @param item The metadata item itself
15+
* @param keyField The property to use as the unique key (default: 'name')
1316
*/
14-
static registerItem<T extends { name: string }>(type: string, item: T) {
17+
static registerItem<T>(type: string, item: T, keyField: keyof T = 'name' as keyof T) {
1518
if (!this.metadata.has(type)) {
1619
this.metadata.set(type, new Map());
1720
}
1821
const collection = this.metadata.get(type)!;
22+
const key = String(item[keyField]);
1923

20-
if (collection.has(item.name)) {
21-
console.warn(`[Registry] Overwriting ${type}: ${item.name}`);
24+
if (collection.has(key)) {
25+
console.warn(`[Registry] Overwriting ${type}: ${key}`);
2226
}
23-
collection.set(item.name, item);
24-
console.log(`[Registry] Registered ${type}: ${item.name}`);
27+
collection.set(key, item);
28+
console.log(`[Registry] Registered ${type}: ${key}`);
2529
}
2630

2731
/**
@@ -46,7 +50,7 @@ export class SchemaRegistry {
4650
* Object Helpers
4751
*/
4852
static registerObject(schema: ServiceObject) {
49-
this.registerItem('object', schema);
53+
this.registerItem('object', schema, 'name');
5054
}
5155

5256
static getObject(name: string): ServiceObject | undefined {
@@ -61,7 +65,7 @@ export class SchemaRegistry {
6165
* App Helpers
6266
*/
6367
static registerApp(app: App) {
64-
this.registerItem('app', app);
68+
this.registerItem('app', app, 'name');
6569
}
6670

6771
static getApp(name: string): App | undefined {
@@ -71,4 +75,26 @@ export class SchemaRegistry {
7175
static getAllApps(): App[] {
7276
return this.listItems<App>('app');
7377
}
78+
79+
/**
80+
* Plugin Helpers
81+
*/
82+
static registerPlugin(manifest: ObjectStackManifest) {
83+
this.registerItem('plugin', manifest, 'id');
84+
}
85+
86+
static getAllPlugins(): ObjectStackManifest[] {
87+
return this.listItems<ObjectStackManifest>('plugin');
88+
}
89+
90+
/**
91+
* Kind (Metadata Type) Helpers
92+
*/
93+
static registerKind(kind: { id: string, globs: string[] }) {
94+
this.registerItem('kind', kind, 'id');
95+
}
96+
97+
static getAllKinds(): { id: string, globs: string[] }[] {
98+
return this.listItems('kind');
99+
}
74100
}

examples/server/src/loader.ts

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,48 @@
11
import { SchemaRegistry } from './kernel/registry';
2-
import { AppSchema } from '@objectstack/spec';
2+
import { AppSchema, ManifestSchema } from '@objectstack/spec';
33

44
// In a real monorepo scenario, we might use path aliases or require.resolve
55
// Here we use relative paths to demonstrate loading from the sibling packages
66
// @ts-ignore
77
import CrmApp from '../../crm/objectstack.config';
88
// @ts-ignore
99
import TodoApp from '../../todo/objectstack.config';
10+
// @ts-ignore
11+
import BiPlugin from '../../plugin-bi/objectstack.config';
1012

1113
export function loadPlugins() {
12-
const apps = [CrmApp, TodoApp];
14+
const packages = [CrmApp, TodoApp, BiPlugin];
1315

14-
for (const app of apps) {
15-
if (!app) continue;
16+
for (const pkg of packages) {
17+
if (!pkg) continue;
1618

17-
console.log(`[Loader] Loading App: ${app.name} (${app.label})`);
19+
console.log(`[Loader] Loading Package: ${pkg.id || pkg.name} (${pkg.type || 'app'})`);
1820

21+
// Handle Plugins
22+
if (pkg.type === 'plugin') {
23+
const parsedPlugin = ManifestSchema.parse(pkg);
24+
SchemaRegistry.registerPlugin(parsedPlugin);
25+
26+
if (parsedPlugin.contributes?.kinds) {
27+
for (const kind of parsedPlugin.contributes.kinds) {
28+
SchemaRegistry.registerKind(kind);
29+
}
30+
}
31+
continue;
32+
}
33+
34+
// Handle Apps
1935
// 0. Register App
20-
const parsedApp = AppSchema.parse(app);
36+
const parsedApp = AppSchema.parse(pkg);
2137
SchemaRegistry.registerApp(parsedApp);
2238

2339
// 1. Register Objects
24-
if (app.objects) {
25-
app.objects.forEach((obj: any) => {
40+
if (pkg.objects) {
41+
pkg.objects.forEach((obj: any) => {
2642
SchemaRegistry.registerObject(obj);
2743
});
2844
}
2945

30-
console.log(`[Loader] Loaded ${app.objects?.length || 0} objects from ${app.name}`);
46+
console.log(`[Loader] Loaded ${pkg.objects?.length || 0} objects from ${pkg.name}`);
3147
}
3248
}

0 commit comments

Comments
 (0)