基本指令 - 执行策略

对于基本指令,模块a-base提供了统一的对外API接口,有如下好处:

  1. 封装数据库事务配置
  2. 封装权限判断
  3. 简化业务Bean组件的开发

基本指令 - 执行链

delete指令为例,指令执行链如下:

1. 模块a-base:提供API路由

src/module-system/a-base-sync/backend/src/routes.js

  1. 1{ method: 'post', path: 'atom/delete', controller: 'atom', middlewares: 'transaction',
  2. 2 meta: { right: { type: 'atom', action: 'delete' } },
  3. 3},

2. 任何前端模块:通过/a/base/atom/delete调用后端API接口

src/module-system/a-base-sync/front/src/components/atom/action.js

  1. 1const key = { atomId: item.atomId, itemId: item.itemId };
  2. 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

  1. 1async delete() {
  2. 2 await this.ctx.service.atom.delete({
  3. 3 key: this.ctx.request.body.key,
  4. 4 user: this.ctx.state.user.op,
  5. 5 });
  6. 6 this.ctx.success();
  7. 7}

4. 模块a-base: Service方法调用ctx.bean.atom方法

  1. 1async delete({ key, user }) {
  2. 2 return await this.ctx.bean.atom.delete({ key, user });
  3. 3}

5. 模块a-base: ctx.bean.atom调用具体业务的bean方法

src/module-system/a-base-sync/backend/src/bean/bean.atom.js

  1. 1async delete({ key, user }) {
  2. 2 const atomClass = await ctx.bean.atomClass.getByAtomId({ atomId: key.atomId });
  3. 3 if (!atomClass) ctx.throw.module(moduleInfo.relativeName, 1002);
  4. 4 if (!key.itemId) key.itemId = atomClass.itemId;
  5. 5 // atom bean
  6. 6 const _moduleInfo = mparse.parseInfo(atomClass.module);
  7. 7 const _atomClass = await ctx.bean.atomClass.atomClass(atomClass);
  8. 8 const beanFullName = `${_moduleInfo.relativeName}.atom.${_atomClass.bean}`;
  9. 9 ...
  10. 10 // delete
  11. 11 await ctx.executeBean({
  12. 12 beanModule: _moduleInfo.relativeName,
  13. 13 beanFullName,
  14. 14 context: { atomClass, key, user },
  15. 15 fn: 'delete',
  16. 16 });
  17. 17 ...
  18. 18}
  1. 通过key获取业务数据的atomClass信息

  2. 通过atomClass信息构造beanFullName

  3. 通过beanFullName执行ctx.executeBean,从而调用业务模块提供的业务Bean组件。参见:跨模块调用本地Bean:ctx.executeBean

业务Bean组件

由以上基本指令的执行链可以看到,针对具体的业务开发,我们只需要针对原子类型提供一个业务Bean组件即可。所有通用的核心逻辑都由模块a-base进行封装,我们只需要在业务Bean组件中提供与业务相关的自定义逻辑即可

定义Bean组件

