微信小程序“二手交易商城”(后端egg.js+sequelize+mysql)全栈开发

2,126 阅读5分钟

微信小程序 校园闲置商城

项目地址github.com/PH-C/wx_sec…(喜欢的请点个star^_^) 

这个项目是一个简单的校园闲置二手商城微信小程序由本人独立开发,包含微信小程序和配套的后台管理系统,用户可以通过微信小程序发布闲置,后台管理系统也可以发布管理商品,类似于京东的自营和非自营的二手商城。含以下功能:商品列表与搜索、商品收藏分享、商品评论与回复,商品下单、发布闲置、我的订单、我的地址管理、我的收藏、我的闲置、系统公告、钱包充值、登陆注册等功能。

基于react技术栈开发的简单商城后台系统提供用户管理、商品管理、订单管理、公告管理等功能。

项目中有个后端服务,即项目中的egg_second,该后端使用nodejs框架蚂蚁金服的egg.js开发restful风格的数据接口,采用mysql作为数据库,使用sequelize 这个ORM 框架来定义数据表模型,以及对数据库进行crud操作,使用jwt token认证实现登陆鉴权功能。

技术栈

  • react
  • react-router
  • axois
  • nodejs
  • eggjs
  • mysql
  • sequelize

项目node后端运行方法

#1
git clone https://github.com/PH-C/wx_secondhand.git

#2 
cd egg_second
npm install

#3
请先通过mysql命令行或者mysql可视化工具创建一个数据库
然后修改config.default.js中sequelize的配置
配置中mysql的账号密码以及数据库名称改为自己本地的

#4
npm run dev(由于项目中使用sequelize定义了表结构会自动创建表,这个很方便^_^)

项目react后台管理系统运行方法

#1
git clone https://github.com/PH-C/wx_secondhand.git

#2 
cd react_second
npm install

#3
npm run start

项目简介

项目采用Egg.js作为后端Node.js框架,正如****Egg.js 所说是一个企业级别的框架,帮助开发团队和开发人员降低开发和维护成本。egg.js官网:eggjs.org/zh-cn/

Config 配置,在这里可以配置数据库连接配置,跨域白名单配置,jwt密钥配置等等

'use strict';
// const sqlConfig = require('../config');
module.exports = appInfo => {
  const config = exports = {};

  // use for cookie sign key, should change to your own and keep security
  config.keys = appInfo.name + '_1520690141955_3949';

  // add your config here
  config.middleware = [ 'errorHandler' ]

  config.sequelize = {
    dialect: 'mysql', // support: mysql, mariadb, postgres, mssql
    // dialectOptions: {
    //   charset: 'utf8mb4',
    // },
    database: 'egg_sechand',
    host: '127.0.0.1',
    port: '3306',
    username: 'root',
    password: '123',
    timezone: '+08:00',
  };

  config.security = {
    csrf: {
      enable: false,
    },
    domainWhiteList: [ 'http://localhost:3000' ],
  };

  config.cors = {
    credentials: true,
  };

  config.jwt = {
    secret: 'Great4-M',
    enable: true, // default is false
    match: '/jwt', // optional
  }

  return config;
};

router路由配置:这里采用了restful风格的api,对于需要jwt token 验证的接口,需要在 第二个参数中加入 app.jwt

'use strict';

module.exports = app => {
  // const { router, controller } = app

  app.post('/api/users/register', 'user.create');
  app.post('/api/users/login', 'user.login');
  app.del('/api/users/:id', app.jwt,'user.destroy');
  app.put('/api/users/recharge', app.jwt,'user.recharge');
  app.put('/api/users/pay', app.jwt,'user.pay');
  app.put('/api/users/current', app.jwt,'user.updateLoginUser');
  app.put('/api/users/:id', 'user.update');

  app.post('/api/users/adminlogin','user.adminLogin')
  app.get('/api/users/current',app.jwt,'user.current')
  app.get('/api/users', app.jwt, 'user.list')
  app.get('/api/authority', app.jwt, 'user.authoritylist')
  app.get('/api/users/:id', 'user.find');

  app.get('/api/users/:id/edit', 'user.find');
};

