Realize a word battle game from scratch (8) Practical chapter 4: Cloud development architecture construction and homepage data joint debugging

Realize a word battle game from scratch (8) Practical chapter 4: Cloud development architecture construction and homepage data joint debugging

This article begins to enter the key content of cloud development, and introduces you to the current practical experience in the word Tiantiandou

Basic knowledge of cloud development

Most companies and developers need to consider server, storage, and database requirements in advance when developing applications and deploying services, whether they choose public clouds or self-built data centers, and they need to spend time and energy in deploying applications and dependencies. So is there a structure that can help us save this part of the cost? This is the Serverless (serverless) architecture. Specifically, the serverless architecture means that a third-party cloud computing provider is responsible for the maintenance of the back-end infrastructure, and provides developers with required functions such as database, messaging, and identity verification in a service manner. In short, this architecture is to allow developers to focus on the operation of the code without the need to manage any infrastructure. Mini Program Cloud Development is a way to implement Serverless architecture.

More straightforward: Individual developers can use the cloud service provided by the mini program for free when the mini program is not visited (DAU[ ] is between 0 and 500), which is very friendly to individual developers. Please refer to the specific price.

Cloud development currently provides three basic capabilities:

  • Cloud function: Code running in the cloud, WeChat private protocol natural authentication, developers only need to write their own business logic code; provide server-side capabilities for the front end of the applet, and support timing automatic triggering, etc.;
  • Database: A JSON database that can be operated on the front end of the applet and can be read and written in cloud functions;
  • File storage: upload/download cloud files directly on the front end of the applet, and visually manage them on the cloud development console.

For more information, please refer to the detailed documentation of cloud development

Cloud Development Practice in Word Tiantiandou

Infrastructure

Due to the small cloud development program, generally you do not need to use wx.requestto request the server data, and before all the usual server api file management in a unified, cloud development is not necessary. However, in cloud development, it is also necessary to obtain server-side data, so it is necessary to encapsulate the data acquisition. However, the code is very scattered in the subsequent period. The following describes the practical content of the word Tiantiandou

In the front-end code directory of the applet, create a new modelfolder, in which the file and the data set (data table name) are consistent; in many back-end frameworks, such as thinkPHP, thinkJS, egg, there are similar practices, and refer to it in cloud development. The data model layer encapsulates all database operations (including cloud function operations on the corresponding database)

  model
|    base.js #  
|    book.js #  
|    index.js #   ( )
|    room.js #  
|    sign.js #  
|    user.js #  
|    userWord.js #  
|    word.js #  
 

Code practice

  • Base class
//base 
import $ from './../utils/Tool'

const DB_PREFIX = 'pk_' // 

export default class {
  constructor(collectionName) {
    const env = $.store.get('env') // 
    const db = wx.cloud.database({ env }) // 
    this.model = db.collection(`${DB_PREFIX}${collectionName}`) // 
    this._ = db.command // db.command 
    this.db = db
    this.env = env
  }

  get date() {
    return wx.cloud.database({ env: this.env }).serverDate() // 
  }

  /**
   *  
   * @param {Number} offset  ms  + -
   */
  serverDate(offset = 0) {
    return wx.cloud.database({ env: this.env }).serverDate({ offset })
  }
}

 
  • room collection

Take some functions of the room collection as an example, which contains all the data operations of the room collection

import Base from './base' // 
import $ from './../utils/Tool' // 

const collectionName = 'room' // 

export const ROOM_STATE = {
  IS_OK: 'OK', // 
  IS_PK: 'PK', // 
  IS_READY: 'READY', // 
  IS_FINISH: 'FINISH', // 
  IS_USER_LEAVE: 'LEAVE' // 
}

/**
 *  :  
 */
class RoomModel extends Base { // base 
  constructor() {
    super(collectionName)
  }

  // 
  userReady(roomId, isNPC = false, openid = $.store.get('openid')) {
    return this.model.where({
      _id: roomId,
      'right.openid': '',
      state: ROOM_STATE.IS_OK
    }).update({
      data: {
        right: { openid },
        state: ROOM_STATE.IS_READY,
        isNPC
      }
    })
  }
  
