基本指令 - 执行策略
对于基本指令
,模块a-base
提供了统一的对外API接口,有如下好处:
- 封装数据库事务配置
- 封装权限判断
- 简化业务Bean组件的开发
基本指令 - 执行链
以delete
指令为例,指令执行链如下:
1. 模块a-base
:提供API路由
src/module-system/a-base-sync/backend/src/routes.js
- 1{ method: 'post', path: 'atom/delete', controller: 'atom', middlewares: 'transaction',
- 2 meta: { right: { type: 'atom', action: 'delete' } },
- 3},
2. 任何前端模块:通过/a/base/atom/delete
调用后端API接口
src/module-system/a-base-sync/front/src/components/atom/action.js
- 1const key = { atomId: item.atomId, itemId: item.itemId };
- 2await ctx.$api.post('/a/base/atom/delete', { key });
3. 模块a-base
: API路由对应Controller方法,组织好参数调用Service方法
src/module-system/a-base-sync/backend/src/controller/atom.js
- 1async delete() {
- 2 await this.ctx.service.atom.delete({
- 3 key: this.ctx.request.body.key,
- 4 user: this.ctx.state.user.op,
- 5 });
- 6 this.ctx.success();
- 7}
4. 模块a-base
: Service方法调用ctx.bean.atom方法
- 1async delete({ key, user }) {
- 2 return await this.ctx.bean.atom.delete({ key, user });
- 3}
5. 模块a-base
: ctx.bean.atom调用具体业务的bean方法
src/module-system/a-base-sync/backend/src/bean/bean.atom.js
- 1async delete({ key, user }) {
- 2 const atomClass = await ctx.bean.atomClass.getByAtomId({ atomId: key.atomId });
- 3 if (!atomClass) ctx.throw.module(moduleInfo.relativeName, 1002);
- 4 if (!key.itemId) key.itemId = atomClass.itemId;
- 5 // atom bean
- 6 const _moduleInfo = mparse.parseInfo(atomClass.module);
- 7 const _atomClass = await ctx.bean.atomClass.atomClass(atomClass);
- 8 const beanFullName = `${_moduleInfo.relativeName}.atom.${_atomClass.bean}`;
- 9 ...
- 10 // delete
- 11 await ctx.executeBean({
- 12 beanModule: _moduleInfo.relativeName,
- 13 beanFullName,
- 14 context: { atomClass, key, user },
- 15 fn: 'delete',
- 16 });
- 17 ...
- 18}
-
通过
key
获取业务数据的atomClass
信息 -
通过
atomClass
信息构造beanFullName
-
通过
beanFullName
执行ctx.executeBean
,从而调用业务模块提供的业务Bean组件
。参见:跨模块调用本地Bean:ctx.executeBean
业务Bean组件
由以上基本指令的执行链可以看到,针对具体的业务开发,我们只需要针对原子类型提供一个业务Bean组件
即可。所有通用的核心逻辑都由模块a-base
进行封装,我们只需要在业务Bean组件
中提供与业务相关的自定义逻辑即可
- 当我们通过命令行创建一个
业务模块
时,业务Bean组件
的代码骨架就已经自动生成了。参见:模块基本概念-新建模块
定义Bean组件
src/suite-vendor/test-party/modules/test-party/backend/src/bean/atom.party.js
- 1module.exports = app => {
- 2
- 3 const gPartyTypeEmojis = {
- 4 Birthday: '🎂',
- 5 Dance: '💃',
- 6 Garden: '🏡',
- 7 };
- 8
- 9 class Atom extends app.meta.AtomBase {
- 10
- 11 async create({ atomClass, item, user }) {
- 12 // super
- 13 const key = await super.create({ atomClass, item, user });
- 14 // add party
- 15 const res = await this.ctx.model.party.insert({
- 16 atomId: key.atomId,
- 17 });
- 18 return { atomId: key.atomId, itemId: res.insertId };
- 19 }
- 20
- 21 async read({ atomClass, options, key, user }) {
- 22 // super
- 23 const item = await super.read({ atomClass, options, key, user });
- 24 if (!item) return null;
- 25 // read
- 26 await this._getMeta(item, options);
- 27 // ok
- 28 return item;
- 29 }
- 30
- 31 async select({ atomClass, options, items, user }) {
- 32 // super
- 33 await super.select({ atomClass, options, items, user });
- 34 // select
- 35 for (const item of items) {
- 36 await this._getMeta(item, options);
- 37 }
- 38 }
- 39
- 40 async write({ atomClass, target, key, item, options, user }) {
- 41 // super
- 42 await super.write({ atomClass, target, key, item, options, user });
- 43 // update party
- 44 const data = await this.ctx.model.party.prepareData(item);
- 45 data.id = key.itemId;
- 46 await this.ctx.model.party.update(data);
- 47 }
- 48
- 49 async delete({ atomClass, key, user }) {
- 50 // delete party
- 51 await this.ctx.model.party.delete({
- 52 id: key.itemId,
- 53 });
- 54 // super
- 55 await super.delete({ atomClass, key, user });
- 56 }
- 57
- 58 async _getMeta(item, options) {
- 59 // layout: list/table/mobile/pc
- 60 const layout = options && options.layout;
- 61 // meta
- 62 const meta = this._ensureItemMeta(item);
- 63 // meta.flags
- 64 if (item.partyOver) {
- 65 meta.flags.push(this.ctx.text('PartyOverFlag'));
- 66 }
- 67 if (layout !== 'table' && item.personCount) {
- 68 meta.flags.push(item.personCount + 'P');
- 69 }
- 70 // meta.summary
- 71 if (item.partyTypeCode) {
- 72 const dictItem = await this.ctx.bean.dict.findItem({
- 73 dictKey: 'test-party:dictPartyType',
- 74 code: item.partyTypeCode,
- 75 });
- 76 meta.summary = `${dictItem.options.emoji}${dictItem.titleLocaleFull}`;
- 77 }
- 78 }
- 79
- 80 }
- 81
- 82 return Atom;
- 83};
业务Bean组件
必须继承基类app.meta.AtomBase
注册Bean组件
src/suite-vendor/test-party/modules/test-party/backend/src/beans.js
- 1const atomParty = require('./bean/atom.party.js');
- 2
- 3module.exports = app => {
- 4 const beans = {
- 5 // atom
- 6 'atom.party': {
- 7 mode: 'app',
- 8 bean: atomParty,
- 9 },
- 10 };
- 11 return beans;
- 12};
注册名称 | 场景 | 所属模块 | global | beanFullName |
---|---|---|---|---|
party | atom | test-party | false | test-party.atom.party |
Bean组件方法
- create
- 调用基类方法生成
原子Key
,得到key.atomId - 新建业务表条目
- 返回
原子Key
名称 | 说明 |
---|---|
atomClass | 原子类型 |
item | 条目数据,前端可以通过item传递更多初始化数据 |
user | 当前操作用户 |
- read
read
业务逻辑一般为空,因为这时对象item
已经提取了原子的基本表
和业务表
的数据
如果想附加基本表
和业务表
之外的数据,可在这里直接操作对象item
名称 | 说明 |
---|---|
atomClass | 原子类型 |
options | 自定义参数 |
key | 原子Key |
user | 当前操作用户 |
- select
select
业务逻辑一般为空,因为这时对象items
已经提取了原子的基本表
和业务表
的数据
如果想附加基本表
和业务表
之外的数据,可在这里直接操作对象items
名称 | 说明 |
---|---|
atomClass | 原子类型 |
options | 自定义参数 |
items | 已经检索出来的数据集 |
user | 当前操作用户 |
- write
- 调用基类方法,更新数据表
aAtom
的基本信息 - 更新业务表条目
名称 | 说明 |
---|---|
atomClass | 原子类型 |
target | 针对什么目标进行write操作 |
key | 原子Key |
item | 条目数据 |
options | 自定义参数 |
user | 当前操作用户 |
- target
名称 | 说明 |
---|---|
(empty) | 保存草稿 |
draft | 从历史副本恢复旧版本,或者从正式副本再次编辑 |
formal | 草稿提交至正式副本 |
history | 正式副本推入历史副本 |
clone | 克隆一个新副本,直接进入草稿编辑状态 |
- delete
- 删除业务表条目
- 调用基类方法,删除数据表
aAtom
条目
名称 | 说明 |
---|---|
atomClass | 原子类型 |
key | 原子Key |
user | 当前操作用户 |
评论: