鋼材信息小程序開發(fā)總結(jié)(三) ---EggJS

整體項目代碼
鋼材信息小程序開發(fā)總結(jié)(一) --- 整體介紹
鋼材信息小程序開發(fā)總結(jié)(二) ---uniapp

這次是對后端部分的介紹

一恤浪、系統(tǒng)環(huán)境介紹

  • NodeJS
  • MySQL
  • Redis

1.NodeJS

JS服務(wù)端運行環(huán)境

參考資料:

2.MySQL數(shù)據(jù)庫

開源關(guān)系型數(shù)據(jù)庫

參考資料:

3.Redis

開源畅哑、支持網(wǎng)絡(luò)、基于內(nèi)存水由、可選持久性的鍵值對存儲數(shù)據(jù)庫

參考資料:


二荠呐、EggJS 相關(guān)插件

  • egg-sequelize
  • egg-redis
  • egg-jwt
  • egg-router-plus
  • egg-validate
  • egg-cors
  • egg-view-nunjucks
  • egg-zrole

1.egg-sequelize

Sequelize的EggJS插件. Sequelize是MySQL的ORM框架

參考資料:

2.egg-redis

Redis的EggJS插件

參考資料:

3.egg-jwt

EggJS的JWT插件

參考資料:

4.egg-router-plus

EggJS的路由加強插件, 主要用于給路由統(tǒng)一添加前綴

參考資料:

5.egg-validate

EggJS的參數(shù)校驗插件

參考資料:

6.egg-cors

EggJS的訪問控制插件

參考資料:

7.egg-view-nunjucks

EggJS的模板渲染引擎插件, 語法類似 Django 的 jinjia

參考資料:

8.egg-zrole

EggJS的基于Casbin的權(quán)限控制插件

參考資料:


三、項目目錄

.
├── app.js
├── server.js
├── app
│   ├── router.js 
│   ├── controller            
│   ├── extend           
│   ├── middleware
│   ├── service
│   ├── public
│   ├── view
│   ├── router
│   └── model (自建Sequelize目錄)
└──  config
    ├── plugin.js
    ├── config.default.js
    └── config.prod.js

以上目錄介紹如下:

  1. app/router.js 用于配置URL路由規(guī)則砂客。
  2. app/controller/ 用于解析用戶輸入泥张,處理后返回響應(yīng)結(jié)果。
  3. app/extend/ 用于框架內(nèi)部對象的拓展(request,response,context,application)和工具類(helper)的編寫鞠值。
  4. app/middleware/ 用于編寫中間件圾结。
  5. app/service/ 用于編寫業(yè)務(wù)邏輯,如數(shù)據(jù)庫操作的封裝齿诉,api請求的封裝等。
  6. app/public/ 用于放置靜態(tài)文件晌姚。
  7. app/view/ 用于放置模板文件粤剧。
  8. app/model/ 用于放置數(shù)據(jù)模型。
  9. app/router/ 用戶放置分離的路由
  10. app.js 用于自定義啟動時的初始化工作挥唠。
  11. server.js 用于使用pm2部署 pm2 start server.js

四抵恋、相關(guān)業(yè)務(wù)邏輯

1.分頁邏輯

分頁中間件, 獲取分頁及其他查詢參數(shù)

module.exports = async (ctx, next) => {
  const { query } = ctx.request

  ctx.validate({
    pageField: { type: 'string', required: false },
    pageSort: { type: 'enum', values: ['ASC', 'DESC'], required: false },
    pageSize: { type: 'string', format: /\d+/, required: false },
    pageIndex: { type: 'string', format: /\d+/, required: false },
  }, query)

  const {
    pageField = 'updated_at',
    pageSort = 'DESC',
  } = query
  // ~~ 用于 Math.floor 的功能, 以及將 undefined, null 轉(zhuǎn)為數(shù)字 0
  const pageSize = ~~query.pageSize || 20
  const pageIndex = ~~query.pageIndex || 1

  ctx.page = { pageField, pageSort, pageSize, pageIndex }

  return next()
}

求和服務(wù), 獲取列表總數(shù)

async findCount({ modelName, where = {} }) {
  return this.ctx.model[modelName].count({
    where
  })
}

根據(jù)查詢參數(shù)獲取列表數(shù)據(jù)

