模型/Sequelize/GraphQL

发布时间:

模型管理

我们在egg中使用sequelize,要先在 app/model/ 目录下编写 Model,在CmsWing我们把编写Model做成了类似 “Navicat”的数据库管理工具,就像Navicat一样后台创建表,字段,关联会自动生成 sequelize Model文件并且会自动生成增、删、改、查的GraphQL接口。

这里只列出 CmsWing 特有的,更详细的内容请阅读 Egg.js 文档 Sequelize

Sequelize

我们通过CmsWing的模型管理,创建一个 cms_doc 的模型,会在app/model/ 目录下自动生成这个 Model,具体的 Sequelize 的使用请阅读 Sequelize官方文档

// app/model/cms_doc.js
// 本文件由CmsWing根据模型管理自动生成,请勿手动修改!
'use strict';
module.exports = app => {
  const DataTypes = app.Sequelize;
  const CmsDoc = app.model.define('cms_doc', {
    id: { type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true, comment: '主键' },
    createdAt: { type: DataTypes.DATE, comment: '创建时间' },
    updatedAt: { type: DataTypes.DATE, comment: '更新时间' },
    user_uuid: { type: DataTypes.UUID, comment: '作者uuid' },
    title: { type: DataTypes.STRING, comment: '内容标题' },
    classify_id: { type: DataTypes.INTEGER, defaultValue: 0, comment: '分类ID' },
    classify_sub: { type: DataTypes.JSON, comment: '子分类' },
    description: { type: DataTypes.STRING(1000), comment: '描述' },
    root: { type: DataTypes.INTEGER, defaultValue: 0, comment: '根节点' },
    pid: { type: DataTypes.INTEGER, defaultValue: 0, comment: '所属ID' },
    models_uuid: { type: DataTypes.UUID, comment: '模型UUID' },
    type: { type: DataTypes.INTEGER, defaultValue: 2, comment: '内容类型(1-目录,2-主题,3-段落)' },
    position: { type: DataTypes.STRING, comment: '推荐位(1-列表推荐,2-频道页推荐,4-首页推荐)' },
    ext_link: { type: DataTypes.STRING, comment: '如果填写链接,会跳转到这个链接,不填不跳转' },
    cover_url: { type: DataTypes.STRING, comment: '封面' },
    display: { type: DataTypes.BOOLEAN, defaultValue: true, comment: '可见性' },
    deadline: { type: DataTypes.DATE, comment: '截止时间' },
    view: { type: DataTypes.INTEGER, defaultValue: 0, comment: '浏览量' },
    level: { type: DataTypes.INTEGER, defaultValue: 0, comment: '优先级(越高排序越靠前)' },
    status: { type: DataTypes.INTEGER, defaultValue: 1, comment: '数据状态(0-禁用,1-正常,2-待审核,3-草稿)' },
    template: { type: DataTypes.STRING, comment: '模板详情' },
    tags: { type: DataTypes.STRING, comment: '标签' },
    sort: { type: DataTypes.INTEGER, defaultValue: 0, comment: '排序同级有效越小越靠前' },
  }, {
    indexes: [{ unique: false, fields: [ 'classify_id' ] }, { unique: false, fields: [ 'pid' ] }, { unique: false, fields: [ 'models_uuid' ] }],
    paranoid: true,
  });
  CmsDoc.associate = function() {
    app.model.CmsClassify.hasMany(app.model.CmsDoc, {
      foreignKey: 'classify_id',
      sourceKey: 'id',
      constraints: false,
    });
    app.model.CmsDoc.belongsTo(app.model.CmsClassify, {
      foreignKey: 'classify_id',
      targetKey: 'id',
      constraints: false,
    });
    app.model.CmsDoc.hasOne(app.model.CmsDocArticle, {
      foreignKey: 'doc_id',
      sourceKey: 'id',
      constraints: false,
    });
    app.model.CmsDocArticle.belongsTo(app.model.CmsDoc, {
      foreignKey: 'doc_id',
      targetKey: 'id',
      constraints: false,
    });
    app.model.CmsDoc.hasOne(app.model.CmsDocDownload, {
      foreignKey: 'doc_id',
      sourceKey: 'id',
      constraints: false,
    });
    app.model.CmsDocDownload.belongsTo(app.model.CmsDoc, {
      foreignKey: 'doc_id',
      targetKey: 'id',
      constraints: false,
    });
    app.model.CmsDoc.hasOne(app.model.CmsDocPicture, {
      foreignKey: 'doc_id',
      sourceKey: 'id',
      constraints: false,
    });
    app.model.CmsDocPicture.belongsTo(app.model.CmsDoc, {
      foreignKey: 'doc_id',
      targetKey: 'id',
      constraints: false,
    });
    app.model.SysUser.hasMany(app.model.CmsDoc, {
      foreignKey: 'user_uuid',
      sourceKey: 'uuid',
      constraints: false,
    });
    app.model.CmsDoc.belongsTo(app.model.SysUser, {
      foreignKey: 'user_uuid',
      targetKey: 'uuid',
      constraints: false,
    });

  };
  // CmsDoc.sync({ alter: true });
  return CmsDoc;
};