控制器(Controller)Controller 负责解析用户的输入,处理后返回相应的结果

'use strict';

const Controller = require('egg').Controller;

class UserController extends Controller {
  async create() {
    const {
      ctx,
    } = this;
    ctx.body = await ctx.service.user.create(ctx.request.body);
  }

  async destroy() {
    const {
      ctx,
    } = this;
    const id = +ctx.params.id;
    ctx.body = await ctx.service.user.del(id);
  }

  async update() {
    const {
      ctx,
    } = this;
    const id = +ctx.params.id;
    const user = ctx.request.body;
    ctx.body = await ctx.service.user.update({
      id,
      user,
    });
  }

  async updateLoginUser(){
    const {
      ctx,
    } = this;
    const user = ctx.request.body;
    ctx.body = await ctx.service.user.updateLoginUser({
      user,
    });
  }

  async recharge(){
    const {
      ctx,
    } = this;
    const id = +ctx.params.id;
    const user = ctx.request.body;
    ctx.body = await ctx.service.user.recharge(user);
  }

  async pay(){
    const {
      ctx,
    } = this;
    // const id = +ctx.params.id;
    const user = ctx.request.body;
    ctx.body = await ctx.service.user.pay(user);
  }

  async login() {
    const {
      ctx,
    } = this;
    const {
      username,
      password,
    } = ctx.request.body;
    ctx.body = await ctx.service.user.login({
      username,
      password,
    });
  }

  async list(){
    const {
      ctx,
    } = this;
    const res = await ctx.service.user.list(ctx.query);
    ctx.body = res;
  }

  async find() {
    const {
      ctx,
    } = this;
    const id = +ctx.params.id;
    ctx.body = await ctx.service.user.find(id);
  }

  async current() {
    const {
      ctx,
    } = this;
    ctx.body = await ctx.service.user.current();
  }

}

module.exports = UserController;

服务(Service)Service 就是在复杂业务场景下用于做业务逻辑封装的一个抽象层

'use strict';

const Service = require('egg').Service;
const md5 = require('js-md5')
const {
  ERROR,
  SUCCESS,
} = require('../util/util');
class UserService extends Service {
  async create(user) {
    const {
      ctx,
    } = this;
    try {
      if (!user.username || !user.password) {
        ctx.status = 400;
        return Object.assign(ERROR, {
          msg: `expected an object with username, password but got: ${JSON.stringify(user)}`,
        });
      }
      const md5Passwd = md5(user.password)
      user = Object.assign(user, {
        password: md5Passwd,
      });
      const userDB = await ctx.model.User.findOne({
        where: {
          username: user.username,
        },
      });
      if (!userDB) {
        const res = await this.ctx.model.User.create({ ...user, "authority_id": 1 });
        ctx.status = 201;
        return Object.assign(SUCCESS, {
          data: res,
        });
      }
      ctx.status = 406;
      return Object.assign(ERROR, {
        msg: 'username already exists',
      });

    } catch (error) {
      ctx.status = 500;
      throw (error);
    }
  }

  async del(id) {
    const {
      ctx,
    } = this;
    try {
      const user = await ctx.model.User.findById(id);
      if (!user) {
        ctx.status = 400;
        return Object.assign(ERROR, {
          msg: 'user not found',
        });
      }
      const res = await user.update({delete:1});
      // user.destroy();
      ctx.status = 200;
      return Object.assign(SUCCESS, {
        data: user,
      });

    } catch (error) {
      ctx.throw(500);
    }
  }

  async update({ id, user }) {
    const {
      ctx,
    } = this;
    try {
      const userDB = await ctx.model.User.findById(id);
      if (!userDB) {
        ctx.status = 400;
        return Object.assign(ERROR, {
          msg: 'user not found',
        });
      }
      const md5Passwd = md5(user.password)
      user = Object.assign(user, {
        password: md5Passwd,
      });
      const res = await userDB.update(user);
      ctx.status = 200;
      return Object.assign(SUCCESS, {
        data: res,
      });

    } catch (error) {
      ctx.throw(500);
    }
  }

  async recharge({
    money = 0
  }) {
    const {
      ctx,
    } = this;
    const id = ctx.state.user.data.id
    try {
      const userDB = await ctx.model.User.findById(id);
      if (!userDB) {
        ctx.status = 400;
        return Object.assign(ERROR, {
          msg: 'user not found',
        });
      }

      if (money) {
        money = Number(userDB.money) + Number(money)
      }

      const res = await userDB.update({ money: money });
      ctx.status = 200;
      return Object.assign(SUCCESS, {
        data: res,
        msg: "充值成功!"
      });

    } catch (error) {
      ctx.throw(500);
    }
  }

  async pay({
    money = 0
  }) {
    const {
      ctx,
    } = this;
    const id = ctx.state.user.data.id
    try {
      const userDB = await ctx.model.User.findById(id);
      if (!userDB) {
        ctx.status = 400;
        return Object.assign(ERROR, {
          msg: 'user not found',
        });
      }

      money = userDB.money - money
      if (money < 0) {
        return { ...ERROR, msg: "余额不足,请充值!" }
      }

      const res = await userDB.update({ money: money });
      ctx.status = 200;
      return Object.assign(SUCCESS, {
        data: res,
        msg: "支付成功!"
      });

    } catch (error) {
      ctx.throw(500);
    }
  }

  async login({ username, password }) {
    const {
      ctx,
    } = this;
    try {
      const user = await ctx.model.User.findOne({
        where: {
          username: username.toString(),
          delete: 0
        },
      });
      if (!user) {
        return Object.assign(ERROR, {
          msg: 'username is error',
        });
      }

      if (md5(password) === user.password) {
        ctx.status = 200;
        const hash = md5.hex(password)

        return Object.assign(SUCCESS, {
          data: Object.assign(user, {
            password: '',
          }),
        }, { token: await ctx.service.actionToken.apply(user.id) });
      }
      return Object.assign(ERROR, {
        msg: 'password is error',
      })

    } catch (error) {
      ctx.status = 500;
      throw (error);
    }
  }

  async list({
    page = 1,
    pageSize = 10,
    order_by = 'created_at',
    order = 'DESC',
  }) {
    const {
      ctx,
    } = this;
    const {
      Op,
    } = this.app.Sequelize;

    pageSize = pageSize || 10                           //一页多少条
    const currentPage = page || 1                  //设置当前页默认第一页
    const skipNum = (currentPage - 1) * pageSize   //跳过数
    let options = {
      offset: parseInt(skipNum),
      limit: parseInt(pageSize),
      order: [
        [order_by, order.toUpperCase()],
      ],
      where: {
        delete:0
      }
    }

    try {
      const res = await ctx.model.User.findAndCountAll({
        ...options,
        include: [{
          model: ctx.model.Authority,
          attributes: ['id', 'name'],
        }]
      })
      ctx.status = 200
      return {
        ...SUCCESS,
        data: res,
        pageSize: pageSize
      }
    } catch (error) {
      ctx.status = 500;
      throw (error);
    }
  }

  async find(id) {
    const {
      ctx,
    } = this;
    try {
      let user = await ctx.model.User.findById(id, {
        include: [{
          model: ctx.model.Authority,
          attributes: ['id', 'name'],
        }],
      });
      if (!user) {
        ctx.status = 401;
        return Object.assign(ERROR, {
          msg: 'user not found',
        });
      }
      ctx.status = 200;
      user = Object.assign(user, {
        password: '',
      })
      return {
        ...SUCCESS,
        data: user
      }

    } catch (error) {
      throw (500);
    }
  }

  async current() {
    const {
      ctx,
    } = this;
    const id = ctx.state.user.data.id

    try {
      const userInfo = await ctx.model.User.findById(id, {
        include: [{
          model: ctx.model.Authority,
          attributes: ['id', 'name'],
        }],
      });
      if (!userInfo) {
        // ctx.status = 401;
        return Object.assign(ERROR, {
          msg: 'user not found',
        });
      }
      ctx.status = 200;
      return Object.assign(SUCCESS, {
        data: userInfo,
      });

    } catch (error) {
      throw (500);
    }

  }

}

module.exports = UserService;

参考文档

Egg官方文档

Sequelize(英文)

Sequelize(中文)

enter image description here enter image description here enter image description here enter image description here