实体模型
定义应用的数据结构和业务规则。实体模型告诉 Entity Engine 您的数据长什么样子,有哪些字段,以及如何验证和处理这些数据。
创建第一个模型
最简单的方式是定义一个包含基本字段的用户模型。每个模型都需要一个名称、标题,以及字段列表:
import { getEntityEngine } from './lib/entity-engine';
const engine = await getEntityEngine();
// 注册用户模型
await engine.metaRegistry.updateOrRegister({
name: 'User', // 模型名称,用于代码中引用
title: '用户', // 显示名称,用于UI界面
fields: [
{
name: 'name', // 字段名称
title: '姓名', // 字段显示名称
type: 'string', // 字段类型
isRequired: true // 是否必填
},
{
name: 'email',
title: '邮箱',
type: 'string',
isRequired: true
},
{
name: 'age',
title: '年龄',
type: 'number',
isRequired: false // 可选字段
}
]
});
注册完成后,您就可以使用这个模型进行数据操作了。Entity Engine 会根据模型定义自动处理数据验证、存储和查询。
字段类型详解
基础字段类型
Entity Engine 支持多种字段类型,每种类型都有不同的验证规则和显示方式。选择正确的字段类型可以确保数据的准确性和用户体验。
const productModel = {
name: 'Product',
title: '产品',
fields: [
// 文本字段 - 存储短文本内容
{
name: 'name',
title: '产品名称',
type: 'string',
isRequired: true
},
// 数字字段 - 存储数值
{
name: 'price',
title: '价格',
type: 'number',
isRequired: true
},
// 布尔字段 - 存储真/假值
{
name: 'isActive',
title: '是否启用',
type: 'boolean',
defaultValue: true // 默认值
},
// 日期字段 - 存储日期和时间
{
name: 'createdAt',
title: '创建时间',
type: 'datetime',
defaultValue: 'now' // 使用当前时间作为默认值
},
// 长文本字段 - 存储多行文本
{
name: 'description',
title: '产品描述',
type: 'text'
}
]
};
await engine.metaRegistry.updateOrRegister(productModel);
关联字段类型
当您需要在不同的数据之间建立关系时,可以使用关联字段。这让您可以轻松地查询相关数据。
// 先定义分类模型
await engine.metaRegistry.updateOrRegister({
name: 'Category',
title: '分类',
fields: [
{
name: 'name',
title: '分类名称',
type: 'string',
isRequired: true
}
]
});
// 然后在产品模型中引用分类
await engine.metaRegistry.updateOrRegister({
name: 'Product',
title: '产品',
fields: [
{
name: 'name',
title: '产品名称',
type: 'string',
isRequired: true
},
// 多对一关联 - 每个产品属于一个分类
{
name: 'category',
title: '所属分类',
type: 'many_to_one',
refModel: 'Category', // 引用的模型名称
isRequired: true
}
]
});
这样设置后,您可以在查询产品时自动包含分类信息:
// 查询产品并包含分类信息
const products = await engine.datasource.listObjects({
modelName: 'Product',
include: {
category: true // 自动加载关联的分类数据
}
});
// 结果会包含完整的分类信息
products.items.forEach(product => {
console.log(`${product.name} - 分类: ${product.category.name}`);
});
一对多关联
当一个记录需要关联多个相关记录时,使用一对多关联:
// 文章模型
await engine.metaRegistry.updateOrRegister({
name: 'Post',
title: '文章',
fields: [
{
name: 'title',
title: '标题',
type: 'string',
isRequired: true
},
{
name: 'content',
title: '内容',
type: 'text'
}
]
});
// 评论模型 - 多个评论属于一篇文章
await engine.metaRegistry.updateOrRegister({
name: 'Comment',
title: '评论',
fields: [
{
name: 'content',
title: '评论内容',
type: 'text',
isRequired: true
},
{
name: 'post',
title: '所属文章',
type: 'many_to_one',
refModel: 'Post',
isRequired: true
}
]
});
查询时可以包含所有相关的评论:
// 查询文章并包含所有评论
const posts = await engine.datasource.listObjects({
modelName: 'Post',
include: {
comments: true // 自动加载所有相关评论
}
});
字段验证
设置字段约束
您可以为字段设置各种约束条件,确保数据的完整性和正确性:
await engine.metaRegistry.updateOrRegister({
name: 'User',
title: '用户',
fields: [
{
name: 'username',
title: '用户名',
type: 'string',
isRequired: true,
unique: true, // 确保用户名唯一
minLength: 3, // 最小长度
maxLength: 20 // 最大长度
},
{
name: 'email',
title: '邮箱',
type: 'string',
isRequired: true,
unique: true,
pattern: '^[^@]+@[^@]+\\.[^@]+$' // 邮箱格式验证
},
{
name: 'age',
title: '年龄',
type: 'number',
min: 0, // 最小值
max: 150 // 最大值
}
]
});
自定义验证规则
对于更复杂的验证需求,您可以定义自定义验证函数:
await engine.metaRegistry.updateOrRegister({
name: 'Order',
title: '订单',
fields: [
{
name: 'orderNumber',
title: '订单号',
type: 'string',
isRequired: true,
validate: (value) => {
// 订单号必须以 "ORD-" 开头,后跟8位数字
const pattern = /^ORD-\d{8}$/;
if (!pattern.test(value)) {
throw new Error('订单号格式无效,应为 ORD-12345678');
}
return true;
}
},
{
name: 'amount',
title: '订单金额',
type: 'number',
isRequired: true,
validate: (value) => {
if (value <= 0) {
throw new Error('订单金额必须大于0');
}
if (value > 100000) {
throw new Error('单次订单金额不能超过10万元');
}
return true;
}
}
]
});
当用户尝试创建或更新数据时,这些验证规则会自动执行:
try {
await engine.datasource.createObject({
modelName: 'Order',
values: {
orderNumber: 'INVALID', // 这会触发验证错误
amount: 1000
}
});
} catch (error) {
console.error('验证失败:', error.message); // "订单号格式无效,应为 ORD-12345678"
}
默认值和计算字段
设置默认值
您可以为字段设置默认值,这样在创建新记录时会自动填充:
await engine.metaRegistry.updateOrRegister({
name: 'Article',
title: '文章',
fields: [
{
name: 'title',
title: '标题',
type: 'string',
isRequired: true
},
{
name: 'status',
title: '状态',
type: 'string',
defaultValue: 'draft' // 新文章默认为草稿状态
},
{
name: 'createdAt',
title: '创建时间',
type: 'datetime',
defaultValue: 'now' // 使用当前时间
},
{
name: 'viewCount',
title: '浏览次数',
type: 'number',
defaultValue: 0 // 初始浏览次数为0
}
]
});
计算字段
计算字段的值根据其他字段动态计算得出:
await engine.metaRegistry.updateOrRegister({
name: 'OrderItem',
title: '订单明细',
fields: [
{
name: 'quantity',
title: '数量',
type: 'number',
isRequired: true
},
{
name: 'unitPrice',
title: '单价',
type: 'number',
isRequired: true
},
{
name: 'totalPrice',
title: '总价',
type: 'number',
computed: true, // 标记为计算字段
compute: (record) => { // 计算逻辑
return record.quantity * record.unitPrice;
}
}
]
});
计算字段会在数据保存时自动更新:
const orderItem = await engine.datasource.createObject({
modelName: 'OrderItem',
values: {
quantity: 3,
unitPrice: 299.99
// totalPrice 会自动计算为 899.97
}
});
console.log(orderItem.totalPrice); // 899.97
模型权限设置
字段级权限
您可以为不同的字段设置不同的访问权限:
await engine.metaRegistry.updateOrRegister({
name: 'User',
title: '用户',
fields: [
{
name: 'name',
title: '姓名',
type: 'string',
isRequired: true
// 所有用户都可以读写
},
{
name: 'email',
title: '邮箱',
type: 'string',
isRequired: true,
readable: ['admin', 'self'], // 只有管理员和本人可以读取
writable: ['admin', 'self'] // 只有管理员和本人可以修改
},
{
name: 'salary',
title: '薪资',
type: 'number',
readable: ['admin'], // 只有管理员可以读取
writable: ['admin'] // 只有管理员可以修改
}
]
});
记录级权限
您也可以设置整个记录的访问权限:
await engine.metaRegistry.updateOrRegister({
name: 'PrivateDocument',
title: '私人文档',
fields: [
{
name: 'title',
title: '标题',
type: 'string',
isRequired: true
},
{
name: 'content',
title: '内容',
type: 'text'
},
{
name: 'owner',
title: '所有者',
type: 'many_to_one',
refModel: 'User',
isRequired: true
}
],
// 记录级权限规则
permissions: {
read: (record, user) => {
// 只有所有者和管理员可以读取
return record.owner.id === user.userId || user.roles.includes('admin');
},
write: (record, user) => {
// 只有所有者可以修改
return record.owner.id === user.userId;
},
delete: (record, user) => {
// 只有所有者和管理员可以删除
return record.owner.id === user.userId || user.roles.includes('admin');
}
}
});
模型继承
创建基础模型
当多个模型有相似的字段时,可以使用继承来减少重复定义:
// 基础内容模型
await engine.metaRegistry.updateOrRegister({
name: 'BaseContent',
title: '基础内容',
abstract: true, // 抽象模型,不能直接创建记录
fields: [
{
name: 'title',
title: '标题',
type: 'string',
isRequired: true
},
{
name: 'content',
title: '内容',
type: 'text'
},
{
name: 'createdAt',
title: '创建时间',
type: 'datetime',
defaultValue: 'now'
},
{
name: 'author',
title: '作者',
type: 'many_to_one',
refModel: 'User',
isRequired: true
}
]
});
继承基础模型
其他模型可以继承基础模型的所有字段,然后添加自己特有的字段:
// 文章模型继承基础内容
await engine.metaRegistry.updateOrRegister({
name: 'Article',
title: '文章',
inherits: 'BaseContent', // 继承基础内容模型
fields: [
// 继承了 title, content, createdAt, author 字段
// 添加文章特有的字段
{
name: 'category',
title: '分类',
type: 'many_to_one',
refModel: 'Category'
},
{
name: 'publishedAt',
title: '发布时间',
type: 'datetime'
}
]
});
// 新闻模型也继承基础内容
await engine.metaRegistry.updateOrRegister({
name: 'News',
title: '新闻',
inherits: 'BaseContent',
fields: [
// 添加新闻特有的字段
{
name: 'urgency',
title: '紧急程度',
type: 'string',
defaultValue: 'normal'
},
{
name: 'expiresAt',
title: '过期时间',
type: 'datetime'
}
]
});
实际应用示例
电商系统模型
这是一个完整的电商系统模型示例,展示了如何定义相互关联的多个模型:
// 用户模型
await engine.metaRegistry.updateOrRegister({
name: 'Customer',
title: '客户',
fields: [
{
name: 'name',
title: '姓名',
type: 'string',
isRequired: true
},
{
name: 'email',
title: '邮箱',
type: 'string',
isRequired: true,
unique: true
},
{
name: 'phone',
title: '电话',
type: 'string'
},
{
name: 'address',
title: '地址',
type: 'text'
}
]
});
// 商品分类
await engine.metaRegistry.updateOrRegister({
name: 'ProductCategory',
title: '商品分类',
fields: [
{
name: 'name',
title: '分类名称',
type: 'string',
isRequired: true
},
{
name: 'description',
title: '描述',
type: 'text'
}
]
});
// 商品模型
await engine.metaRegistry.updateOrRegister({
name: 'Product',
title: '商品',
fields: [
{
name: 'name',
title: '商品名称',
type: 'string',
isRequired: true
},
{
name: 'description',
title: '商品描述',
type: 'text'
},
{
name: 'price',
title: '价格',
type: 'number',
isRequired: true,
min: 0
},
{
name: 'category',
title: '分类',
type: 'many_to_one',
refModel: 'ProductCategory',
isRequired: true
},
{
name: 'stock',
title: '库存',
type: 'number',
defaultValue: 0,
min: 0
},
{
name: 'isActive',
title: '是否上架',
type: 'boolean',
defaultValue: true
}
]
});
// 订单模型
await engine.metaRegistry.updateOrRegister({
name: 'Order',
title: '订单',
fields: [
{
name: 'orderNumber',
title: '订单号',
type: 'string',
isRequired: true,
unique: true
},
{
name: 'customer',
title: '客户',
type: 'many_to_one',
refModel: 'Customer',
isRequired: true
},
{
name: 'status',
title: '订单状态',
type: 'string',
defaultValue: 'pending',
options: ['pending', 'paid', 'shipped', 'delivered', 'cancelled']
},
{
name: 'totalAmount',
title: '订单总额',
type: 'number',
isRequired: true,
min: 0
},
{
name: 'createdAt',
title: '下单时间',
type: 'datetime',
defaultValue: 'now'
}
]
});
// 订单明细模型
await engine.metaRegistry.updateOrRegister({
name: 'OrderItem',
title: '订单明细',
fields: [
{
name: 'order',
title: '所属订单',
type: 'many_to_one',
refModel: 'Order',
isRequired: true
},
{
name: 'product',
title: '商品',
type: 'many_to_one',
refModel: 'Product',
isRequired: true
},
{
name: 'quantity',
title: '数量',
type: 'number',
isRequired: true,
min: 1
},
{
name: 'unitPrice',
title: '单价',
type: 'number',
isRequired: true,
min: 0
},
{
name: 'totalPrice',
title: '小计',
type: 'number',
computed: true,
compute: (record) => record.quantity * record.unitPrice
}
]
});
使用模型进行业务操作
定义好模型后,您就可以进行各种业务操作了:
// 创建客户
const customer = await engine.datasource.createObject({
modelName: 'Customer',
values: {
name: '张三',
email: 'zhangsan@example.com',
phone: '13812345678'
}
});
// 创建商品分类
const category = await engine.datasource.createObject({
modelName: 'ProductCategory',
values: {
name: '电子产品',
description: '各类电子设备'
}
});
// 创建商品
const product = await engine.datasource.createObject({
modelName: 'Product',
values: {
name: 'iPhone 15',
description: '最新款苹果手机',
price: 6999,
category: category.id,
stock: 100
}
});
// 创建订单
const order = await engine.datasource.createObject({
modelName: 'Order',
values: {
orderNumber: 'ORD-20250101',
customer: customer.id,
status: 'pending',
totalAmount: 6999
}
});
// 创建订单明细
await engine.datasource.createObject({
modelName: 'OrderItem',
values: {
order: order.id,
product: product.id,
quantity: 1,
unitPrice: 6999
// totalPrice 会自动计算
}
});
最佳实践
模型命名规范
使用清晰、一致的命名规范可以让您的应用更易维护:
// ✅ 推荐:使用单数名词,首字母大写
const goodNames = [
'User', // 不是 Users
'Product', // 不是 Products
'OrderItem', // 复合词使用驼峰命名
'BlogPost' // 不是 Blog_Post 或 blogpost
];
// ❌ 避免:模糊或过长的名称
const badNames = [
'Data', // 太通用
'UserAccountInformation', // 太长
'u', // 太短
'user_info' // 使用下划线
];
字段设计原则
设计字段时应该考虑数据的完整性和使用便利性:
// ✅ 推荐:明确的字段定义
{
name: 'email',
title: '邮箱地址',
type: 'string',
isRequired: true,
unique: true,
pattern: '^[^@]+@[^@]+\\.[^@]+$' // 明确的验证规则
}
// ❌ 避免:模糊的字段定义
{
name: 'data', // 名称不明确
title: 'Data', // 标题没有意义
type: 'text' // 没有验证规则
}
关联关系设计
合理设计关联关系可以让数据查询更高效:
// ✅ 推荐:明确的关联关系
{
name: 'author',
title: '作者',
type: 'many_to_one',
refModel: 'User', // 明确引用的模型
isRequired: true, // 明确是否必须
onDelete: 'restrict' // 明确删除策略
}
// ❌ 避免:没有约束的关联
{
name: 'user',
type: 'many_to_one',
refModel: 'User'
// 缺少必要的约束和策略
}
现在您已经掌握了如何定义和使用实体模型。接下来可以学习如何创建用户界面视图来展示和编辑这些数据。
Last updated on