这个 Model 就可以在 Controller 和 Service 中通过 app.model.CmsDoc 或者 ctx.model.CmsDoc 访问到了。

注意事项

创建模型命名建议都是小写 应用_表名 的格式,比如 cms_doc , cms_doc_ext, 在Controller 和 Service 中使用 ctx.model.CmsDoc,ctx.model.CmsDocExt;

  • cms_doc -> CmsDoc
    cms_doc_ext -> CmsDocExt

 

多数据源

CmsWing 模型管理不支持多数据源,但是我们可以手动创建Model,来实现,比如我们需要增加一个db2数据库,配置如下,需要在app目录下新建一个mysql2目录来放置db2的model文件。

'use strict';
const { Op } = require('sequelize');
module.exports = {
  datasources: [
    {//默认数据库,支持CmsWing模型管理
      dialect: 'mysql',
      host: '127.0.0.1',
      port: 3306,
      database: 'cmswing2',
      username: 'root',
      password: 'root123456',
      timezone: '+08:00',
      define: {
        freezeTableName: true, // 强制表名称等于模型名称
        underscored: false,
      },
    },
    {//新增加的数据库配置,不支持模型管理,需要手动编写Model
      dialect: 'mysql',
      // 这里是通过app.Model2来使用db1数据库的模型
      delegate: 'model2',
      // 指定模型文件在哪里,默认是model
      baseDir: 'model2',
      host: '127.0.0.1',
      port: 3306,
      database: 'db2',
      username: 'root',
      password: 'root123456',
      timezone: '+08:00',
      define: {
        freezeTableName: true, // 强制表名称等于模型名称
        underscored: false,
      },
    }
  ],
};

 

model2需要手动编写model文件

// model2/title2.js
"use strict"
module.exports = app=>{
	const {
		INTEGER,
		STRING,
		DATE
	} = app.Sequelize;
	const Title2 = app.Model2.define('student1',{
		Id: {
			type: STRING,
		},
		age1: INTEGER,
		name: STRING,
		birth: DATE
	}, {
		freezeTableName: true,
		timestamps: false
	})
	return Title2;
}

 

使用方式

//默认
await this.ctx.model.CmsDoc.findOne({where: {name: id}})
//db2
await this.ctx.model2.Title2.findOne({where: {name: id}})

 

GraphQL

使用CmsWing模型管理创建会在app/graphql目录下自动生成对应的 GraphQL 接口。app/graphql目录下的文件系统自动生成,请勿手动修改,详细的graphql语法请阅读 graphql官方文档

生成命名规则 假如你的 模型名称是cms_doc ,会自动生成下面的 GraphQL 方法。

#Query
type Query {
  #cms内容主表 findAll 方法. 它生成一个标准的 SELECT 查询,该查询将从表中检索所有条目(除非受到 where 子句的限制).
  CmsDoc_findAll(where:WhereCmsDoc,order:[[String]],limit:Int,offset:Int):[CmsDoc]
  #cms内容主表 findByPk 方法使用提供的主键从表中仅获得一个条目.
  CmsDoc_findByPk(id:ID!):CmsDoc
  #cms内容主表 findOne 方法获得它找到的第一个条目(它可以满足提供的可选查询参数).
  CmsDoc_findOne(where:WhereCmsDoc):CmsDoc
  #cms内容主表 findAndCountAll 方法是结合了 findAll 和 count 的便捷方法. 在处理与分页有关的查询时非常有用,在分页中,你想检索带有 limit 和 offset 的数据,但又需要知道与查询匹配的记录总数.
  CmsDoc_findAndCountAll(where:WhereCmsDoc,order:[[String]],limit:Int!,offset:Int!):CountCmsDoc
}

