yumi-golang/internal/router/smash_egg_routes.go
2026-05-19 19:50:50 +08:00

264 lines
8.1 KiB
Go

package router
import (
"context"
"net/http"
"strconv"
"strings"
"time"
"chatapp3-golang/internal/common"
"chatapp3-golang/internal/service/smashegg"
"github.com/gin-gonic/gin"
)
// registerSmashEggRoutes registers app, legacy H5, and admin smash egg APIs.
func registerSmashEggRoutes(engine *gin.Engine, javaClient authGateway, service *smashegg.Service) {
if service == nil {
return
}
appGroup := engine.Group("/app/smash-golden-egg")
appGroup.Use(authMiddleware(javaClient))
appGroup.GET("/config", func(c *gin.Context) {
resp, err := service.GetAppConfig(c.Request.Context(), mustAuthUser(c))
if err != nil {
writeError(c, err)
return
}
writeOK(c, resp)
})
appGroup.POST("/draw", func(c *gin.Context) {
var req smashegg.DrawRequest
if err := c.ShouldBindJSON(&req); err != nil {
writeError(c, smashegg.NewAppError(http.StatusBadRequest, "bad_request", err.Error()))
return
}
req.IdempotencyKey = requestIdempotencyKey(c, req.IdempotencyKey)
resp, err := service.Draw(c.Request.Context(), mustAuthUser(c), req)
if err != nil {
writeError(c, err)
return
}
writeOK(c, resp)
})
appGroup.GET("/records", func(c *gin.Context) {
cursor, _ := strconv.Atoi(strings.TrimSpace(c.DefaultQuery("cursor", "1")))
limit, _ := strconv.Atoi(strings.TrimSpace(c.DefaultQuery("limit", "20")))
resp, err := service.PageUserRecords(c.Request.Context(), mustAuthUser(c), cursor, limit)
if err != nil {
writeError(c, err)
return
}
writeOK(c, resp)
})
appGroup.GET("/notices", func(c *gin.Context) {
resp, err := service.ListRecentNotices(c.Request.Context(), mustAuthUser(c).SysOrigin, 20)
if err != nil {
writeError(c, err)
return
}
writeOK(c, resp)
})
appGroup.GET("/rank/day", func(c *gin.Context) {
cursor, _ := strconv.Atoi(strings.TrimSpace(c.DefaultQuery("cursor", "1")))
limit, _ := strconv.Atoi(strings.TrimSpace(c.DefaultQuery("limit", "100")))
resp, err := service.PageDayRank(c.Request.Context(), mustAuthUser(c).SysOrigin, cursor, limit)
if err != nil {
writeError(c, err)
return
}
writeOK(c, resp)
})
adminGroup := engine.Group("/resident-activity/smash-golden-egg")
adminGroup.Use(consoleAuthMiddleware(javaClient))
adminGroup.GET("/config", func(c *gin.Context) {
resp, err := service.GetConfig(c.Request.Context(), c.Query("sysOrigin"))
if err != nil {
writeError(c, err)
return
}
writeOK(c, resp)
})
adminGroup.POST("/config/save", func(c *gin.Context) {
var req smashegg.SaveConfigRequest
if err := c.ShouldBindJSON(&req); err != nil {
writeError(c, smashegg.NewAppError(http.StatusBadRequest, "bad_request", err.Error()))
return
}
resp, err := service.SaveConfig(c.Request.Context(), req)
if err != nil {
writeError(c, err)
return
}
writeOK(c, resp)
})
adminGroup.GET("/draw-record/page", func(c *gin.Context) {
cursor, _ := strconv.Atoi(strings.TrimSpace(c.DefaultQuery("cursor", "1")))
limit, _ := strconv.Atoi(strings.TrimSpace(c.DefaultQuery("limit", "20")))
resp, err := service.PageAdminRecords(
c.Request.Context(),
c.Query("sysOrigin"),
smashegg.ParseUserID(c.Query("userId")),
c.Query("status"),
cursor,
limit,
c.Query("startTime"),
c.Query("endTime"),
)
if err != nil {
writeError(c, err)
return
}
writeOK(c, resp)
})
h5Group := engine.Group("/h5/smash-golden-egg")
h5Group.Any("/config", func(c *gin.Context) {
handleLegacySmashEggAction(c, javaClient, service, "Action/TreasureHunt.getPrizeList")
})
h5Group.Any("/draw", func(c *gin.Context) {
handleLegacySmashEggAction(c, javaClient, service, "Action/TreasureHunt.startHunt")
})
h5Group.Any("/records", func(c *gin.Context) {
handleLegacySmashEggAction(c, javaClient, service, "Action/TreasureHunt.getMyHuntRecord")
})
h5Group.Any("/notices", func(c *gin.Context) {
handleLegacySmashEggAction(c, javaClient, service, "Action/TreasureHunt.getHuntRecord")
})
h5Group.Any("/rank/day", func(c *gin.Context) {
handleLegacySmashEggAction(c, javaClient, service, "Action/TreasureHunt.getDayRank")
})
engine.Any("/index.php", func(c *gin.Context) {
action := strings.TrimSpace(c.Query("action"))
if !strings.HasPrefix(action, "Action/TreasureHunt.") {
writeError(c, smashegg.NewAppError(http.StatusNotFound, "not_found", "legacy action is not supported"))
return
}
handleLegacySmashEggAction(c, javaClient, service, action)
})
}
func handleLegacySmashEggAction(c *gin.Context, javaClient authGateway, service *smashegg.Service, action string) {
user, err := legacySmashEggUser(c, javaClient)
if err != nil {
writeLegacyError(c, http.StatusUnauthorized, err.Error())
return
}
var resp map[string]any
switch action {
case "Action/TreasureHunt.getPrizeList":
resp, err = service.LegacyPrizeList(c.Request.Context(), user)
case "Action/TreasureHunt.startHunt":
times, _ := strconv.Atoi(strings.TrimSpace(firstNonEmptyQuery(c, "times", "num")))
if times <= 0 {
times = 1
}
resp, err = service.LegacyStartHunt(c.Request.Context(), user, times, requestIdempotencyKey(c, ""))
case "Action/TreasureHunt.getMyHuntRecord":
start, _ := strconv.Atoi(strings.TrimSpace(firstNonEmptyQuery(c, "offset", "start")))
limit, _ := strconv.Atoi(strings.TrimSpace(firstNonEmptyQuery(c, "limit", "num")))
if limit <= 0 {
limit = 20
}
resp, err = service.LegacyMyHuntRecords(c.Request.Context(), user, start, limit)
case "Action/TreasureHunt.getHuntRecord":
resp, err = service.LegacyHuntNotices(c.Request.Context(), user.SysOrigin)
case "Action/TreasureHunt.getDayRank":
resp, err = service.LegacyDayRank(c.Request.Context(), user.SysOrigin)
default:
writeLegacyError(c, http.StatusNotFound, "legacy action is not supported")
return
}
if err != nil {
writeLegacyError(c, http.StatusBadRequest, err.Error())
return
}
writeLegacyOK(c, resp)
}
func legacySmashEggUser(c *gin.Context, javaClient authGateway) (common.AuthUser, error) {
token := strings.TrimSpace(c.Query("token"))
if token == "" {
token = trimBearer(strings.TrimSpace(c.GetHeader("Authorization")))
}
if token != "" && javaClient != nil {
ctx, cancel := context.WithTimeout(c.Request.Context(), 10*time.Second)
defer cancel()
credential, err := javaClient.AuthenticateToken(ctx, token)
if err != nil {
return common.AuthUser{}, err
}
return common.AuthUser{
UserID: int64(credential.UserID),
SysOrigin: credential.SysOrigin,
Token: token,
}, nil
}
userID := smashegg.ParseUserID(firstNonEmptyQuery(c, "_login_uid", "uid", "userId"))
sysOrigin := strings.ToUpper(strings.TrimSpace(firstNonEmptyQuery(c, "sysOrigin", "originSys")))
if sysOrigin == "" {
sysOrigin = strings.ToUpper(strings.TrimSpace(c.GetHeader("req-sys-origin")))
}
if userID <= 0 || sysOrigin == "" {
return common.AuthUser{}, common.NewAppError(http.StatusUnauthorized, "missing_token", "token is required")
}
return common.AuthUser{UserID: userID, SysOrigin: sysOrigin, Token: token}, nil
}
func firstNonEmptyQuery(c *gin.Context, keys ...string) string {
for _, key := range keys {
if value := strings.TrimSpace(c.Query(key)); value != "" {
return value
}
}
return ""
}
func requestIdempotencyKey(c *gin.Context, fallback string) string {
for _, value := range []string{
fallback,
c.GetHeader("Idempotency-Key"),
c.GetHeader("X-Idempotency-Key"),
firstNonEmptyQuery(c, "idempotencyKey", "idempotency_key", "requestId", "request_id", "nonce"),
strings.TrimSpace(c.PostForm("idempotencyKey")),
strings.TrimSpace(c.PostForm("idempotency_key")),
strings.TrimSpace(c.PostForm("requestId")),
strings.TrimSpace(c.PostForm("request_id")),
strings.TrimSpace(c.PostForm("nonce")),
} {
if strings.TrimSpace(value) != "" {
return strings.TrimSpace(value)
}
}
return ""
}
func writeLegacyOK(c *gin.Context, data any) {
c.JSON(http.StatusOK, gin.H{
"response_data": data,
"response_status": gin.H{
"code": 0,
"error": "",
},
})
}
func writeLegacyError(c *gin.Context, code int, message string) {
if message == "" {
message = "error"
}
c.JSON(http.StatusOK, gin.H{
"response_data": gin.H{},
"response_status": gin.H{
"code": code,
"error": message,
},
})
}