本教程指导您如何使用华炎魔方,创建合同管理应用,并同步到代码,通过编写触发器实现高级业务逻辑。
本教程指导您如何使用华炎魔方,创建合同管理应用,并同步到代码,通过编写触发器实现高级业务逻辑。
华炎魔方基于商业智能和模型驱动,帮助您轻松便捷地创建智能化、移动化的企业应用。华炎魔方,最适合的场景是,技术人员以此为基础进行企业业务应用系统的二次开发。
首先,需要在本地的Linux/Mac/Windows环境中安装华炎魔方低代码开放平台,以及其他一些必要的开发工具。更为简化的方式,是在线申请开通华炎魔方云服务 。
安装好华炎魔方后,就可以以此为基础进行应用开发了。这个过程包括:
我们以企业应用中常见的合同管理系统为例,一起了解如何在华炎魔方里进行配置与开发。
在华炎魔方中构建应用程序时,可以使用可视化界面管理所有的元数据。开发的第一步,就是由管理员在“设置”里,对系统进行管理,并对对象、应用等元数据进行配置。
比如合同管理,主要的对象就是合同,我们首先来配置它。
管理员,在设置》对象设置》对象,
点击新建按钮,输入合同对象的显示名、API名称等,点击保存按钮,即新建对象。
对象新建时,系统会自动给对象增加4个对象字段,而其他的字段可自行定义。
比如,我们设想的合同对象,准备设置如下字段:
序号 | 字段名 | 字段API | 类型 |
---|---|---|---|
1 | 名称 | name | 文本 |
2 | 签订日期 | signed_date | 日期 |
3 | 合同金额 | amount | 数值 |
4 | 收支类别 | bop | 选择框 |
5 | 签约对象 | othercompany | 相关表(内置对象:业务伙伴) |
6 | 合同编号 | no | 文本 |
7 | 款项已结清 | paid_all | 选择框 |
8 | 合同主要内容 | subject | 长文本 |
我们在对象的详情页面上,
点对象字段列表左上角的“新建”按钮,逐一新建所需字段。
新建和修改字段时,可以配置排序号、宽字段、必填等高级选项。
字段属性填写完毕,点击“提交”。
其他字段的建立方法也一样
在对象新建的时候,系统还会自动新建2个列表视图,
可自行修改其设置。点击“编辑”,可以按先后顺序,增加列表视图上所要显示的字段,
显示的列调整完毕,点击“提交”。
已配置的对象,可以通过对象的“预览”按钮,查看对象的列表显示、新增、详情显示等情况。
点击对象详情页的“预览”,就进入了对象的列表显示页,点击“新建“按钮
就可以新建合同了。也可以试着用下显示、编辑、删除、导出等功能。
在合同管理中,除了合同,我们还需要管理合同的付款等。在华炎魔方里,合同是一个对象,相当于表;付款则是另一个对象,相当于另一张表。而付款与合同存在密切的逻辑关系。
同一份合同,可能会有多笔付款,这里,合同是主表,付款则是子表。
在华炎魔方中,合同(主表)是一个对象,付款(子表)则是另一个对象,需要分别定义。同时,可以定义一个“主表/子表”字段来描述合同与付款两个对象之间的主子表关系。
下面,我们新建合同对象的子对象:付款。
管理员,进入设置》对象设置》对象,点击新建按钮,输入显示名、API名称等,点击保存按钮,即新建对象。
对象新建后,我们继续设定其他的字段。
按预定目标,付款对象,拟设置如下字段:
序号 | 字段名 | 字段API | 类型 |
---|---|---|---|
1 | 相关合同 | contract | 主表/子表(合同) |
2 | 计划付款日期 | due_date | 日期 |
3 | 付款金额 | amount | 数值 |
4 | 付款状态 | payment_status | 选择框 |
5 | 付款日期 | payment_date | 日期 |
6 | 付款说明 | payment_note | 长文本 |
其中的“相关合同“这个字段,就实现了合同与付款之间的主子表关系 。
在付款对象的详情页面上,点“新建”按钮逐一新建上述字段。
付款对象新建时,系统同样会自动新建2个列表视图。我们可以按先后顺序增加列表视图显示的列字段。
预览对象
已配置的付款对象,同样可以通过对象的“预览”按钮,查看列表显示、新增、详情显示等情况。
如果我们预览合同主表的记录,可以看到会多出付款的子表记录。
建立好合同、付款等2个对象后,我们可以建立自定义应用:合同了。
管理员,进入 设置》对象设置》对象,点击新建按钮,
输入应用程序的名称、API名称等,选择好桌面主菜单、手机主菜单,点击保存按钮。
建立自定义应用后,就可以进入应用,查看这个应用的具体情况了。
点击左上角的“应用程序启动器”图标,可以点击进入合同应用。
里面已经有之前在预览时录入的数据。
查看合同详情页,不但显示合同的详情,也会将作为子表的付款记录列表显示。
经过上述的配置,我们就建立起了合同管理系统的框架。具备了合同应用的基本功能,比如管理合同,建立和跟踪合同的付款记录等。
在界面配置好相应的对象及应用后,可以将这些元数据通过同步工具转换为代码,为后续的代码扩充作准备。
首先需要安装华炎魔方同步工具,安装方法和过程如下:
在Visual Studio Code中,进入 扩展页面,搜索“Steedos”,安装 “Steedos Extensions for Visual Studio Code”插件
安装后,可见“Steedos CLI Integration”也已安装好。
修改根目录下的 .env,增加以下两个参数,来实现元数据同步功能,其中METADATA_SERVER 为系统的Root的URL,METADATA_APIKEY为激活本地私有化华炎魔方自动生成的的API Key。
[metadata]
METADATA_SERVER=http://127.0.0.1:5000
METADATA_APIKEY=-D0hUDsU0-_nhonh8TKZRukDZsqQQwiLCy
修改配置文件后,需重启华炎魔方服务。
重启后,在 VS Code中,进入Steedos插件页,可以看到自定义的对象及应用等。
在 VS Code中,切换到Steedos插件,可以看到已在页面配置的元数据,包括对象、应用等
点击 自定义对象 后的 下载图标,
即可将页面配置的对象转换为对象代码。
主要包括:
***.object.yml ,对象的基本配置属性
fields文件夹,其下是每个字段对应的文件 ***.fileld.yml
listviews文件夹,其下是每个列表视图对应的文件 ***.listview.yml
点击 自定义应用 后的 下载图标,
即可将页面配置的应用转换为应用代码。
应用代码是 ***.app.yml ,应用的基本配置属性。
在上一节,我们已将页面配置的对象及应用转为了代码,下面,我们就可以通过代码来扩充业务逻辑了。
例如,在合同管理的需求中,如果合同的付款已结清,就需要将合同归档,不再开放编辑功能,也不再允许录入付款记录。在华炎魔方里,可以通过对象记录的“锁定”来实现,即将记录的字段locked赋值为 true 。
为此,我们需要做:
下面,我们分别来关注下 服务端API 和 按钮。
我们首先来建立自定义服务端API 。
华炎魔方已经内置了自定义对象的增、删、改等基本操作的API,如需进行业务逻辑扩充,推荐自定义API接口,用于接收数据、处理业务逻辑、返回结果及数据,包括:
API接口定义完成后,可调用API接口的包括:
下面,我们以合同归档的需求为例,介绍下如何创建API 。华炎魔方中,服务端API即为Router文件,由Javascript编写。
我们先在 VS Code中,打开查看下的“命令面板”,输入Steedos,选择 “Steedos:Create Router”,创建Router文件
输入文件名,例如contract_locked,即可在相应路径下创建Router文件。
contract_locked.router.js
const express = require("express");
const router = express.Router();
const core = require('@steedos/core');
const objectql = require('@steedos/objectql');
router.get('/api/test', core.requireAuthentication, async function (req, res) {
// const userSession = req.user;
// const spaceId = userSession.spaceId;
// const userId = userSession.userId;
// const isSpaceAdmin = userSession.is_space_admin;
res.status(200).send({ message: 'router ok' });
});
exports.default = router;
接下来,我们在Router文件中继续编写业务逻辑。
contract_locked.router.js
const express = require("express");
const router = express.Router();
const core = require('@steedos/core');
const objectql = require('@steedos/objectql');
router.post('/api/contract/locked/:contractId', core.requireAuthentication, async function (req, res) {
// const userSession = req.user;
// const spaceId = userSession.spaceId;
// const userId = userSession.userId;
// const isSpaceAdmin = userSession.is_space_admin;
const { contractId } = req.params;
const contract = await objectql.getObject("contracts").updateOne(contractId, {
locked: true
});
res.status(200).send({ message: '本合同已归档', success:true });
});
exports.default = router;
上面,就是 通过调用objectql.getObject().updateOne()来给合同记录的locked赋值。
关于如何查询、插入、修改、删除华炎魔方的内置/自定义对象,可参考官网相关介绍。
合同归档的需求,是在前台页面存在一个“归档”按钮。因为系统自带的默认按钮只有新增、修改等,我们需要自定义这个按钮。
在 VS Code中查看代码,在合同对象下建立文件夹“buttons”,右键后,选择“Steedos:Create Object Button”
输入按钮名,例如close,即可在buttons下创建按钮相关文件。
按钮文件包括:
close.button.yml
name: close
is_enable: true
label: close
'on': record_only
visible: true
close.button.js
module.exports = {
close: function (object_name, record_id) {
},
closeVisible: function () {
return true
}
}
按预想要求,我们修改按钮文件的内容。
close.button.xml
name: close
is_enable: true
label: 归档
'on': record_only
visible: true
close.button.js
module.exports = {
close: function (object_name, record_id) {
let url = `api/contract/locked/${record_id}`;
let options = { type: 'post', async: false };
let result = Steedos.authRequest(url, options);
console.log(result);
if (result && result.success == false) {
toastr.error(result.message);
} else if (result && result.success) {
toastr.success('归档成功。');
FlowRouter.reload();
}
},
closeVisible: function (object_name, record_id, record_permissions, record) {
if (record.locked == true) {
return false
} else {
return true
}
}
}
这里,在close函数里,调用自定义服务端API“api/contract/locked/” ;在closeVisible函数里,按locked的值判断是否显示“归档”按钮。
自定义按钮需发布到页面,才能测试确认。
在 VS Code的代码中,右键合同对象,选择“Steedos: Deploy Source”。
执行后,可以看到右侧输出提示`deploy success!`,一般来说不需要重启服务即可生效。
进入华炎魔方系统,在合同详情页,出现了“归档”按钮
点击此按钮
可以看到此合同已归档(无法编辑)了。
在上面的两节里,我们看到了如何自定义API和按钮,下面一起看到触发器(Trigger)的部分。
我们的合同管理需求里,假设包括:
上述的需求即可通过触发器实现,相关文件名称以.trigger.js结尾。下面我们给付款对象建立这2个触发器(Trigger)。
在 VS Code中,打开查看下的“命令面板”,输入Steedos,选择 “Steedos:Create Object Trigger”
选择所需的触发器,这里选择“beforeInsert”、“afterUpdate”
即可在相应路径,创建Trigger文件。
pay.trigger.js
const objectql = require('@steedos/objectql');
module.exports = {
listenTo: 'Your object name',
beforeInsert: async function(){
},
afterUpdate: async function(){
},
}
其中listenTo就是设定触发器绑定的对象,本文,这就应修改为“payment__c” 。
需求是,在增加付款记录时,判断如果合同已归档,则禁止增加付款记录。
pay.trigger.js
const objectql = require('@steedos/objectql');
module.exports = {
listenTo: 'payment__c',
beforeInsert: async function(){
let doc = this.doc;
let contractId = doc.contract__c;
console.log("contractId:",contractId);
if (contractId) {
let contractObj = this.getObject('contract__c');
let contractDoc = await contractObj.findOne(contractId, { fields: ['locked'] });
console.log("contractDoc",contractDoc);
console.log("contractDoc.locked",contractDoc.locked);
if (contractDoc && contractDoc.locked == true) {
throw new Error('合同已归档,不能再增加付款记录。');
}
}
},
afterUpdate: async function(){
},
}
在trigger的beforeInsert里,调用this.getObject(‘contract__c’).findOne()取得付款对应的合同的属性,如果locked,则抛出Error。
重启服务后,即可在前台进行测试确认。
在尚未归档前,可以“新建”付款,正巧,这时别的用户已将此合同归档,所以提交后,后台检测到合同已归档,发出报错信息、本次新增操作未生效。
需求是,修改付款记录后,判断如果全部款项已付款,则标记合同为已完成付款。
const objectql = require('@steedos/objectql');
module.exports = {
listenTo: 'payment__c',
beforeInsert: async function(){
let doc = this.doc;
let contractId = doc.contract__c;
console.log("contractId:",contractId);
if (contractId) {
let contractObj = this.getObject('contract__c');
let contractDoc = await contractObj.findOne(contractId, { fields: ['locked'] });
console.log("contractDoc",contractDoc);
console.log("contractDoc.locked",contractDoc.locked);
if (contractDoc && contractDoc.locked == true) {
throw new Error('合同已归档,不能再增加付款记录。');
}
}
},
afterUpdate: async function(){
let doc = this.doc;
const contractId = doc.contract__c;
const contractObj = this.getObject('contract__c');
const paymentDoc = this.getObject('payment__c');
let updateFlag = true;
const paymentDocs = await paymentDoc.find({ filters: [['contract__c', '=', contractId]], fields: ['_id','payment_status__c','amount__c'] });
for (const pDoc of paymentDocs) {
if (pDoc.payment_status__c == 'unpaid'){
updateFlag = false;
}
}
if (updateFlag === true){
await contractObj.update(contractId, { paid_all: true });
}
},
}
在trigger的afterUpdate里,调用this.getObject(‘paymentc’).find()取得付款对应的合同的所有付款记录,循环判断,如果所有记录的payment_statusc都不是’unpaid’,则调用this.getObject(‘contract__c’).update()给合同的字段“已完成付款”赋值为true 。
重启服务后,即可在前台进行测试确认。修改某合同最后一条付款记录
修改为已支付后,
可以看到这个合同,已被标记为款项已结清 。