原子的基本概念与用法

2018-11-21 08:32:27

原子是Cabloy最基本的要素,如文章、公告、请假单,等等

通过原子的组合,就可以实现任何想要的功能,如CMS、OA、CRM、ERP等等

正由于从各种业务模型中抽象出来一个通用的原子概念,因而,Cabloy为原子实现了许多通用的特性和功能,从而可以便利的为各类实际业务赋能

更详细的原子概念请参见:Cabloy:原子基本概念

在这里,主要通过原子类型todo介绍最基本的概念与用法

业务模块模版:module-business

这里再次强调一下,如果要开发一项业务功能,建议使用业务模块模版module-business创建业务模块的文件骨架。此模版会自动创建与业务相关的代码,大量简化工作量

$ cd cabloy-demo
$ egg-born src/module/test-todo --type=module-business

声明原子类型

原子类型原子对应的元数据信息,也是在模块的meta中设置

src/module/test-todo/backend/src/meta.js

const meta = {
  base: {
    atoms: {
      todo: {
        info: {
          title: 'Todo',
          tableName: 'testTodo',
        },
        actions: {
        },
        flags: {
        },
        validator: 'todo',
        search: {
          validator: 'todoSearch',
        },
      },
    },
  },
};
名称 说明
info.title 原子类型的名称
info.tableName 原子类型对应的业务数据表名称
validator 原子类型对应的验证器,用于渲染业务表单,并验证表单数据
search.validator 与搜索相关的验证器,用于渲染自定义的搜索字段

原子业务数据表

所有与模块数据架构变更相关的代码都在后端API路由version/update中,便于维护模块的数据版本,从而实现模块的无缝升级

src/module/test-todo/backend/src/service/version.js

async update(options) {
  if (options.version === 1) {
    // create table: testTodo
    const sql = `
      CREATE TABLE testTodo (
        id int(11) NOT NULL AUTO_INCREMENT,
        createdAt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
        updatedAt timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
        deleted int(11) DEFAULT '0',
        iid int(11) DEFAULT '0',
        atomId int(11) DEFAULT '0',
        description varchar(255) DEFAULT NULL,
        PRIMARY KEY (id)
      )
    `;
    await this.ctx.model.query(sql);
  }
}

model对象

定义与业务数据表对应的model对象,可以更便利的操作数据

src/module/test-todo/backend/src/model/todo.js

module.exports = app => {
  class Todo extends app.meta.Model {
    constructor(ctx) {
      super(ctx, { table: 'testTodo', options: { disableDeleted: false } });
    }
  }
  return Todo;
};
名称 默认值 说明
table model对象对应的数据表名,也可以是视图名
disableDeleted false 是否禁用软删除特性

验证器

Cabloy的验证机制底层采用ajv,建议您对ajv有初步的了解

使用验证器,我们只需定义好与业务相关的JSON Schema,就可以自动渲染表单,同时还可以自动验证表单数据,如果表单数据不符合预期,会自动把错误信息显示出来

关于表单验证的更详细信息请参见:Cabloy:表单验证

src/module/test-todo/backend/src/config/validation/schemas.js

module.exports = app => {
  const schemas = {};
  // todo
  schemas.todo = {
    type: 'object',
    meta: {
      custom: {
        // component: 'todoItem',
      },
    },
    properties: {
      atomName: {
        type: 'string',
        ebType: 'text',
        ebTitle: 'Name',
        notEmpty: true,
      },
      description: {
        type: 'string',
        ebType: 'text',
        ebTitle: 'Description',
      },
    },
  };
  // todo search
  schemas.todoSearch = {
    type: 'object',
    properties: {
      description: {
        type: 'string',
        ebType: 'text',
        ebTitle: 'Description',
      },
    },
  };
  return schemas;
};

原子指令API路由

Cabloy将所有业务数据的操作称为原子指令,主要分两类:

  1. 基本指令:create、read、write、delete
  2. 扩展指令:与具体业务相关的操作,如reviewpublish等等

Cabloy提供了一套基础API路由,对原子指令进行了封装,这样可以统一配置数据库事务验证器数据权限等中间件

业务模块只需提供扩展逻辑路由即可,这些扩展逻辑路由会在合适的时候被基础API路由调用

扩展逻辑路由

src/module/test-todo/backend/src/routes.js

  // todo
  { method: 'post', path: 'todo/create', controller: todo, middlewares: 'inner' },
  { method: 'post', path: 'todo/read', controller: todo, middlewares: 'inner' },
  { method: 'post', path: 'todo/select', controller: todo, middlewares: 'inner' },
  { method: 'post', path: 'todo/write', controller: todo, middlewares: 'inner' },
  { method: 'post', path: 'todo/delete', controller: todo, middlewares: 'inner' },
  { method: 'post', path: 'todo/action', controller: todo, middlewares: 'inner' },
  { method: 'post', path: 'todo/enable', controller: todo, middlewares: 'inner' },
名称 说明
middlewares: ‘inner’ 中间件’inner’,声明此API路由只允许内部访问
todo/create 新建todo时调用
todo/read 查询单条todo时调用
todo/select 查询多条todo时调用
todo/write 修改todo时调用
todo/delete 删除todo时调用
todo/action 在这个路由中实现与业务相关的扩展指令
todo/enable todo草稿状态转为正常状态时调用

扩展逻辑代码

src/module/test-todo/backend/src/service/todo.js

module.exports = app => {

  class Todo extends app.Service {

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

    async read({ atomClass, key, item, user }) {
      // read
    }

    async select({ atomClass, options, items, user }) {
      // select
    }

    async write({ atomClass, key, item, validation, user }) {
      // update todo
      await this.ctx.model.todo.update({
        id: key.itemId,
        description: item.description,
      });
    }

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

    async action({ action, atomClass, key, user }) {
    }

    async enable({ atomClass, key, atom, user }) {
    }

  }

  return Todo;
};


评论: