基本指令 - 执行策略
对于基本指令
,模块a-detail
提供了统一的对外API接口,有如下好处:
- 封装数据库事务配置
- 封装权限判断
- 简化业务Bean组件的开发
基本指令 - 执行链
以delete
指令为例,指令执行链如下:
1. 模块a-detail
:提供API路由
src/module-system/a-detail/backend/src/routes.js
{ method: 'post', path: 'detail/delete', controller: 'detail', middlewares: 'transaction',
meta: { right: { type: 'detail', action: 4 } },
},
2. 任何前端模块:通过/a/detail/atom/delete
调用后端API接口
src/module-system/a-detail/front/src/components/detail/action.js
const flowTaskId = (meta && meta.flowTaskId) || 0;
const key = { detailId: item.detailId, detailItemId: item.detailItemId };
await ctx.$api.post('/a/detail/detail/delete', { flowTaskId, key });
3. 模块a-detail
: API路由对应Controller方法,组织好参数调用Service方法
src/module-system/a-detail/backend/src/controller/detail.js
async delete() {
await this.ctx.service.detail.delete({
key: this.ctx.request.body.key,
user: this.ctx.state.user.op,
});
this.ctx.success();
}
4. 模块a-detail
: Service方法调用ctx.bean.detail方法
async delete({ key, user }) {
return await this.ctx.bean.detail.delete({ key, user });
}
5. 模块a-detail
: ctx.bean.detail调用具体业务的bean方法
src/module-system/a-detail/backend/src/bean/bean.detail.js
async delete({ key, target, user }) {
const detailClass = await ctx.bean.detailClass.getByDetailId({ detailId: key.detailId });
if (!detailClass) ctx.throw.module('a-base', 1002);
if (!key.detailItemId) key.detailItemId = detailClass.detailItemId;
// delete
await this._delete2({ detailClass, key, target, user });
}
async _delete2({ detailClass, key, target, user }) {
// detail bean
const _moduleInfo = mparse.parseInfo(detailClass.module);
const _detailClass = ctx.bean.detailClass.detailClass(detailClass);
const beanFullName = `${_moduleInfo.relativeName}.detail.${_detailClass.bean}`;
// delete
await ctx.executeBean({
beanModule: _moduleInfo.relativeName,
beanFullName,
context: { detailClass, target, key, user },
fn: 'delete',
});
}
-
- 通过
key
获取业务数据的detailClass
信息
- 通过
-
- 通过
detailClass
信息构造beanFullName
- 通过
-
- 通过
beanFullName
执行ctx.executeBean
,从而调用业务模块提供的业务Bean组件
。参见:跨模块调用本地Bean:ctx.executeBean
- 通过
业务Bean组件
由以上基本指令的执行链
可以看到,针对具体的业务开发,我们只需要针对明细类型
提供一个业务Bean组件
即可。所有通用的核心逻辑都由模块a-detail
进行封装,我们只需要在业务Bean组件
中提供与业务相关的自定义逻辑即可
- 当我们通过命令行创建一个
业务模块
时,业务Bean组件
的代码骨架就已经自动生成了。参见:明细基本概念-业务模块模版
定义Bean组件
src/module/test-flow/backend/src/bean/detail.purchaseOrder.js
module.exports = app => {
class Detail extends app.meta.DetailBase {
async create({ atomKey, detailClass, item, user }) {
// super
const key = await super.create({ atomKey, detailClass, item, user });
// add purchaseOrder detail
const res = await this.ctx.model.purchaseOrderDetail.insert({
atomId: atomKey.atomId,
detailId: key.detailId,
});
// return key
return { detailId: key.detailId, detailItemId: res.insertId };
}
async read({ detailClass, options, key, user }) {
// super
const item = await super.read({ detailClass, options, key, user });
if (!item) return null;
// meta
this._getMeta(item);
// ok
return item;
}
async select({ atomKey, detailClass, options, items, user }) {
// super
await super.select({ atomKey, detailClass, options, items, user });
// meta
for (const item of items) {
this._getMeta(item);
}
}
async write({ detailClass, target, key, item, options, user }) {
// super
await super.write({ detailClass, target, key, item, options, user });
// update purchaseOrder detail
const data = await this.ctx.model.purchaseOrderDetail.prepareData(item);
data.id = key.detailItemId;
// update
await this.ctx.model.purchaseOrderDetail.update(data);
}
async delete({ detailClass, target, key, user }) {
// delete purchaseOrder detail
await this.ctx.model.purchaseOrderDetail.delete({
id: key.detailItemId,
});
// super
await super.delete({ detailClass, target, key, user });
}
_getMeta(item) {
// flags
const flags = [];
if (item.quantity > 1) {
flags.push(item.quantity);
}
const amount = (item.amount / 100).toFixed(2);
flags.push(amount);
// meta
const meta = {
summary: item.detailCode,
flags,
};
// ok
item._meta = meta;
}
}
return Detail;
};
业务Bean组件
必须继承基类app.meta.DetailBase
注册Bean组件
src/module/test-flow/backend/src/beans.js
const detailPurchaseOrder = require('./bean/detail.purchaseOrder.js');
module.exports = app => {
const beans = {
// detail
'detail.purchaseOrder': {
mode: 'app',
bean: detailPurchaseOrder,
},
};
return beans;
};
注册名称 | 场景 | 所属模块 | global | beanFullName |
---|---|---|---|---|
purchaseOrder | detail | test-flow | false | test-flow.detail.purchaseOrder |
Bean组件方法
- create
- 调用基类方法生成
明细Key
,得到key.detailId - 新建业务表明细条目
- 返回
明细Key
名称 | 说明 |
---|---|
atomKey | 原子Key |
detailClass | 明细类型 |
item | 明细条目数据,前端可以通过item传递更多初始化数据 |
user | 当前操作用户 |
- read
read
业务逻辑一般为空,因为这时对象item
已经提取了明细的基本表
和业务表
的数据
如果想附加基本表
和业务表
之外的数据,可在这里直接操作对象item
名称 | 说明 |
---|---|
detailClass | 明细类型 |
options | 自定义参数 |
key | 明细Key |
user | 当前操作用户 |
- select
select
业务逻辑一般为空,因为这时对象items
已经提取了明细的基本表
和业务表
的数据
如果想附加基本表
和业务表
之外的数据,可在这里直接操作对象items
名称 | 说明 |
---|---|
detailClass | 明细类型 |
options | 自定义参数 |
items | 已经检索出来的数据集 |
user | 当前操作用户 |
- write
- 调用基类方法,更新数据表
aDetail
的基本信息 - 更新业务表明细条目
名称 | 说明 |
---|---|
detailClass | 明细类型 |
target | 针对什么目标进行write操作 |
key | 明细Key |
item | 条目数据 |
options | 自定义参数 |
user | 当前操作用户 |
- target
一般而言,target参数来自原子write
方法。比如,将原子数据从草稿
提交至正式副本
时,也会同时把该原子关联的明细数据
提交至正式副本
,此时target
值就是formal
名称 | 说明 |
---|---|
(empty) | 保存草稿 |
draft | 从历史副本恢复旧版本,或者从正式副本再次编辑 |
formal | 草稿提交至正式副本 |
history | 正式副本推入历史副本 |
clone | 克隆一个新副本,直接进入草稿编辑状态 |
- delete
- 删除业务表明细条目
- 调用基类方法,删除数据表
aDetail
条目
名称 | 说明 |
---|---|
detailClass | 明细类型 |
key | 明细Key |
user | 当前操作用户 |
评论: