新增几大中心功能

This commit is contained in:
Administrator
2026-04-23 15:29:07 +08:00
parent 5c2a146a44
commit c6354ee065
123 changed files with 13074 additions and 229 deletions

View File

@@ -0,0 +1,34 @@
package middleware
import (
"time"
appSvc "git.echol.cn/loser/Go-Web-Template/server/service/app"
"git.echol.cn/loser/Go-Web-Template/server/utils"
"github.com/gin-gonic/gin"
)
// AppActivity 玩家端活跃记录:每次请求写 last_active_at轻量 upsert
func AppActivity() gin.HandlerFunc {
return func(c *gin.Context) {
c.Next()
uid := utils.GetUserID(c)
if uid == 0 {
return
}
// 避免极端高频写:简单节流(同请求链路内只写一次即可)
if _, exists := c.Get("__app_activity_written"); exists {
return
}
c.Set("__app_activity_written", true)
// 尽量不阻塞响应:允许轻微延迟
go func(userID uint, ip string) {
_ = appSvc.AppActivityServiceApp.TouchActive(userID, ip)
}(uid, c.ClientIP())
_ = time.Now() // 保留 import time避免未来扩展时频繁改动不影响逻辑
}
}

View File

@@ -0,0 +1,74 @@
package middleware
import (
"errors"
"strconv"
"time"
"git.echol.cn/loser/Go-Web-Template/server/global"
"git.echol.cn/loser/Go-Web-Template/server/model/common/response"
"git.echol.cn/loser/Go-Web-Template/server/utils"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
)
// AppJWTAuth 玩家端 JWT 鉴权(仅接受 aud=GVA_APP
func AppJWTAuth() gin.HandlerFunc {
return func(c *gin.Context) {
token := utils.GetToken(c)
if token == "" {
response.NoAuth("未登录或非法访问,请登录", c)
c.Abort()
return
}
if isBlacklist(token) {
response.NoAuth("您的帐户异地登陆或令牌失效", c)
utils.ClearToken(c)
c.Abort()
return
}
j := utils.NewJWT()
claims, err := j.ParseToken(token)
if err != nil {
if errors.Is(err, utils.TokenExpired) {
response.NoAuth("登录已过期,请重新登录", c)
utils.ClearToken(c)
c.Abort()
return
}
response.NoAuth(err.Error(), c)
utils.ClearToken(c)
c.Abort()
return
}
if len(claims.Audience) == 0 || claims.Audience[0] != utils.JWTAudienceApp {
response.NoAuth("令牌无效", c)
utils.ClearToken(c)
c.Abort()
return
}
c.Set("claims", claims)
if claims.ExpiresAt.Unix()-time.Now().Unix() < claims.BufferTime {
dr, _ := utils.ParseDuration(global.GVA_CONFIG.JWT.ExpiresTime)
claims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(dr))
newToken, _ := j.CreateTokenByOldToken(token, *claims)
newClaims, _ := j.ParseToken(newToken)
c.Header("new-token", newToken)
c.Header("new-expires-at", strconv.FormatInt(newClaims.ExpiresAt.Unix(), 10))
utils.SetToken(c, newToken, int(dr.Seconds()/60))
if global.GVA_CONFIG.System.UseMultipoint {
_ = utils.SetRedisJWT(newToken, newClaims.Username)
}
}
c.Next()
if newToken, exists := c.Get("new-token"); exists {
c.Header("new-token", newToken.(string))
}
if newExpiresAt, exists := c.Get("new-expires-at"); exists {
c.Header("new-expires-at", newExpiresAt.(string))
}
}
}

View File

@@ -18,11 +18,11 @@ func Cors() gin.HandlerFunc {
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type, New-Token, New-Expires-At")
c.Header("Access-Control-Allow-Credentials", "true")
// 放行所有OPTIONS方法
// 放行所有OPTIONS方法(预检请求不应继续进入路由,否则易落到 404
if method == "OPTIONS" {
c.AbortWithStatus(http.StatusNoContent)
return
}
// 处理请求
c.Next()
}
}
@@ -50,14 +50,14 @@ func CorsByRules() gin.HandlerFunc {
// 严格白名单模式且未通过检查,直接拒绝处理请求
if whitelist == nil && global.GVA_CONFIG.Cors.Mode == "strict-whitelist" && !(c.Request.Method == "GET" && c.Request.URL.Path == "/health") {
c.AbortWithStatus(http.StatusForbidden)
} else {
// 非严格白名单模式,无论是否通过检查均放行所有 OPTIONS 方法
if c.Request.Method == http.MethodOptions {
c.AbortWithStatus(http.StatusNoContent)
}
return
}
// OPTIONS 预检:直接结束,避免继续匹配路由导致 404
if c.Request.Method == http.MethodOptions {
c.AbortWithStatus(http.StatusNoContent)
return
}
// 处理请求
c.Next()
}
}

View File

@@ -44,6 +44,14 @@ func JWTAuth() gin.HandlerFunc {
return
}
// 后台接口仅接受后台受众 token
if len(claims.Audience) == 0 || claims.Audience[0] != utils.JWTAudienceAdmin {
response.NoAuth("令牌无效", c)
utils.ClearToken(c)
c.Abort()
return
}
// 已登录用户被管理员禁用 需要使该用户的jwt失效 此处比较消耗性能 如果需要 请自行打开
// 用户被删除的逻辑 需要优化 此处比较消耗性能 如果需要 请自行打开