基本指令 - 执行策略
对于基本指令
,模块a-detail
提供了统一的对外API接口,有如下好处:
- 封装数据库事务配置
- 封装权限判断
- 简化业务Bean组件的开发
基本指令 - 执行链
以delete
指令为例,指令执行链如下:
1. 模块a-detail
:提供API路由
src/module-system/a-detail/backend/src/routes.js
- 1{ method: 'post', path: 'detail/delete', controller: 'detail', middlewares: 'transaction',
- 2 meta: { right: { type: 'detail', action: 4 } },
- 3},
2. 任何前端模块:通过/a/detail/atom/delete
调用后端API接口
src/module-system/a-detail/front/src/components/detail/action.js
- 1const flowTaskId = (meta && meta.flowTaskId) || 0;
- 2const key = { detailId: item.detailId, detailItemId: item.detailItemId };
- 3await 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
- 1async delete() {
- 2 await this.ctx.service.detail.delete({
- 3 key: this.ctx.request.body.key,
- 4 user: this.ctx.state.user.op,
- 5 });
- 6 this.ctx.success();
- 7}
4. 模块a-detail
: Service方法调用ctx.bean.detail方法
- 1async delete({ key, user }) {
- 2 return await this.ctx.bean.detail.delete({ key, user });
- 3}
5. 模块a-detail
: ctx.bean.detail调用具体业务的bean方法
src/module-system/a-detail/backend/src/bean/bean.detail.js
- 1async delete({ key, target, user }) {
- 2 const detailClass = await ctx.bean.detailClass.getByDetailId({ detailId: key.detailId });
- 3 if (!detailClass) ctx.throw.module('a-base', 1002);
- 4 if (!key.detailItemId) key.detailItemId = detailClass.detailItemId;
- 5 // delete
- 6 await this._delete2({ detailClass, key, target, user });
- 7}
- 8
- 9async _delete2({ detailClass, key, target, user }) {
- 10 // detail bean
- 11 const _moduleInfo = mparse.parseInfo(detailClass.module);
- 12 const _detailClass = ctx.bean.detailClass.detailClass(detailClass);
- 13 const beanFullName = `${_moduleInfo.relativeName}.detail.${_detailClass.bean}`;
- 14 // delete
- 15 await ctx.executeBean({
- 16 beanModule: _moduleInfo.relativeName,
- 17 beanFullName,
- 18 context: { detailClass, target, key, user },
- 19 fn: 'delete',
- 20 });
- 21}
- 通过
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
- 1module.exports = app => {
- 2
- 3 class Detail extends app.meta.DetailBase {
- 4
- 5 async create({ atomKey, detailClass, item, user }) {
- 6 // super
- 7 const key = await super.create({ atomKey, detailClass, item, user });
- 8 // add purchaseOrder detail
- 9 const res = await this.ctx.model.purchaseOrderDetail.insert({
- 10 atomId: atomKey.atomId,
- 11 detailId: key.detailId,
- 12 });
- 13 // return key
- 14 return { detailId: key.detailId, detailItemId: res.insertId };
- 15 }
- 16
- 17 async read({ detailClass, options, key, user }) {
- 18 // super
- 19 const item = await super.read({ detailClass, options, key, user });
- 20 if (!item) return null;
- 21 // meta
- 22 this._getMeta(item);
- 23 // ok
- 24 return item;
- 25 }
- 26
- 27 async select({ atomKey, detailClass, options, items, user }) {
- 28 // super
- 29 await super.select({ atomKey, detailClass, options, items, user });
- 30 // meta
- 31 for (const item of items) {
- 32 this._getMeta(item);
- 33 }
- 34 }
- 35
- 36 async write({ detailClass, target, key, item, options, user }) {
- 37 // super
- 38 await super.write({ detailClass, target, key, item, options, user });
- 39 // update purchaseOrder detail
- 40 const data = await this.ctx.model.purchaseOrderDetail.prepareData(item);
- 41 data.id = key.detailItemId;
- 42 // update
- 43 await this.ctx.model.purchaseOrderDetail.update(data);
- 44 }
- 45
- 46 async delete({ detailClass, target, key, user }) {
- 47 // delete purchaseOrder detail
- 48 await this.ctx.model.purchaseOrderDetail.delete({
- 49 id: key.detailItemId,
- 50 });
- 51 // super
- 52 await super.delete({ detailClass, target, key, user });
- 53 }
- 54
- 55 _getMeta(item) {
- 56 const meta = this._ensureItemMeta(item);
- 57 // meta.flags
- 58 if (item.quantity > 1) {
- 59 meta.flags.push(item.quantity);
- 60 }
- 61 const amount = (item.amount / 100).toFixed(2);
- 62 meta.flags.push(amount);
- 63 // meta.summary
- 64 meta.summary = item.detailCode;
- 65 }
- 66
- 67 }
- 68
- 69 return Detail;
- 70};
业务Bean组件
必须继承基类app.meta.DetailBase
注册Bean组件
src/module/test-flow/backend/src/beans.js
- 1const detailPurchaseOrder = require('./bean/detail.purchaseOrder.js');
- 2
- 3module.exports = app => {
- 4 const beans = {
- 5 // detail
- 6 'detail.purchaseOrder': {
- 7 mode: 'app',
- 8 bean: detailPurchaseOrder,
- 9 },
- 10 };
- 11 return beans;
- 12};
注册名称 | 场景 | 所属模块 | 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 | 当前操作用户 |
评论: