欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

eggjs 构建后端博客系统(一),记录解决 sql 事务问题

最编程 2024-03-31 21:29:53
...

废话部分

该项目git地址:https://github.com/ht-sauce/dream-admin

惯例:个人的废话部分

个人是前端开发,但是公司有时候比较闲。所以就想着自己写点东西,老摸鱼,太没意思了,还有一个就是装逼和加薪。

后端这块我比较早以前用过express,自己开发过简单的后端管理系统。

最近一年多正式做了开发,一开始我先搭建了我的前端博客页面(现在想起来是单页面项目,并不完美,最好是用服务端渲染来开发的)。后面博客系统肯定需要有后端所以,继续用nodejs是没错的。我不打算用java,或者go语言之类的。对于我这样的菜鸟,贪多嚼不烂。而且java是我见过最笨重的语言之一。java的开发很繁琐,历史包袱重。新手教程非常不友好。自学难度入门很难。进门之后又容易被不完美的教程带偏(缺这个少那个)。

java主要原因是因为早期没有一个包管理系统(目前的go语言等没有这个问题),导致了java在包管理上的混乱。导致新手不友好。

整体项目我是模拟大项目的。

项目目前分为四大块。

博客系统(个人生活展示平台,记录平台)

会员系统(负责登录和权限的统一管理,将登录和人员部分单独分离)

接口管理系统(就是一个接口记录平台)

后端管理平台

想法上非常美好,但是我目前为止后端才刚开始搞。前端基本页面差不多,但是没有后端的数据配合,写了总感觉缺少什么。虽然也能写啦,但是和后端不同,前端我写了一年多。感觉没有什么大挑战了。只是写的慢和快的问题,加上公司也不一定会有一些我设想中的东西。所以自己写后端,自己玩。(但是自己还是很懒,三个月才这么点)。

后端项目选型

一、框架选型

express肯定不会用了,所以后端我也就剩下koa框架了。(这块主要是参考招聘网站中常用的框架,而且结合市场去学习选型)

koa框架我官网看过,比express好很多。但是koa有种react的感觉,什么都需要自己弄。express也是一样的。

后面在逛论坛(cnode社区)下,发现cnode基本放弃express使用koa,jest和eggjs框架。其中eggjs是最多的。然后也去官网看了看。

eggjs最大的优点是他是阿里的,它用于双十一了。还有eggjs帮初学者和新手继承了非常多的功能。就像vue脚手架,初始就给你把架子搭建好了。你不再需要自己去做那些繁琐的事情,而且是业界大神帮你搭建的。

举例:

基础安全机制

日志系统

多进程负载

等……

其中多进程负载是我很看重的,因为这个新手老手都不一定能搞定。这是nodejs能利用多核cpu的关键。

二、基础学习

推荐我的这个文章看看,很好的入门视频教程。下载不了的请留言。我找时间自己上传百度云看看。

https://juejin.cn/post/6844903933324820493

三、插件选型

因为egg对后端的基础环境做了非常良好的集成。所以我们不再需要做繁琐的初始工作注重开发就行了。但是还是需要做基础的选型。主要是数据库连接上。

我说句不好听的,为什么后端程序员常常会被嘲讽为增删改查工程师。甚至是一些后端人员认为自己很牛逼(实际上只是会增删改查)。这不懂后端的大家不知道,懂了的会发现。后端入门之后基本上的事情就是连接数据库查询结果,插入数据,返回数据。就是这么简单会sql,甚至是部分代码只需要复制黏贴就行了。格式整齐划一。所以后端我认为主要牛逼在多年经验和算法。一个初级的后端程序员比前端简单,但是后端比前端工资高。为什么呢?因为前端没了后端走不了业务,后端可以离开前端(简单的页面)。其实非常不公平,如果两者分开,初级前端比初级后端强。特别是当下的前端环境。

说了这么多,其实所有的一切在初级后端下,我们只需要搞定框架和数据库连接插件就ok了。恩,还有写接口。

mongdb:egg-mongoose(也就是mongoose)

在数据库上面nodejs目前分为两大块,一个是mongdb和mysql。一个是nosql一个是关系型数据库。两者我都用过,但是在nosql上我表示自己不够熟练,还有就是在数据库建模方面总觉得有问题所以我放弃了。

那么只有mysql了。