async findPage({
  modelName,
  where = {},
  page,
  attributes = {},
  include = [],
}) {
  const {
    pagination
  } = this.ctx.helper
  const {
    order,
    offset,
    limit
  } = pagination(page)
  return this.ctx.model[modelName].findAll({
    where,
    order,
    offset,
    limit,
    attributes,
    include,
  })
}

示例
1.controller, 獲取用戶日志

async getUserLog() {
  const { ctx, app } = this
  let { page } = ctx
  const { common } = ctx.service
  console.log(page)
  page = {
    ...page,
    pageField: 'id',
    pageSort: 'DESC'
  }
  const [total, items] = await Promise.all([
    common.findCount({ modelName: 'Log' }),
    common.findPage({ modelName: 'Log', page }),
  ])
  const res = { total, items, pageSize: page.pageSize, pageIndex: page.pageIndex }
  ctx.helper.success({ ctx, res: res })
}

2.配置路由, 引入分頁中間件

apiV1Router.get('/log', app.jwt, middleware.pagination, controller.api.user.getUserLog)

2.文件上傳

1.后端controller

class UploadController extends Controller {
  async index() {
    const { ctx } = this
    const stream = await ctx.getFileStream()
    ctx.helper.success({ctx, res: 'upload ok' + stream})
  }
}

2.前端上傳文件

<label for="file">upload</label>
<input type="file" id="file">
<script>
  var file = document.querySelector('#file')
  file.addEventListener('change', function(e) {
    var f = e.target.files[0]
    var formdata = new FormData()
    formdata.append('file', f)
    console.log(formdata)
    fetch(`http://localhost:7001/upload`, {
      method: 'POST',
      body: formdata
    })
      .then(res => res.json())
      .then(res => {
        e.target.value = null
        console.log(res.data)
      })
  })
</script>

3.小程序登錄

http://www.reibang.com/p/8df2213f6f9d

4.小程序統(tǒng)計數(shù)據(jù)獲取

由于請求獲取access_token的次數(shù)有限制, 所以將其存入redis中緩存

async getWechatToken() {
  const {ctx, app} = this
  let result = await ctx.helper.request({ctx, url: `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${app.config.weapp.appId}&secret=${app.config.weapp.secret}`})
  app.redis.set('weToken', result.data.access_token, 'EX', result.data.expires_in)
  return result.data.access_token
}

使用access_token請求獲取前一天的用戶畫像
注意: 在零點前后數(shù)據(jù)會獲取不到

async getWechatUser() {
  const {ctx, app} = this
  const yesterday = dayjs().subtract(1, 'day').format("YYYYMMDD")
  let token = await app.redis.get('token');
  if(token && token !== '') {
    const result = await ctx.helper.request({
      ctx, 
      url: `https://api.weixin.qq.com/datacube/getweanalysisappiduserportrait?access_token=${token}`,
      method: 'POST',
      data: {
        begin_date: yesterday,
        end_date: yesterday
      }
    })
    return result
  } else {
    token = await ctx.service.data.getWechatToken()
    const result = await ctx.helper.request({
      ctx, 
      url: `https://api.weixin.qq.com/datacube/getweanalysisappiduserportrait?access_token=${token}`,
      method: 'POST',
      data: {
        begin_date: yesterday,
        end_date: yesterday
      }
    })
    return result
  }
}

5.熱門鋼材排序

按照新的順序, 遍歷數(shù)據(jù)庫中熱門鋼材的sort字段

async setSortById(list) {
  let arr = []
  list.forEach(v => {
    arr.push(this.ctx.model.Hot.update(
      {sort: v.sort},
      {
        where: {
          id: v.id
        }
      }
    ))
  })
  const result = await Promise.all(arr)
  if(Array.isArray(result) && result.length > 0) {
    return true
  } else {
    return false
  }
}

6.HTTP請求封裝

request: async ({ctx, method = 'GET', url='', data = {}}) => {
  const result = await ctx.curl(url, {
    method: method,
    contentType: 'json',
    dataType: 'json',
    data: data
  })
  return result
}

7.響應(yīng)體封裝

/**
 * 錯誤碼
 */