#Mutation
type Mutation {
  #cms内容主表 添加
  CmsDoc_create(data:AddCmsDoc):CmsDoc
  #cms内容主表 删除
  CmsDoc_destroy(where:WhereCmsDoc!):ResDelCmsDoc
  #cms内容主表 更新
  CmsDoc_update(data:EditCmsDoc!,where:WhereCmsDoc!):ResEditCmsDoc
}

 

API调用

[POST] /graphql/admin

  • 后台接口,受后台登录和权限管理的限制
  • token,如果是不同端,比如小程序,app,调用时在 headers 中携带 token:xxxxx
  • query (必填), qraphql的查询语句,放到data里面。
  • variables (选填),qraphql的变量,放到data里面。
  • 以amis为例:

    {
    	"method": "post",
    	"url": "/graphql/admin",
    	"data": {
    		"query": "mutation AddUser($name: String!, $email: String!) {insert_user(object: { title: $title, email: $email }) { title email}}",
    		"variables": {
    			"name": "${name}",
    			"email": "${email}"
    		}
    	}
    }

[POST] /graphql/mc

  • MCenter接口,受MCenter登录限制
  • token,如果是不同端,比如小程序,app,调用时在 headers 中携带 token:xxxxx
  • query (必填), qraphql的查询语句,放到data里面。
  • variables (选填),qraphql的变量,放到data里面。
  • 以amis为例:

    {
    	"method": "post",
    	"url": "/graphql/mc",
    	"data": {
    		"query": "mutation AddUser($name: String!, $email: String!) {insert_user(object: { title: $title, email: $email }) { title email}}",
    		"variables": {
    			"name": "${name}",
    			"email": "${email}"
    		}
    	}
    }

[POST] /graphql/open

  • 开放接口,受后台开放接口管理限制,在开发接口放行的才可以使用
  • query (必填), qraphql的查询语句,放到data里面。
  • variables (选填),qraphql的变量,放到data里面。
  • 以amis为例:

    {
    	"method": "post",
    	"url": "/graphql/open",
    	"data": {
    		"query": "mutation AddUser($name: String!, $email: String!) {insert_user(object: { title: $title, email: $email }) { title email}}",
    		"variables": {
    			"name": "${name}",
    			"email": "${email}"
    		}
    	}
    }

 

查询(Query)🌰

假如我们有这么一个需求,我们在后台创建了一个模型,名称是 sys_models,我们要根据id获取这个模型的信息

在后台使用 amis 调用,amis 的api本身就支持 graphql 可以参考 amis文档

{
"initApi": {
    "method": "post",
    "url": "/graphql/admin",
    "graphql": "query($id:ID!){SysModels_findByPk(id:$id){id name desc paranoid}}",
    "data": {
      "id": "${models_id}"
    },
    "responseData": {
      "&": "${SysModels_findByPk}",
      "oldName":"${SysModels_findByPk.name}"
    }
  }
}

在jQuery中使用ajax请求

$.ajax({
   // headers: { token: "123456" }, //如果需要携带 token
    type: "post",
    url: "/graphql/mc",
    data: {
      query: "query($id:ID!){SysModels_findByPk(id:$id){id name desc paranoid}}",
      variables: { id: 1 },
    },
    success: function (result) {
      // 业务逻辑
      console.log(result);
    },
  });

微信小程序示例

 wx.request({
      method:'POST',
      url: 'http://127.0.0.1:7001/graphql/mc', 
      data: {
        query: "query($id:ID!){SysModels_findByPk(id:$id){id name desc paranoid}}",
        variables: { id: 1 },
      },
      header: {
        'content-type': 'application/json', 
        //因为小程序是跨端调用 所有需要登录的接口需要携带登录返回的token
        'token':'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7ImlkIjoxLCJjcmVhdGVkQXQiOiIyMDIyLTA5LTI3VDA3OjAzOjM5LjAwMFoiLCJ1cGRhdGVkQXQiOiIyMDIyLTA5LTI4VDA0OjUxOjA5LjAwMFoiLCJ1c2VybmFtZSI6ImFydGVybGkiLCJwYXNzd29yZCI6IjE3Mzg5NGJhY2M0Yjk1MDI0OGE1MDM0MTMyNTQzOWNhIiwiZW1haWwiOiJhcnRlcmxpQHFxLmNvbSIsIm1vYmlsZSI6IjE4NjgxODUxNjM3Iiwic3RhdGUiOnRydWUsInV1aWQiOiIwMGNjMjRkYy03YmY4LTRkZGQtODg5NS1hMTM1MWZmNmM1ODUiLCJ0aGlyZCI6bnVsbCwiYXZhdGFyIjoiaHR0cHM6Ly9vc3MuY21zd2luZy5jb20vdXBsb2FkLzRiZDJmODE0LWFhZTAtNDkxZC05MTdiLTlmNWNiNzJlZDUzOC5qcGcifSwiaWF0IjoxNjY2MTYxODkxfQ.vmu71eQm_bz79b9UDkDBQiJcuMjoxnaBOBWnZS4SHhU'
      },
      success (res) {
        console.log(res.data)
      }
    })

 