mysql的选择不多,主要是mysql官方插件和orm插件(sequelize

比较方面:

mysql:

egg-mysql:

就是传统的sql语句,主要缺点是开发人员容易发生数据库语句拼接,发生sql注入。

egg-sequelize

很多人说性能问题,这个可以忽略不计。最大问题在于非关系型语句操作。全部是封装好的对象编写方式。关键在于学习了sql语句之后还需要学习这个。但是不得不说,orm框架简化了一些sql的编写问题。

接口整合方面:

graphql:

非常不喜欢,文档少,而且繁琐。总感觉目前而已意义不大。虽然很诱人的感觉。

四、连接数据库。

基础的数据库安装我就不教了。我用mysql8.X

数据库连接很简单,egg封装上都做好了。

先执行

npm install --save egg-sequelize mysql2

然后配置数据库连接

\dream-admin\config\plugin.js

配置插件

'use strict';

/** @type Egg.EggPlugin */
module.exports = {
  static: {
    enable: true,
  },
  sequelize: {
    enable: true,
    package: 'egg-sequelize',
  },
};

配置数据库连接(mysql时间格式转化也写好了。看配置)

\dream-admin\config\plugin.js\config.default.js

/* eslint valid-jsdoc: "off" */

'use strict';

module.exports = appInfo => {
  const config = exports = {
    static: { prefix: '/dreamdht/' },
    keys: appInfo.name + '_1568770372144_7988',
    security: {
      csrf: {
        ignore: () => true,
      },
    },
  };
  config.sequelize = {
    dialect: 'mysql', // support: mysql, mariadb, postgres, mssql
    database: 'dream',
    host: 'localhost',
    port: 9906,
    username: 'root',
    password: '1111',
    timezone: '+08:00',
    // 时间格式转化
    dialectOptions: {
      dateStrings: true,
      typeCast: true,
    },
  };
  return config;
};

五、写模型

orm框架和传统数据库插件相比,多了模型的编写这一步。相当于数据库建表,建模。

egg框架所有业务模型都放在app\model文件下面

我的是这样的


拿出一个模型例子:userInfo

这里我不做多余延伸。因为这篇文章主要是记录数据库操作的问题。整篇文章涉及很多知识点。每一项都需要很大的精力去阅读文档。特别是没有后端基础那更加是看不了的。

'use strict';
module.exports = app => {
  const { STRING, INTEGER, DATE, ENUM } = app.Sequelize;

  const UserInfo = app.model.define('consumer-userInfos', {
    userid: {
      type: INTEGER,
      primaryKey: true,
      comment: 'user账号表id',
    },
    nickname: {
      type: STRING(30),
      allowNull: false,
      defaultValue: '',
      comment: '昵称',
    },
    portrait: {
      type: STRING(200),
      comment: '用户头像',
    },
    sex: {
      type: ENUM('男', '女'),
      comment: '性别',
    },
    phone: {
      type: STRING(15),
      comment: '联系电话',
      unique: true,
    },
    birthday: {
      type: DATE,
      comment: '生日',
    },
    provinceAndCity: {
      type: STRING(30),
      comment: '省市区域码',
    },
    address: {
      type: STRING(100),
      comment: '详细住址',
    },
    qq: {
      type: STRING(100),
      comment: 'qq或微信信息',
    },
    email: {
      type: STRING(50),
      comment: '邮箱地址',
      unique: true,
    },
  }, { comment: '用户信息表,每个用户唯一' });

  return UserInfo;
};


六、服务编写(service层,业务逻辑编写全部在这里)

这里一样说文章主要目的。

下面的语句包含两部分,一个是sql事务,还有一个是关于现代nodejs框架配合async\await如何处理错误。

sql事务,主要看注释。我之前犯错主要在这里

await ctx.model.Consumer.UserInfo.create(data, { transaction });

错误的方式:

await ctx.model.Consumer.UserInfo.create(data, transaction );

错误处理

eggjs在错误处理上和传统的后端很像了,主要是靠try\catch捕获错误。

然后我这里的逻辑调用在于我是控制层访问——服务层——访问——数据库

那么服务层其实就能捕获数据库错误了。但是在控制层我一样是用try\catch来捕获错误。那么你应该在我下面代码一样。用

// 返回错误信息
return Promise.reject(e);
// 或throw e;

来返回错误结果,保证控制层也正常的报错

直接上代码:

'use strict';
const Service = require('egg').Service;

class UserService extends Service {
  async find() {
    const { ctx, app } = this;
    const sequelize = app.Sequelize;
    const query = [[ sequelize.fn('COUNT', sequelize.col('id')), 'num' ]];
    // console.log(result[0].dataValues.num);
    return await ctx.model.Consumer.User.findAll({
      attributes: query,
    });
  }
  // 创建用户账号和用户信息
  async create(data) {
    // 获取当前条目数加1作为主键id
    const { ctx, app } = this;

    const sequelize = app.Sequelize;
    const query = [[ sequelize.fn('COUNT', sequelize.col('id')), 'num' ]];
    const count = await ctx.model.Consumer.User.findAll({
      attributes: query,
    });

    const id = count[0].dataValues.num + 1;
    data.id = id;
    data.userid = id;
    let transaction;
    try {
      // 启用事务
      transaction = await ctx.model.transaction();
      // 创建账号
      await ctx.model.Consumer.UserInfo.create(data, { transaction });
      await ctx.model.Consumer.User.create(data, { transaction });
      // 提交事务
      await transaction.commit();
      return true;
    } catch (e) {
      // 错误事务回滚
      await transaction.rollback();
      // 返回错误信息
      return Promise.reject(e);
    }
  }
}

module.exports = UserService;


七:总结

文章主要三点:

Sequelize下面事务的使用。格式很标准。

// 启用事务 

 transaction = await ctx.model.transaction();

// 创建账号 

sql业务操作 

//提交事务

transaction.commit();

当发生错误的时候进行回滚

await transaction.rollback();

然后是事务使用的时候注意事项。代码格式别错了。

还有就是mysql时区导致查询出来的结果时间格式不对

八、致谢

感谢eggjs团队开源框架

参考文章:

sequelize中文文档:https://demopark.github.io/sequelize-docs-Zh-CN/

参考博客:https://blog.****.net/awhlmcyn/article/details/79816494

参考博客:https://blog.****.net/clearlxj/article/details/94597734