领域模型DSL设计与解析引擎技术文档
一、概述
在当前工程中,model
和 docs
文件夹承担了领域模型DSL(领域特定语言)设计与解析引擎的核心功能。领域模型DSL允许开发者以特定的、易于理解的语言来描述业务领域的模型,而解析引擎则负责将这些描述转换为可执行的代码或数据结构。本技术文档将详细介绍这两个文件夹中代码的功能、实现原理以及如何使用。
二、文件结构与功能概述
2.1 model
文件夹
model
文件夹包含了多个子文件夹和文件,用于定义不同领域的模型以及相关的项目配置。以下是主要的文件和功能:
model/index.js
:核心解析引擎,负责遍历所有模型和项目配置文件,将项目配置继承自模型配置,并返回最终的模型列表。model/buiness
和model/course
等子文件夹 :不同领域的模型定义,每个子文件夹下包含model.js
定义通用模型,以及project
子文件夹下的具体项目配置。
2.2 docs
文件夹
docs
文件夹包含了与领域模型相关的文档和示例,如 dashboard-model.js
定义了仪表盘模型的结构,为开发者提供了清晰的模型定义规范。
三、核心功能实现
3.1 模型配置继承
在 model/index.js
中,实现了项目配置继承模型配置的功能。核心函数 projectExtendModel
用于处理配置的合并:
js
const glob = require("glob");
const path = require("path");
const { sep } = path;
const _ = require("lodash");
/**
* 继承 model 配置
* @param {*} model 模型配置
* @param {*} project 项目配置
*/
const projectExtendModel = (model, project) => {
return _.mergeWith({}, model, project, (modelValue, projectValue) => {
// 处理数据合并的特殊情况
if (Array.isArray(modelValue) && Array.isArray(projectValue)) {
let result = [];
// project 继承 model, 所以需要处理修改和新增的内容情况 [menu中的值比对]
// project中有键值,model也有 => 修改(重载) 递归调用 projectExtendModel 方法覆盖
// project中没有键值,model中有键值 => 保留(继承)
// project中有键值,model没有 => 新增(拓展)
// 处理修改和保留
for (let i = 0; i < modelValue.length; i++) {
let modelItem = modelValue[i];
const projItem = projectValue.find(
(projItem) => projItem.key === modelItem.key
);
// project中存在的key,model中也存在,则递归调用 projectExtendModel 方法覆盖修改
result.push(
projItem ? projectExtendModel(modelItem, projItem) : modelItem
);
}
// 处理新增
for (let i = 0; i < projectValue.length; i++) {
let projItem = projectValue[i];
const modelItem = modelValue.find(
(modelItem) => modelItem.key === projItem.key
);
if (!modelItem) {
result.push(projItem);
}
}
return result;
}
});
};
3.2 模型解析与整理
model/index.js
中的 module.exports
函数负责遍历所有模型和项目配置文件,将它们解析并整理成最终的模型列表:
js
/**
* 解析 model 模型配置,并返回 menu 菜单配置
* @example return
* [
{
model: { model: 'dashboard', name: '电商系统', menu: [Array], key: 'buiness' },
project: { echomarket: [Object], shopnest: [Object] }
},
{
model: { model: 'dashboard', name: '教育系统', menu: [Array], key: 'course' },
project: { learnly: [Object], scholarly: [Object] }
}
]
*/
module.exports = (app) => {
const modelList = [];
// 遍历当前文件夹,构造模型数据结构,挂载到modelList上
const modelPath = path.resolve(app.baseDir, `.${sep}model`);
const fileList = glob.sync(path.resolve(modelPath, `.${sep}**${sep}**.js`));
fileList.forEach((file) => {
if (file.indexOf("index.js") > -1) return;
// 区分配置类型 [model / project]
const type = file.indexOf(`project`) > -1 ? "project" : "model";
if (type === "project") {
const modelKey = file.match(/\/model\/(.*?)\/project/)?.[1];
const projKey = file.match(/\/project\/(.*?)\.js/)?.[1];
let modelItem = modelList.find((item) => item.model?.key === modelKey);
if (!modelItem) {
//初始化 model 数据结构
modelItem = {};
modelList.push(modelItem);
}
if (!modelItem.project) {
//初始化 project 数据结构
modelItem.project = {};
}
modelItem.project[projKey] = require(path.resolve(file));
modelItem.project[projKey].key = projKey; // 注入 projectKey
}
if (type === "model") {
const modelKey = file.match(/\/model\/(.*?)\/model\.js/)?.[1];
let modelItem = modelList.find((item) => item.model?.key === modelKey);
if (!modelItem) {
//初始化 model 数据结构
modelItem = {};
modelList.push(modelItem);
}
modelItem.model = require(path.resolve(file));
modelItem.model.key = modelKey; // 注入 modelKey
}
});
// 整理 project => 继承 model
modelList.forEach((item) => {
const { model, project } = item;
for (const key in project) {
project[key] = projectExtendModel(model, project[key]);
}
});
return modelList;
};
3.3 模型定义规范
docs/dashboard-model.js
提供了仪表盘模型的定义规范,开发者可以根据这个规范来定义自己的模型:
js
{
mode: "dashboard", // 模板类型,不同模板类型对应不一样的模板数据结构
name: "", // 模板名称
desc: "", // 模板描述
icon: "", // 模板图标
homePage: "", // 模板首页地址
menu: [
{
key: "", // 菜单key
name: "", // 菜单名称
menuType: "", // 菜单类型 枚举值:group / module
// 当 menuType === group 可配置子菜单
submenu: [
{
// 可递归 submenuItem
},
],
// 当 menuType === module 可配置模块信息
moduleType: "", // 模块类型 枚举值:sider / iframe / custom / schema
// 当 moduleType === sider 可配置 侧边栏信息
siderConfig: {
menu: [
{
// 可递归 sidermenuItem
},
],
}, // 侧边栏地址
// 当 moduleType === iframe 可配置 iframe 信息
iframeConfig: {
path: "", //iframe 路径
}, // iframe 地址
// 当 moduleType === cutom 可配置 自定义模块信息
customConfig: {
path: "", // 自定义路由路径
}, // 自定义模块地址
// 当 moduleType === schema 可配置 schema 信息
schemaConfig: {
api: "", // 数据源API[遵循 RESTFUL 规范]
schema: {
// 板块数据结构
type: "object",
properties: {
key: {
...schema, // 标准的 schema 配置
type: "", //字段类型
label: "", //字段中文名
},
},
},
tableConfig: {}, // table 表格配置
searchConfig: {}, // search-bar 搜索相关配置
components: {}, // 模块组件
},
},
];
}
四、使用方法
4.1 定义模型
开发者可以在 model
文件夹下的不同子文件夹中定义模型和项目配置。例如,在 model/buiness
文件夹中:
model.js
定义通用的业务模型:
js
module.exports = {
model: "dashboard",
name: "电商系统",
menu: [
{
key: "product",
name: "商品管理",
menuType: "module",
moduleType: "custom",
customConfig: {
path: "/todo",
},
},
// 其他菜单配置...
],
};
project
子文件夹下的具体项目配置:
js
module.exports = {
name: "口碑商城",
desc: "一家非常注重用户评价的购物商城",
homePage: "",
menu: [
{
key: "operating",
name: "运营活动",
menuType: "module",
moduleType: "sider",
siderConfig: {
menu: [
{
key: "coupon",
name: "优惠券",
menuType: "module",
customConfig: {
path: "/todo",
},
},
// 其他侧边栏菜单配置...
],
},
},
],
};
4.2 解析数据
通过 model/index.js
解析引擎,对model/buiness
电商系统和model/course
教育系统2个模拟板块项目进行解析, 获得约定模型数据源:
4.2.1 模型数据结构
js
[
{
model: { model: 'dashboard', name: '电商系统', menu: [Array], key: 'buiness' },
project: { echomarket: [Object], shopnest: [Object] }
},
{
model: { model: 'dashboard', name: '教育系统', menu: [Array], key: 'course' },
project: { learnly: [Object], scholarly: [Object] }
}
]
4.2.2 模型数据源
json
[
{
"model": {
"model": "dashboard",
"name": "电商系统",
"menu": [
{
"key": "product",
"name": "商品管理",
"menuType": "module",
"moduleType": "custom",
"customConfig": {
"path": "/todo"
}
},
{
"key": "order",
"name": "订单管理",
"menuType": "module",
"moduleType": "custom",
"customConfig": {
"path": "/todo"
}
},
{
"key": "client",
"name": "客户管理",
"menuType": "module",
"moduleType": "custom",
"customConfig": {
"path": "/todo"
}
}
],
"key": "buiness"
},
"project": {
"echomarket": {
"model": "dashboard",
"name": "口碑商城",
"menu": [
{
"key": "product",
"name": "商品管理",
"menuType": "module",
"moduleType": "custom",
"customConfig": {
"path": "/todo"
}
},
{
"key": "order",
"name": "订单管理",
"menuType": "module",
"moduleType": "custom",
"customConfig": {
"path": "/todo"
}
},
{
"key": "client",
"name": "客户管理",
"menuType": "module",
"moduleType": "custom",
"customConfig": {
"path": "/todo"
}
},
{
"key": "operating",
"name": "运营活动",
"menuType": "module",
"moduleType": "sider",
"siderConfig": {
"menu": [
{
"key": "coupon",
"name": "优惠券",
"menuType": "module",
"customConfig": {
"path": "/todo"
}
},
{
"key": "limited",
"name": "限量购",
"menuType": "module",
"customConfig": {
"path": "/todo"
}
},
{
"key": "festival",
"name": "节日活动",
"menuType": "module",
"customConfig": {
"path": "/todo"
}
}
]
}
}
],
"key": "echomarket",
"desc": "一家非常注重用户评价的购物商城",
"homePage": ""
},
"shopnest": {
"model": "dashboard",
"name": "购物巢穴",
"menu": [
{
"key": "product",
"name": "商品清单",
"menuType": "module",
"moduleType": "custom",
"customConfig": {
"path": "/todo"
}
},
{
"key": "order",
"name": "订单管理",
"menuType": "module",
"moduleType": "custom",
"customConfig": {
"path": "/todo"
}
},
{
"key": "client",
"name": "客户清单",
"menuType": "module",
"moduleType": "custom",
"customConfig": {
"path": "/todo"
}
},
{
"key": "search",
"name": "信息查询",
"moduleType": "iframe",
"iframeConfig": {
"path": "https://www.taobao.com"
}
},
{
"key": "data",
"name": "数据分析",
"menuType": "module",
"moduleType": "sider",
"sideConfig": {
"menu": [
{
"key": "analysis",
"name": "电商罗盘",
"menuType": "module",
"moduleType": "custom",
"customConfig": {
"path": "/todo"
}
},
{
"key": "sider-search",
"name": "信息查询",
"moduleType": "iframe",
"iframeConfig": {
"path": "https://www.baidu.com"
}
}
]
}
}
],
"key": "shopnest",
"desc": "一个注入像家一样温馨购物环境的超市",
"homePage": ""
}
}
},
{
"model": {
"model": "dashboard",
"name": "教育系统",
"menu": [
{
"key": "video",
"name": "视频管理",
"menuType": "module",
"moduleType": "custom",
"customConfig": {
"path": "/todo"
}
},
{
"key": "user",
"name": "用户管理",
"menuType": "module",
"moduleType": "custom",
"customConfig": {
"path": "/todo"
}
}
],
"key": "course"
},
"project": {
"learnly": {
"model": "dashboard",
"name": "现代教育",
"menu": [
{
"key": "video",
"name": "视频管理",
"menuType": "module",
"moduleType": "custom",
"customConfig": {
"path": "/todo"
}
},
{
"key": "user",
"name": "用户管理",
"menuType": "module",
"moduleType": "custom",
"customConfig": {
"path": "/todo"
}
},
{
"key": "traffic",
"name": "流量管理",
"menuType": "module",
"moduleType": "sider",
"siderConfig": {
"menu": [
{
"key": "user-traffic",
"name": "学员流量",
"menuType": "module",
"moduleType": "custom",
"customConfig": {
"path": "/todo"
}
}
]
}
}
],
"key": "learnly",
"desc": "一家前沿现代教育平台系统",
"homePage": ""
},
"scholarly": {
"model": "dashboard",
"name": "学者教育",
"menu": [
{
"key": "video",
"name": "视频管理",
"menuType": "module",
"moduleType": "custom",
"customConfig": {
"path": "/todo"
}
},
{
"key": "user",
"name": "用户管理",
"menuType": "module",
"moduleType": "custom",
"customConfig": {
"path": "/todo"
}
},
{
"key": "coursematerials",
"name": "课程资料",
"menuType": "module",
"moduleType": "sider",
"siderConfig": {
"menu": [
{
"key": "pdf",
"name": "PDF",
"menuType": "module",
"moduleType": "custom",
"customConfig": {
"path": "/todo"
}
},
{
"key": "excel",
"name": "EXCEL",
"menuType": "module",
"moduleType": "custom",
"customConfig": {
"path": "/todo"
}
},
{
"key": "ppt",
"name": "PPT",
"menuType": "module",
"moduleType": "custom",
"customConfig": {
"path": "/todo"
}
},
{
"key": "word",
"name": "WORD",
"menuType": "module",
"moduleType": "custom",
"customConfig": {
"path": "/todo"
}
}
]
}
}
],
"key": "scholarly",
"desc": "一家学术类教育平台系统",
"homePage": ""
}
}
}
]
4.3 参考文档
开发者可以参考 docs/dashboard-model.js
中的模型定义规范,确保自己定义的模型符合要求。
五、总结
通过 model
和 docs
文件夹的设计,我们实现了一个灵活的领域模型DSL设计与解析引擎。开发者可以方便地定义不同领域的模型和项目配置,并且通过解析引擎将这些配置转换为可使用的模型列表。同时,文档提供了清晰的模型定义规范,帮助开发者更好地理解和使用这个系统。