errorCode: {
  200: '服務(wù)器成功返回請求的數(shù)據(jù)。',
  201: '新建或修改數(shù)據(jù)成功宝磨。',
  202: '一個請求已經(jīng)進入后臺排隊(異步任務(wù))弧关。',
  204: '刪除數(shù)據(jù)成功。',
  400: '發(fā)出的請求有錯誤唤锉,服務(wù)器沒有進行新建或修改數(shù)據(jù)的操作世囊。',
  401: '用戶沒有權(quán)限(令牌、用戶名窿祥、密碼錯誤)株憾。',
  403: '用戶得到授權(quán),但是訪問是被禁止的。',
  404: '發(fā)出的請求針對的是不存在的記錄嗤瞎,服務(wù)器沒有進行操作墙歪。',
  406: '請求的格式不可得。',
  410: '請求的資源被永久刪除贝奇,且不會再得到的虹菲。',
  422: '當創(chuàng)建一個對象時,發(fā)生一個驗證錯誤掉瞳。',
  500: '服務(wù)器發(fā)生錯誤毕源,請檢查服務(wù)器。',
  502: '網(wǎng)關(guān)錯誤菠赚。',
  503: '服務(wù)不可用脑豹,服務(wù)器暫時過載或維護。',
  504: '網(wǎng)關(guān)超時衡查。'
}

/**
 * 處理成功請求后的響應(yīng)
 */
success: ({ ctx, code = 200, res = {}, msg }) => {
  if (!res) code = 404
  ctx.status = 200
  ctx.body = {
    code: code,
    message: msg || ctx.helper.errorCode[code],
    data: res
  }
}

/**
 * 處理失敗請求后的響應(yīng)
 */
fail: ({ ctx, code = 500, res = {}, msg }) => {
  ctx.status = 200
  ctx.body = {
    code: code,
    message: msg || ctx.helper.errorCode[code],
    data: res
  }
}

8.數(shù)據(jù)庫初始化

  1. 第一次啟動, 取消掉app.js中注釋即可
'use strict';

module.exports = app => {
  if (app.config.env === 'local') {
    app.beforeStart(async () => {
      await app.model.sync({ force: true })
    })
  }
}
  1. 之后注釋掉
await app.model.sync({ force: true })
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瘩欺,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子拌牲,更是在濱河造成了極大的恐慌俱饿,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,734評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件塌忽,死亡現(xiàn)場離奇詭異拍埠,居然都是意外死亡,警方通過查閱死者的電腦和手機土居,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,931評論 3 394
  • 文/潘曉璐 我一進店門枣购,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人擦耀,你說我怎么就攤上這事棉圈。” “怎么了眷蜓?”我有些...
    開封第一講書人閱讀 164,133評論 0 354
  • 文/不壞的土叔 我叫張陵分瘾,是天一觀的道長。 經(jīng)常有香客問我吁系,道長德召,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,532評論 1 293
  • 正文 為了忘掉前任汽纤,我火速辦了婚禮上岗,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘冒版。我一直安慰自己液茎,他們只是感情好,可當我...
    茶點故事閱讀 67,585評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著捆等,像睡著了一般滞造。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上栋烤,一...
    開封第一講書人閱讀 51,462評論 1 302
  • 那天谒养,我揣著相機與錄音,去河邊找鬼明郭。 笑死买窟,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的薯定。 我是一名探鬼主播始绍,決...
    沈念sama閱讀 40,262評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼话侄!你這毒婦竟也來了亏推?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,153評論 0 276
  • 序言:老撾萬榮一對情侶失蹤年堆,失蹤者是張志新(化名)和其女友劉穎吞杭,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體变丧,經(jīng)...
    沈念sama閱讀 45,587評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡芽狗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,792評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了痒蓬。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片童擎。...
    茶點故事閱讀 39,919評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖攻晒,靈堂內(nèi)的尸體忽然破棺而出柔昼,到底是詐尸還是另有隱情,我是刑警寧澤炎辨,帶...
    沈念sama閱讀 35,635評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站聪姿,受9級特大地震影響碴萧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜末购,卻給世界環(huán)境...
    茶點故事閱讀 41,237評論 3 329
  • 文/蒙蒙 一破喻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧盟榴,春花似錦曹质、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,855評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽几莽。三九已至,卻和暖如春宅静,著一層夾襖步出監(jiān)牢的瞬間章蚣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,983評論 1 269
  • 我被黑心中介騙來泰國打工姨夹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留纤垂,地道東北人。 一個月前我還...
    沈念sama閱讀 48,048評論 3 370
  • 正文 我出身青樓磷账,卻偏偏與公主長得像峭沦,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子逃糟,可洞房花燭夜當晚...
    茶點故事閱讀 44,864評論 2 354