Go + Gin 搭建 RESTful API 服务

用 Go 的 Gin 框架搭建后端 API 非常轻量。核心结构如下:

r := gin.Default()

api := r.Group("/api/v1")
api.GET("/users/:id", getUser)
api.POST("/users", createUser)

r.Run(":8080")

Gin 的路由分组 + 中间件机制很优雅。比如 JWT 认证只需要一行:

protected := api.Group("")
protected.Use(middleware.JWTAuth(secret))

所有 protected 下的路由自动走鉴权,干净利落。

微信小程序登录流程梳理

小程序登录的核心流程就三步:

  • 前端调用 wx.login() 拿到临时 code
  • 把 code 发给自己的后端
  • 后端拿 code 调微信的 code2Session 接口换 openid
// 后端 Go 伪代码
func wxLogin(code string) {
    resp := http.Get(
        "https://api.weixin.qq.com/sns/jscode2session" +
        "?appid=" + appID +
        "&secret=" + appSecret +
        "&js_code=" + code +
        "&grant_type=authorization_code",
    )
    // 从 resp 中取 openid,生成 JWT token 返回前端
}

拿到 openid 后生成自己的 JWT token 返回前端即可,不需要存 session。注意 code 只能用一次,5 分钟过期。

Redis 实现接口限流

用 Redis 的 INCR + EXPIRE 实现固定窗口限流,简单有效:

func Allow(rdb *redis.Client, key string, limit int64, ttl time.Duration) bool {
    count, _ := rdb.Incr(ctx, key).Result()
    if count == 1 {
        rdb.Expire(ctx, key, ttl) // 首次设置过期时间
    }
    return count <= limit
}

Key 按用户 + 日期设计,比如 ai_rate:user_123:20260507,TTL 设到当天 24:00 自动过期。这样每天自动重置,无需手动清理。

如果是 VIP 用户不受限制,只需要在中间件里判断用户角色,VIP 直接放行即可。

Docker Compose 一键部署 Go + PostgreSQL + Redis

一个典型的 docker-compose.yml 长这样:

services:
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: secret
      POSTGRES_DB: appdb
  redis:
    image: redis:7-alpine
  server:
    build: ./server
    ports:
      - "8080:8080"
    environment:
      DB_HOST: postgres
      REDIS_ADDR: redis:6379
    depends_on:
      - postgres
      - redis

几个要点:

  • 服务间用服务名互相访问,如 DB_HOST: postgres
  • Go 的 Dockerfile 用多阶段构建,编译阶段用 golang:alpine,运行阶段用 alpine,镜像只有十几 MB
  • 数据卷别忘挂载,不然容器重建数据就没了

uni-app 开发小程序踩坑记录

用 uni-app 开发微信小程序,几个常见问题:

环境变量区分开发/生产

根目录放两个文件:

# .env.development
VITE_BASE_URL=http://localhost:8080

# .env.production
VITE_BASE_URL=https://your-domain.com

代码里用 import.meta.env.VITE_BASE_URL 读取,打包时自动切环境。

网络请求封装

不要到处写 uni.request,统一封装一层处理 token 注入和错误处理:

function request(url, options) {
  const token = uni.getStorageSync('token')
  return new Promise((resolve, reject) => {
    uni.request({
      url: BASE_URL + url,
      header: { Authorization: 'Bearer ' + token },
      success(res) {
        if (res.statusCode === 401) {
          // 跳转登录
        }
        resolve(res.data)
      },
      fail(err) {
        reject(new Error(err.errMsg || '网络请求失败'))
      }
    })
  })
}

上传注意事项

  • manifest.json 开启 "lazyCodeLoading": "requiredComponents",不然上传时会有警告
  • 服务器域名必须在小程序后台「服务器域名」中配置,且要求 HTTPS
  • 域名需要完成 ICP 备案