Skip to content

服务端与云开发实践

预计阅读 35 分钟
版本信息
最后更新:2026-07-09 · 适用基础库:≥ 3.9.0(最低兼容 ≥ 2.30.0)· Node.js ≥ 18

前端工程师转型独立开发者时,往往对服务端开发感到陌生。本章讲解小游戏中最常见的服务端需求:登录鉴权、米大师支付回调、排行榜、开放数据域服务端方案,并对比微信云开发与自建服务器的适用场景。


云开发 vs 自建服务器

维度微信云开发自建服务器
运维成本低,免服务器运维高,需部署、监控、扩容
成本曲线按调用量计费,初期便宜固定成本,规模大了更划算
灵活性受限于云函数能力完全可控
数据库云数据库,有容量限制自选 MySQL/MongoDB/Redis
适合阶段原型 / 小型项目商业化 / 中重度项目
网络延迟微信生态内较优取决于机房和 CDN

建议:原型期用云开发快速验证;商业化后迁移到自建服务器以获得更低边际成本和更高可控性。


登录鉴权

核心流程

  1. 前端调用 wx.login() 获取 code
  2. 前端将 code 发送给服务端。
  3. 服务端调用 jscode2session 换取 openIdsession_key
  4. 服务端生成自定义登录态(如 JWT)返回前端。

Node.js 示例

typescript
// server/auth.ts
import crypto from 'crypto'
import axios from 'axios'

const APP_ID = process.env.WX_APP_ID!
const APP_SECRET = process.env.WX_APP_SECRET!

export async function login(code: string) {
  const url = `https://api.weixin.qq.com/sns/jscode2session?appid=${APP_ID}&secret=${APP_SECRET}&js_code=${code}&grant_type=authorization_code`
  const { data } = await axios.get(url)

  if (data.errcode) {
    throw new Error(`微信登录失败: ${data.errmsg}`)
  }

  const { openid, session_key, unionid } = data
  // 生成自定义登录态
  const token = generateToken(openid)

  // 将 session_key 安全存储,用于后续敏感数据解密
  await saveSessionKey(openid, session_key)

  return { openid, unionid, token }
}

function generateToken(openid: string): string {
  return crypto
    .createHmac('sha256', process.env.JWT_SECRET!)
    .update(openid + Date.now())
    .digest('hex')
}

米大师支付回调与订单校验

为什么必须服务端校验

客户端的 wx.requestMidasPayment.success 回调可以被伪造或篡改。生产环境必须通过服务端确认订单真实状态。

服务端校验流程

前端支付成功 → 将 { orderId, code } 发送给服务端

服务端用 code 换取 openId

服务端调用米大师「查询订单」接口

确认金额、状态、签名无误

发放道具并持久化到数据库

简化的 Node.js 校验示例

typescript
// server/payment.ts
import crypto from 'crypto'

interface MidasOrder {
  orderId: string
  openId: string
  offerId: string
  buyQuantity: number
  status: 'SUCCESS' | 'PENDING' | 'FAIL'
}

export async function verifyOrder(orderId: string, openId: string): Promise<MidasOrder> {
  // 实际应调用微信米大师查询订单接口
  // 这里仅展示校验签名和状态的逻辑框架
  const order = await queryMidasOrder(orderId, openId)

  const sign = generateMidasSign(order)
  if (sign !== order.signature) {
    throw new Error('订单签名无效')
  }

  if (order.status !== 'SUCCESS') {
    throw new Error(`订单状态异常: ${order.status}`)
  }

  return order
}

function generateMidasSign(order: Record<string, any>): string {
  const secret = process.env.MIDAS_SECRET!
  const sorted = Object.keys(order)
    .filter((k) => k !== 'signature')
    .sort()
    .map((k) => `${k}=${order[k]}`)
    .join('&')
  return crypto.createHmac('sha256', secret).update(sorted).digest('hex')
}

排行榜服务端方案

全局排行榜

使用 Redis 有序集合(Sorted Set)是最高效的方式:

typescript
// server/ranking.ts
import Redis from 'ioredis'
const redis = new Redis(process.env.REDIS_URL)

export async function updateScore(openId: string, score: number) {
  // ZADD 会自动去重并保留最高分
  const current = await redis.zscore('ranking:weekly', openId)
  if (!current || score > Number(current)) {
    await redis.zadd('ranking:weekly', score, openId)
  }
}

export async function getTopPlayers(topN = 100) {
  const list = await redis.zrevrange('ranking:weekly', 0, topN - 1, 'WITHSCORES')
  const result = []
  for (let i = 0; i < list.length; i += 2) {
    result.push({ openId: list[i], score: Number(list[i + 1]), rank: i / 2 + 1 })
  }
  return result
}

好友排行榜

好友排行榜需要用户授权访问好友关系,数据存储在微信云端。服务端无法直接获取好友列表,因此通常采用:

  1. 前端通过 wx.getFriendCloudStorage 获取好友数据。
  2. 将数据交给开放数据域渲染(参见 开放数据域)。
  3. 服务端仅负责存储和同步玩家自己的最高分。

云开发示例

typescript
// 云函数 login/index.js
const cloud = require('wx-server-sdk')
cloud.init()

exports.main = async (event) => {
  const { OPENID } = cloud.getWXContext()

  const db = cloud.database()
  const user = await db.collection('users').doc(OPENID).get()

  if (!user.data) {
    await db.collection('users').add({
      data: {
        _id: OPENID,
        createdAt: db.serverDate(),
        bestScore: 0,
      },
    })
  }

  return { openId: OPENID }
}

📝 课后练习

练习 1:设计排行榜接口

题目: 设计一个获取周排行榜 Top 100 的 HTTP 接口,要求:

  1. 需要鉴权(token 校验)。
  2. 返回排名、openId 掩码、分数。
  3. 接口有缓存,避免每次请求都打 Redis。

参考答案要点:

  • 使用 zrevrange 获取 Top 100。
  • openId 脱敏处理,如 user_*****1234
  • 使用 Redis 缓存结果 30 秒。
  • token 校验失败返回 401。

本章小结

  • 登录鉴权:前端只传 code,服务端用 jscode2session 换取 openId 和 session_key。
  • 支付校验:必须服务端调用米大师接口确认订单,客户端 success 不可信。
  • 排行榜:全局榜用 Redis Sorted Set,好友榜依赖微信开放数据域。
  • 云开发适合原型和小型项目,商业化后建议迁移到自建服务器。
  • session_key 和支付密钥必须服务端安全存储,绝不泄露到客户端。

用心学习,持续实践