变更(Mutations)🌰

假如我们要对 sys_models 进行添加,更新,删除,我们需要使用 Mutations ,下面我们以更新为例来说明如何使用

在后台使用 amis 调用,amis 的api本身就支持 graphql 可以参考 amis文档

{
"api": {
    "url": "/graphql/admin",
    "method": "post",
    "graphql": "mutation($name:String,$desc:String,$paranoid:Boolean,$id:ID!,$oldName:String){SysModels_update(data:{name:$name,desc:$desc,paranoid:$paranoid,oldName:$oldName},where:{id:{op_eq:$id}}){ids}}"
  },
}

在jQuery中使用ajax请求

$.ajax({
   // headers: { token: "123456" }, //如果需要携带 token
    type: "post",
    url: "/graphql/mc", // MCenter 中使用
    data: {
      query: "mutation($name:String,$desc:String,$paranoid:Boolean,$id:ID!,$oldName:String){SysModels_update(data:{name:$name,desc:$desc,paranoid:$paranoid,oldName:$oldName},where:{id:{op_eq:$id}}){ids}}",
      variables: { name: 'ceshi_ceshibiao',desc:'测试',paranoid:false, id:1,oldName:'sys_models'},
    },
    success: function (result) {
      // 业务逻辑
      console.log(result);
    },
  });

微信小程序示例

 wx.request({
      method:'POST',
      url: 'http://127.0.0.1:7001/graphql/mc', //MCenter 中使用
      data: {
       query: "mutation($name:String,$desc:String,$paranoid:Boolean,$id:ID!,$oldName:String){SysModels_update(data:{name:$name,desc:$desc,paranoid:$paranoid,oldName:$oldName},where:{id:{op_eq:$id}}){ids}}",
      variables: { name: 'ceshi_ceshibiao',desc:'测试',paranoid:false, id:1,oldName:'sys_models'},
      },
      header: {
        'content-type': 'application/json', 
        //因为小程序是跨端调用 所有需要登录的接口需要携带登录返回的token
        'token':'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7ImlkIjoxLCJjcmVhdGVkQXQiOiIyMDIyLTA5LTI3VDA3OjAzOjM5LjAwMFoiLCJ1cGRhdGVkQXQiOiIyMDIyLTA5LTI4VDA0OjUxOjA5LjAwMFoiLCJ1c2VybmFtZSI6ImFydGVybGkiLCJwYXNzd29yZCI6IjE3Mzg5NGJhY2M0Yjk1MDI0OGE1MDM0MTMyNTQzOWNhIiwiZW1haWwiOiJhcnRlcmxpQHFxLmNvbSIsIm1vYmlsZSI6IjE4NjgxODUxNjM3Iiwic3RhdGUiOnRydWUsInV1aWQiOiIwMGNjMjRkYy03YmY4LTRkZGQtODg5NS1hMTM1MWZmNmM1ODUiLCJ0aGlyZCI6bnVsbCwiYXZhdGFyIjoiaHR0cHM6Ly9vc3MuY21zd2luZy5jb20vdXBsb2FkLzRiZDJmODE0LWFhZTAtNDkxZC05MTdiLTlmNWNiNzJlZDUzOC5qcGcifSwiaWF0IjoxNjY2MTYxODkxfQ.vmu71eQm_bz79b9UDkDBQiJcuMjoxnaBOBWnZS4SHhU'
      },
      success (res) {
        console.log(res.data)
      }
    })

 

GraphQL 调试

我们可以在后台点击左上角的 GraphQL 按钮进行调试

最后更新时间: 2024-03-28 16:41:29