基本指令 - 执行策略

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

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

基本指令 - 执行链

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

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

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

{ method: 'post', path: 'atom/delete', controller: 'atom', middlewares: 'transaction',
  meta: { right: { type: 'atom', action: 4 } },
},

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

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

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

async delete() {
  await this.ctx.service.atom.delete({
    key: this.ctx.request.body.key,
    user: this.ctx.state.user.op,
  });
  this.ctx.success();
}

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

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

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

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

async delete({ key, user }) {
  const atomClass = await ctx.bean.atomClass.getByAtomId({ atomId: key.atomId });
  if (!atomClass) ctx.throw.module(moduleInfo.relativeName, 1002);
  if (!key.itemId) key.itemId = atomClass.itemId;
  // atom bean
  const _moduleInfo = mparse.parseInfo(atomClass.module);
  const _atomClass = await ctx.bean.atomClass.atomClass(atomClass);
  const beanFullName = `${_moduleInfo.relativeName}.atom.${_atomClass.bean}`;
  ...
  // delete
  await ctx.executeBean({
    beanModule: _moduleInfo.relativeName,
    beanFullName,
    context: { atomClass, key, user },
    fn: 'delete',
  });
  ...
}
    1. 通过key获取业务数据的atomClass信息
    1. 通过atomClass信息构造beanFullName
    1. 通过beanFullName执行ctx.executeBean,从而调用业务模块提供的业务Bean组件。参见:跨模块调用本地Bean:ctx.executeBean

业务Bean组件

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

定义Bean组件

src/module/test-party/backend/src/bean/atom.party.js

module.exports = app => {

  const gPartyTypeEmojis = {
    Birthday: '🎂',
    Dance: '💃',
    Garden: '🏡',
  };

  class Atom extends app.meta.AtomBase {

    async create({ atomClass, item, user }) {
      // super
      const key = await super.create({ atomClass, item, user });
      // add party
      const res = await this.ctx.model.party.insert({
        atomId: key.atomId,
      });
      return { atomId: key.atomId, itemId: res.insertId };
    }

    async read({ atomClass, options, key, user }) {
      // super
      const item = await super.read({ atomClass, options, key, user });
      if (!item) return null;
      // read
      this._getMeta(item, options);
      // ok
      return item;
    }

    async select({ atomClass, options, items, user }) {
      // super
      await super.select({ atomClass, options, items, user });
      // select
      for (const item of items) {
        this._getMeta(item, options);
      }
    }

    async write({ atomClass, target, key, item, options, user }) {
      // super
      await super.write({ atomClass, target, key, item, options, user });
      // update party
      const data = await this.ctx.model.party.prepareData(item);
      data.id = key.itemId;
      await this.ctx.model.party.update(data);
    }

    async delete({ atomClass, key, user }) {
      // delete party
      await this.ctx.model.party.delete({
        id: key.itemId,
      });
      // super
      await super.delete({ atomClass, key, user });
    }

    _getMeta(item, options) {
      // layout: list/table/mobile/pc
      const layout = options && options.layout;
      // flags
      const flags = [];
      if (layout !== 'table' && item.personCount) {
        flags.push(item.personCount + 'P');
      }
      // summary
      let summary;
      if (item.partyTypeName) {
        summary = `${gPartyTypeEmojis[item.partyTypeName]}${this.ctx.text(item.partyTypeName)}`;
      }
      // meta
      const meta = {
        flags,
        summary,
      };
      // ok
      item._meta = meta;
    }

  }

  return Atom;
};

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

注册Bean组件

src/module/test-party/backend/src/beans.js

const atomParty = require('./bean/atom.party.js');

module.exports = app => {
  const beans = {
    // atom
    'atom.party': {
      mode: 'app',
      bean: atomParty,
    },
  };
  return beans;
};
注册名称 场景 所属模块 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 当前操作用户