  // 
  userCancelReady(roomId) {
    return this.model.where({
      _id: roomId,
      'right.openid': this._.neq(''),
      state: ROOM_STATE.IS_READY
    }).update({
      data: {
        right: { openid: '' },
        state: ROOM_STATE.IS_OK
      }
    })
  }
  
  // PK
  startPK(roomId) {
    return this.model.where({
      _id: roomId,
      'right.openid': this._.neq(''),
      state: ROOM_STATE.IS_READY
    }).update({
      data: {
        state: ROOM_STATE.IS_PK
      }
    })
  }

  // 
  async create(list, isFriend, bookDesc, bookName) {
    try {
      const { _id = '' } = await this.model.add({ data: {
        list,
        isFriend,
        createTime: this.date,
        bookDesc,
        bookName,
        left: {
          openid: '{openid}',
          gradeSum: 0,
          grades: {}
        },
        right: {
          openid: '',
          gradeSum: 0,
          grades: {}
        },
        state: ROOM_STATE.IS_OK,
        nextRoomId: '', // id
        isNPC: false // 
      } })
      if (_id !== '') { return _id }
      throw new Error('roomId get fail')
    } catch (error) {
      log.error(error)
      throw error
    }
  }
  
  // 
  selectOption(roomId, index, score, listIndex, isHouseOwner) {
    const position = isHouseOwner ? 'left' : 'right'
    return this.model.doc(roomId).update({
      data: {
        [position]: {
          gradeSum: this._.inc(score),
          grades: {
            [listIndex]: {
              index,
              score
            }
          }
        }
      }
    })
  }

  /**
   *  
   */
  finish(roomId) {
    return this.model.where({
      _id: roomId,
      state: ROOM_STATE.IS_PK
    }).update({
      data: {
        state: ROOM_STATE.IS_FINISH
      }
    })
  }

  leave(roomId) {
    return this.model.where({
      _id: roomId,
      state: ROOM_STATE.IS_PK
    }).update({
      data: {
        state: ROOM_STATE.IS_USER_LEAVE
      }
    })
  }

  remove(roomId, state = ROOM_STATE.IS_OK) {
    return this.model.where({
      _id: roomId,
      _openid: '{openid}',
      state
    }).remove()
  }

  /**
   *  
   * 2mins 
   */
  searchRoom(bookDesc) {
    return this.model.where({
      bookDesc,
      isFriend: false,
      'right.openid': '',
      'left.openid': this._.neq($.store.get('openid')),
      state: ROOM_STATE.IS_OK,
      createTime: this._.gt(this.serverDate(-2 * 60 * 1000)) // >2 
    }).limit(1).field({ _id: true }).get()
  }

  /**
   *  
   * @param {String} roomId  id
   * @param {String} nextRoomId  id
   */
  updateNextRoomId(roomId, nextRoomId) {
    return this.model.where({
      _id: roomId,
      state: ROOM_STATE.IS_FINISH,
      nextRoomId: ''
    }).update({
      data: {
        nextRoomId
      }
    })
  }
}

export default new RoomModel()

 
  • Word book collection

In the vocabulary book, the cloud function is used to change the vocabulary book selected by the current user, so an example will be analyzed.

import Base from './base'
import $ from './../utils/Tool'
const collectionName = 'book'

/**
 *  :  
 */
class BookModel extends Base {
  constructor() {
    super(collectionName)
  }

  async getInfo() {
    const { data } = await this.model.get()
    return data
  }

  async changeBook(bookId, oldBookId, bookName, bookDesc) {
    if (bookId !== oldBookId) {
      const { result: bookList } = await $.callCloud('model_book_changeBook', { bookId, oldBookId, bookName, bookDesc }) // 
      return bookList
    }
  }
}

export default new BookModel()

 
  • Organize all collection dependencies

Importing and exporting in this way has one of the biggest advantages. After importing the collection file, you only need to import index.js.

