模块系统
将相关功能打包成可重用的模块,轻松在不同项目间分享和部署。Entity Engine 的模块可以包含模型定义、视图配置、自定义组件和初始数据。
创建基础模块
最简单的模块只需要实现三个方法:配置设置、组件设置和数据设置。
import { IEntityModule } from '@scenemesh/entity-engine';
export const UserModule: IEntityModule = {
info: {
name: 'UserModule',
description: '用户管理模块',
provider: 'MyCompany',
version: '1.0.0'
},
async setupConfig(args) {
// 注册用户相关的模型
args.models.push({
name: 'User',
title: '用户',
fields: [
{ name: 'id', title: 'ID', type: 'string', isRequired: true },
{ name: 'name', title: '姓名', type: 'string', isRequired: true },
{ name: 'email', title: '邮箱', type: 'string', isRequired: true }
]
});
// 注册用户视图
args.views.push({
modelName: 'User',
name: 'UserListView',
title: '用户列表',
type: 'GridView'
});
},
async setupComponents(args) {
// 注册自定义组件(可选)
// args.views.push(customViews);
// args.renderers.push(customRenderers);
},
async setupData(args) {
// 提供初始数据(可选)
args.entities.push({
id: 'admin-user',
modelName: 'User',
values: {
name: '管理员',
email: 'admin@example.com'
}
});
}
};
注册模块
创建模块后需要向引擎注册:
import { getEntityEngine } from './lib/entity-engine';
import { UserModule } from './modules/user-module';
const engine = await getEntityEngine();
// 直接注册模块对象
const modules = await engine.moduleRegistry.registerModule(UserModule);
console.log('已注册模块:', modules?.[0]?.info.name);
// 注册后模块的配置会自动生效
const userModel = await engine.metaRegistry.getModel('User');
console.log('用户模型已可用:', !!userModel);
也支持从 NPM 包动态加载:
// 从 NPM 包加载模块(客户端)
await engine.moduleRegistry.registerModule('@company/user-module', true);
// 从本地路径加载(服务端)
await engine.moduleRegistry.registerModule('./modules/blog-module');
完整模块示例
一个包含所有功能的完整模块:
export const BlogModule: IEntityModule = {
info: {
name: 'BlogModule',
description: '博客内容管理系统',
provider: 'MyCompany',
version: '2.1.0',
url: 'https://example.com/blog-module',
dependencies: ['@company/rich-text-module']
},
async setupConfig(args) {
// 博客文章模型
args.models.push({
name: 'BlogPost',
title: '博客文章',
description: '博客文章内容管理',
fields: [
{ name: 'id', title: 'ID', type: 'string', isRequired: true },
{ name: 'title', title: '标题', type: 'string', isRequired: true },
{ name: 'content', title: '内容', type: 'richtext', isRequired: true },
{ name: 'status', title: '状态', type: 'enum',
options: ['draft', 'published'], defaultValue: 'draft' },
{ name: 'publishedAt', title: '发布时间', type: 'datetime' }
]
});
// 博客分类模型
args.models.push({
name: 'BlogCategory',
title: '博客分类',
fields: [
{ name: 'id', title: 'ID', type: 'string', isRequired: true },
{ name: 'name', title: '分类名', type: 'string', isRequired: true },
{ name: 'slug', title: '标识符', type: 'string', isRequired: true }
]
});
// 文章编辑视图
args.views.push({
modelName: 'BlogPost',
name: 'BlogPostFormView',
title: '文章编辑',
type: 'FormView',
config: {
fields: ['title', 'content', 'status'],
submitAction: 'save'
}
});
// 文章列表视图
args.views.push({
modelName: 'BlogPost',
name: 'BlogPostGridView',
title: '文章列表',
type: 'GridView',
config: {
columns: ['title', 'status', 'publishedAt'],
filters: ['status'],
sorting: true
}
});
// 自定义动作处理器
args.actionHandlers.push({
name: 'publishPost',
async handle(context) {
await context.datasource.updateObject({
modelName: 'BlogPost',
id: context.objectId,
values: {
status: 'published',
publishedAt: new Date()
}
});
return { success: true, message: '文章已发布' };
}
});
// API 端点
args.servlets.push({
path: '/api/blog/stats',
methods: ['GET'],
async handle(req, res) {
const stats = await getBlogStats();
res.json(stats);
}
});
},
async setupComponents(args) {
// 动态导入自定义视图组件
const BlogEditorView = await import('./components/BlogEditorView');
args.views.push(new BlogEditorView.default());
// 自定义渲染器
const RichTextRenderer = await import('./renderers/RichTextRenderer');
args.renderers.push(RichTextRenderer.default);
// 自定义小部件
const BlogStatsWidget = await import('./widgets/BlogStatsWidget');
args.widgets.push(BlogStatsWidget.default);
},
async setupData(args) {
// 默认分类数据
args.entities.push({
id: 'tech-category',
modelName: 'BlogCategory',
values: {
name: '技术',
slug: 'technology'
}
});
args.entities.push({
id: 'life-category',
modelName: 'BlogCategory',
values: {
name: '生活',
slug: 'life'
}
});
// 示例文章
args.entities.push({
id: 'welcome-post',
modelName: 'BlogPost',
values: {
title: '欢迎使用博客模块',
content: '<p>这是一篇示例文章...</p>',
status: 'published',
publishedAt: new Date()
},
references: [{
fromFieldName: 'category',
toObjectId: 'tech-category'
}]
});
}
};
模块管理
查看已注册的模块
// 获取特定模块
const blogModule = engine.moduleRegistry.getModule('BlogModule');
if (blogModule) {
console.log('模块信息:', blogModule.info);
console.log('版本:', blogModule.info.version);
console.log('提供者:', blogModule.info.provider);
}
// 获取所有模块
const allModules = engine.moduleRegistry.getAllModules();
console.log('已注册模块数量:', allModules.length);
allModules.forEach(module => {
console.log(`- ${module.info.name} v${module.info.version}`);
});
检查模块依赖
// 验证模块依赖是否满足
const checkDependencies = (module: IEntityModule) => {
if (!module.info.dependencies) return true;
return module.info.dependencies.every(dep => {
const isRegistered = engine.moduleRegistry.getModule(dep);
if (!isRegistered) {
console.warn(`缺少依赖模块: ${dep}`);
return false;
}
return true;
});
};
if (checkDependencies(BlogModule)) {
await engine.moduleRegistry.registerModule(BlogModule);
console.log('✅ 博客模块注册成功');
} else {
console.error('❌ 模块依赖不满足');
}
模块开发最佳实践
模块信息规范
// 推荐的模块信息格式
const moduleInfo = {
name: 'CompanyBlogModule', // 唯一标识符,建议加前缀
description: '企业博客内容管理系统', // 清晰描述功能
provider: 'CompanyName', // 开发者或公司名称
version: '1.2.3', // 遵循语义化版本
url: 'https://docs.company.com/blog-module', // 文档地址
dependencies: [ // 明确声明依赖
'@company/rich-text-module@^2.0.0',
'@company/image-gallery@^1.5.0'
]
};
模块文件结构
// 推荐的模块文件组织结构
my-module/
├── index.ts // 模块主文件
├── models/ // 模型定义
│ ├── user.model.ts
│ └── profile.model.ts
├── views/ // 视图配置
│ ├── user-form.view.ts
│ └── user-grid.view.ts
├── components/ // 自定义组件
│ ├── UserEditor.tsx
│ └── UserProfile.tsx
├── data/ // 初始数据
│ └── seed-data.ts
└── package.json // NPM 包配置
模块测试
// 模块功能测试
describe('UserModule', () => {
let engine: EntityEngine;
beforeEach(async () => {
engine = await createTestEngine();
});
it('应该成功注册模块', async () => {
const modules = await engine.moduleRegistry.registerModule(UserModule);
expect(modules).toHaveLength(1);
expect(modules?.[0].info.name).toBe('UserModule');
});
it('应该注册所有模型', async () => {
await engine.moduleRegistry.registerModule(UserModule);
const userModel = await engine.metaRegistry.getModel('User');
expect(userModel).toBeDefined();
expect(userModel?.fields).toHaveLength(3);
});
it('应该提供初始数据', async () => {
await engine.moduleRegistry.registerModule(UserModule);
const users = await engine.datasourceFactory
.create('User')
.listObjects({ modelName: 'User' });
expect(users.items).toHaveLength(1);
expect(users.items[0].name).toBe('管理员');
});
});
环境适配
// 根据环境调整模块行为
export const createUserModule = (env: 'development' | 'production'): IEntityModule => ({
info: {
name: 'UserModule',
description: '用户管理模块',
provider: 'MyCompany',
version: '1.0.0'
},
async setupConfig(args) {
// 生产环境和开发环境使用不同的模型配置
const userModel = {
name: 'User',
title: '用户',
fields: [
{ name: 'id', title: 'ID', type: 'string', isRequired: true },
{ name: 'name', title: '姓名', type: 'string', isRequired: true },
{ name: 'email', title: '邮箱', type: 'string', isRequired: true },
// 开发环境添加调试字段
...(env === 'development' ? [
{ name: 'debugInfo', title: '调试信息', type: 'json' }
] : [])
]
};
args.models.push(userModel);
},
async setupData(args) {
// 只在开发环境添加测试数据
if (env === 'development') {
args.entities.push({
id: 'test-user',
modelName: 'User',
values: {
name: '测试用户',
email: 'test@example.com'
}
});
}
}
});
// 使用
const userModule = createUserModule(process.env.NODE_ENV === 'production' ? 'production' : 'development');
await engine.moduleRegistry.registerModule(userModule);
Last updated on