补齐 HTTP 层:实现用户模块、路由与中间件接线、COS 上传及优雅启动

- 新增用户与上传错误码文件及 Recovery 中间件
- 修复 router.go 导入与中间件链,注册用户路由并补 handler 错误码导入
- 补充用户状态常量与 UserGetByWechatOpenID 查询,修订刷新令牌 dto
- 移除 upload 包 OTel 调用,新增 FileUploadCos 上传封装
- 实现 UserService 微信登录/刷新令牌/我的信息/改资料/上传头像
- 裁剪 main.go 配置段并移除 OTel 桥接,补启动与优雅退出
- 新增 configs 示例配置、global 配置结构及 go.mod 依赖
This commit is contained in:
2026-09-27 15:02:01 +08:00
parent 8f08d94a56
commit ff9add8655
33 changed files with 2473 additions and 0 deletions
+75
View File
@@ -0,0 +1,75 @@
package middleware
import (
"context"
"errors"
"log/slog"
"strings"
"git.lzcwy.cn/zhengchiliu/zt-online-game-backend/pkg/app"
"git.lzcwy.cn/zhengchiliu/zt-online-game-backend/pkg/errcode"
"git.lzcwy.cn/zhengchiliu/zt-online-game-backend/pkg/lzcwyjwt"
"github.com/gin-gonic/gin"
)
type ctxKey string
const (
userIDKey ctxKey = "userID"
)
func JWTAuth() gin.HandlerFunc {
return func(c *gin.Context) {
response := app.NewResponse(c)
// 1. 从 Authorization 头取 token:Bearer xxx.yyy.zzz
authHeader := c.GetHeader("Authorization")
if authHeader == "" {
slog.WarnContext(c.Request.Context(), "jwt auth: authorization header missing",
"method", c.Request.Method, "path", c.Request.URL.Path, "ip", c.ClientIP())
response.ToErrorResponse(errcode.UnauthorizedTokenMissing)
c.Abort()
return
}
parts := strings.SplitN(authHeader, " ", 2)
if len(parts) != 2 || parts[0] != "Bearer" {
slog.WarnContext(c.Request.Context(), "jwt auth: invalid authorization format",
"method", c.Request.Method, "path", c.Request.URL.Path, "ip", c.ClientIP())
response.ToErrorResponse(errcode.UnauthorizedTokenError.WithDetails("invalid Authorization format"))
c.Abort()
return
}
// 2. 解析 token
claims, err := lzcwyjwt.ParseAccessToken(parts[1])
if err != nil {
switch {
case errors.Is(err, lzcwyjwt.ErrTokenExpired):
slog.InfoContext(c.Request.Context(), "jwt auth: token expired", "path", c.Request.URL.Path)
response.ToErrorResponse(errcode.UnauthorizedTokenTimeout)
case errors.Is(err, lzcwyjwt.ErrTokenWrongType):
slog.WarnContext(c.Request.Context(), "jwt auth: token type mismatch", "path", c.Request.URL.Path)
response.ToErrorResponse(errcode.UnauthorizedTokenError.WithDetails("token type mismatch"))
default:
slog.WarnContext(c.Request.Context(), "jwt auth: parse token failed",
"path", c.Request.URL.Path, "ip", c.ClientIP(), "error", err)
response.ToErrorResponse(errcode.UnauthorizedTokenError.WithDetails(err.Error()))
}
c.Abort()
return
}
// 4. 把 userID注入 context
ctx := context.WithValue(c.Request.Context(), userIDKey, claims.UserID)
c.Request = c.Request.WithContext(ctx)
c.Next()
}
}
// GetUserID 从 context 取登录用户 ID(在 handler 里用)
func GetUserID(ctx context.Context) (int64, bool) {
uid, ok := ctx.Value(userIDKey).(int64)
return uid, ok
}