//index.js
import userModel from './user'
import bookModel from './book'
import wordModel from './word'
import roomModel from './room'
import userWordModel from './userWord'
import signModel from './sign'

export {
  userModel,
  bookModel,
  wordModel,
  roomModel,
  userWordModel,
  signModel
}

 
  • Call method
// model 
import { userModel, bookModel, wordModel, roomModel } from './../../model/index'

// 
const bookList = await bookModel.changeBook(bookId, oldBookId, name, desc)
 

Homepage data joint debugging

Login to get openid, if it is a new user, register first

//user.js    

import Base from './base'
import $ from './../utils/Tool'
const collectionName = 'user'

/**
 *  :  
 */
class UserModel extends Base {
  constructor() {
    super(collectionName)
  }

  register() {
    return this.model.add({ data: { ...doc, createTime: this.date } })
  }

  /**
   *  
   */
  async getOwnInfo() {
    const { result: userInfo } = await $.callCloud('model_user_getInfo')
    if (userInfo === null) { // 
      await this.register()
      return (await this.getOwnInfo()) // 
    }
    $.store.set('openid', userInfo._openid)
    return userInfo
  }
}

export default new UserModel()

 

model_user_getInfoThe cloud functions are as follows:

const cloud = require('wx-server-sdk')

cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV
})

const db = cloud.database()
const userModel = db.collection('pk_user')

exports.main = async () => {
  const { OPENID: openid } = cloud.getWXContext()
  const asBook = 'book'
  const { list } = await userModel
    .aggregate()
    .match({ _openid: openid })
    .limit(1)
    .lookup({
      from: 'pk_book',
      localField: 'bookId',
      foreignField: '_id',
      as: asBook
    })
    .end()
  if (list.length === 0) { return null }
  const userInfo = {
    ...list[0],
    bookName: list[0][asBook][0].name,
    bookDesc: list[0][asBook][0].desc
  }
  delete userInfo[asBook]
  return userInfo
}

 

To home.jsget the server data from the home page , call as follows

  async onLoad() {
    await this.getData() //  +  
  }
  
  /**
   *  
   */
  async getData() {
    $.loading()
    const userInfo = await userModel.getOwnInfo()
    const bookList = await bookModel.getInfo()
    this.setData({ userInfo, bookList })
    $.hideLoading()
  }
 

Other common problems of cloud development

  • Why do small programs can directly manipulate the database, but still need cloud functions?

    • Part of database operations, can not directly call the applet, such as the lower part of the scene update, removethe operation, the number of records acquired over more than 20 of the getinquiry operation or the like
    • Cloud functions can implement service-to-service, such as calling third-party non-https protocol apis, QR code generation, subscription messages, etc.
    • Cloud functions support triggers and operate on the database regularly
    • More private information, such as APPID, appSecret, etc. needs to be stored on the server side, so cloud functions are needed to correspond to some operations
  • The basic cost of cloud development

Basic version 1, there is a certain free quota, check the details , but in addition to the free quota, you need to pay attention to the following points

  • as follows:

    • In the free quota, there is a limit of 20 concurrent connections to the database. It is recommended to add an availability alert group in the WeChat background, and you can receive some errors in the abnormal operation of the applet (the information received is as follows)
    • Cloud function (single run) running memory: 256M5
    • Number of cloud functions: 50
    • The number of concurrent cloud functions: 10006
    • Database traffic: the size of a single packet is 16M
    • Database single set index limit: 20
    • Mini terminal request frequency limit of a single mini program: 1 million times/minute
   
AppID  wx51a5362ef159dbcd
 ID  prod-words-pk
   prod-words-pk 56 >=20 
 - 
 

This book is a series of actual sharing, and will continue to be updated and revised. Thank you for learning together~

Project open source

Since this project has participated in a university competition, it will not be open source for the time being. The competition will end on the WeChat official account: Join-FEopen source (code + design drawing), pay attention and don t miss it~

The same series of articles, you can go to the old Bao classmate s Nuggets homepage to eat