src/suite-vendor/test-party/modules/test-party/backend/src/bean/atom.party.js

  1. 1module.exports = app => {
  2. 2
  3. 3 const gPartyTypeEmojis = {
  4. 4 Birthday: '🎂',
  5. 5 Dance: '💃',
  6. 6 Garden: '🏡',
  7. 7 };
  8. 8
  9. 9 class Atom extends app.meta.AtomBase {
  10. 10
  11. 11 async create({ atomClass, item, user }) {
  12. 12 // super
  13. 13 const key = await super.create({ atomClass, item, user });
  14. 14 // add party
  15. 15 const res = await this.ctx.model.party.insert({
  16. 16 atomId: key.atomId,
  17. 17 });
  18. 18 return { atomId: key.atomId, itemId: res.insertId };
  19. 19 }
  20. 20
  21. 21 async read({ atomClass, options, key, user }) {
  22. 22 // super
  23. 23 const item = await super.read({ atomClass, options, key, user });
  24. 24 if (!item) return null;
  25. 25 // read
  26. 26 await this._getMeta(item, options);
  27. 27 // ok
  28. 28 return item;
  29. 29 }
  30. 30
  31. 31 async select({ atomClass, options, items, user }) {
  32. 32 // super
  33. 33 await super.select({ atomClass, options, items, user });
  34. 34 // select
  35. 35 for (const item of items) {
  36. 36 await this._getMeta(item, options);
  37. 37 }
  38. 38 }
  39. 39
  40. 40 async write({ atomClass, target, key, item, options, user }) {
  41. 41 // super
  42. 42 await super.write({ atomClass, target, key, item, options, user });
  43. 43 // update party
  44. 44 const data = await this.ctx.model.party.prepareData(item);
  45. 45 data.id = key.itemId;
  46. 46 await this.ctx.model.party.update(data);
  47. 47 }
  48. 48
  49. 49 async delete({ atomClass, key, user }) {
  50. 50 // delete party
  51. 51 await this.ctx.model.party.delete({
  52. 52 id: key.itemId,
  53. 53 });
  54. 54 // super
  55. 55 await super.delete({ atomClass, key, user });
  56. 56 }
  57. 57
  58. 58 async _getMeta(item, options) {
  59. 59 // layout: list/table/mobile/pc
  60. 60 const layout = options && options.layout;
  61. 61 // meta
  62. 62 const meta = this._ensureItemMeta(item);
  63. 63 // meta.flags
  64. 64 if (item.partyOver) {
  65. 65 meta.flags.push(this.ctx.text('PartyOverFlag'));
  66. 66 }
  67. 67 if (layout !== 'table' && item.personCount) {
  68. 68 meta.flags.push(item.personCount + 'P');
  69. 69 }
  70. 70 // meta.summary
  71. 71 if (item.partyTypeCode) {
  72. 72 const dictItem = await this.ctx.bean.dict.findItem({
  73. 73 dictKey: 'test-party:dictPartyType',
  74. 74 code: item.partyTypeCode,
  75. 75 });
  76. 76 meta.summary = `${dictItem.options.emoji}${dictItem.titleLocaleFull}`;
  77. 77 }
  78. 78 }
  79. 79
  80. 80 }
  81. 81
  82. 82 return Atom;
  83. 83};

业务Bean组件必须继承基类app.meta.AtomBase

注册Bean组件

src/suite-vendor/test-party/modules/test-party/backend/src/beans.js

  1. 1const atomParty = require('./bean/atom.party.js');
  2. 2
  3. 3module.exports = app => {
  4. 4 const beans = {
  5. 5 // atom
  6. 6 'atom.party': {
  7. 7 mode: 'app',
  8. 8 bean: atomParty,
  9. 9 },
  10. 10 };
  11. 11 return beans;
  12. 12};
注册名称 场景 所属模块 global beanFullName
party atom test-party false test-party.atom.party

Bean组件方法

- create

  1. 调用基类方法生成原子Key,得到key.atomId
  2. 新建业务表条目
  3. 返回原子Key
名称 说明
atomClass 原子类型
item 条目数据,前端可以通过item传递更多初始化数据
user 当前操作用户

- read

read业务逻辑一般为空,因为这时对象item已经提取了原子的基本表业务表的数据

如果想附加基本表业务表之外的数据,可在这里直接操作对象item

名称 说明
atomClass 原子类型
options 自定义参数
key 原子Key
user 当前操作用户

- select

select业务逻辑一般为空,因为这时对象items已经提取了原子的基本表业务表的数据

如果想附加基本表业务表之外的数据,可在这里直接操作对象items

名称 说明
atomClass 原子类型
options 自定义参数
items 已经检索出来的数据集
user 当前操作用户

- write

  1. 调用基类方法,更新数据表aAtom的基本信息
  2. 更新业务表条目
名称 说明
atomClass 原子类型
target 针对什么目标进行write操作
key 原子Key
item 条目数据
options 自定义参数
user 当前操作用户
  • target
名称 说明
(empty) 保存草稿
draft 从历史副本恢复旧版本,或者从正式副本再次编辑
formal 草稿提交至正式副本
history 正式副本推入历史副本
clone 克隆一个新副本,直接进入草稿编辑状态

- delete

  1. 删除业务表条目
  2. 调用基类方法,删除数据表aAtom条目
名称 说明
atomClass 原子类型
key 原子Key
user 当前操作用户