模拟飘屏

This commit is contained in:
hy001 2026-05-25 21:02:28 +08:00
parent adcc722284
commit 4cf4eba5ff
3 changed files with 233 additions and 10 deletions

View File

@ -4,6 +4,7 @@ import (
"net/http" "net/http"
"chatapp3-golang/internal/config" "chatapp3-golang/internal/config"
"chatapp3-golang/internal/service/highwin"
"chatapp3-golang/internal/service/regionimgroup" "chatapp3-golang/internal/service/regionimgroup"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@ -65,6 +66,7 @@ func registerRegionIMGroupInternalRoutes(engine *gin.Engine, cfg config.Config,
func registerRegionIMGroupAdminRoutes(engine *gin.Engine, javaClient authGateway, service *regionimgroup.Service) { func registerRegionIMGroupAdminRoutes(engine *gin.Engine, javaClient authGateway, service *regionimgroup.Service) {
registerRegionIMGroupAdminGroup(engine.Group("/region/config"), javaClient, service) registerRegionIMGroupAdminGroup(engine.Group("/region/config"), javaClient, service)
registerRegionIMGroupAdminGroup(engine.Group("/resident-activity/voice-room-red-packet"), javaClient, service) registerRegionIMGroupAdminGroup(engine.Group("/resident-activity/voice-room-red-packet"), javaClient, service)
registerRegionBroadcastSimulationAdminGroup(engine.Group("/operate/region-broadcast"), javaClient, service)
} }
func registerRegionIMGroupAdminGroup(group *gin.RouterGroup, javaClient authGateway, service *regionimgroup.Service) { func registerRegionIMGroupAdminGroup(group *gin.RouterGroup, javaClient authGateway, service *regionimgroup.Service) {
@ -104,3 +106,20 @@ func registerRegionIMGroupAdminGroup(group *gin.RouterGroup, javaClient authGate
writeOK(c, resp) writeOK(c, resp)
}) })
} }
func registerRegionBroadcastSimulationAdminGroup(group *gin.RouterGroup, javaClient authGateway, service *regionimgroup.Service) {
group.Use(consoleAuthMiddleware(javaClient))
group.POST("/simulate", func(c *gin.Context) {
var req highwin.BroadcastSimulationRequest
if err := c.ShouldBindJSON(&req); err != nil {
writeError(c, regionimgroup.NewAppError(http.StatusBadRequest, "bad_request", err.Error()))
return
}
resp, err := highwin.SendSimulationBroadcast(c.Request.Context(), service, req)
if err != nil {
writeError(c, err)
return
}
writeOK(c, resp)
})
}

View File

@ -0,0 +1,202 @@
package highwin
import (
"context"
"fmt"
"net/http"
"strconv"
"strings"
"time"
"chatapp3-golang/internal/common"
"chatapp3-golang/internal/service/regionimgroup"
)
const (
SimulationKindLuckyGift = "LUCKY_GIFT"
SimulationKindGameWin = "GAME_WIN"
)
type BroadcastSimulationRequest struct {
SysOrigin string `json:"sysOrigin"`
RegionCode string `json:"regionCode"`
Kind string `json:"kind"`
SenderUserID string `json:"senderUserId"`
RoomID string `json:"roomId"`
Multiple string `json:"multiple"`
WinAmount string `json:"winAmount"`
AcceptUserID string `json:"acceptUserId"`
GiftID string `json:"giftId"`
GiftCandy string `json:"giftCandy"`
GiftQuantity string `json:"giftQuantity"`
GameID string `json:"gameId"`
GameRoundID string `json:"gameRoundId"`
GameURL string `json:"gameUrl"`
BetAmount string `json:"betAmount"`
ProviderOrder string `json:"providerOrderId"`
}
type BroadcastSimulationResponse struct {
SysOrigin string `json:"sysOrigin"`
RegionCode string `json:"regionCode"`
GroupID string `json:"groupId"`
Type string `json:"type"`
Data map[string]any `json:"data"`
}
func SendSimulationBroadcast(
ctx context.Context,
broadcaster RegionBroadcaster,
req BroadcastSimulationRequest,
) (*BroadcastSimulationResponse, error) {
if broadcaster == nil {
return nil, common.NewAppError(http.StatusInternalServerError, "region_broadcaster_missing", "region broadcaster is missing")
}
messageType, err := simulationMessageType(req.Kind)
if err != nil {
return nil, err
}
userID, err := parseRequiredInt64(req.SenderUserID, "senderUserId")
if err != nil {
return nil, err
}
roomID := strings.TrimSpace(req.RoomID)
if roomID == "" {
return nil, common.NewAppError(http.StatusBadRequest, "missing_room_id", "roomId is required")
}
amount, err := parseRequiredInt64(req.WinAmount, "winAmount")
if err != nil {
return nil, err
}
multiple, err := parseRequiredFloat64(req.Multiple, "multiple")
if err != nil {
return nil, err
}
if !ShouldBroadcast(multiple, amount) {
return nil, common.NewAppError(http.StatusBadRequest, "broadcast_threshold_not_met", "multiple must be >= 10 and winAmount must be > 0")
}
data := simulationData(messageType, req, amount)
event := BroadcastEvent{
SysOrigin: req.SysOrigin,
RegionCode: req.RegionCode,
Type: messageType,
UserID: userID,
RoomID: roomID,
Multiple: multiple,
Amount: amount,
Data: data,
}
if err := SendRegionBroadcast(ctx, broadcaster, event); err != nil {
return nil, err
}
previewData := cloneData(data)
setRequired(previewData, "userId", strconv.FormatInt(userID, 10))
setRequired(previewData, "sendUserId", strconv.FormatInt(userID, 10))
setRequired(previewData, "senderUserId", strconv.FormatInt(userID, 10))
setRequired(previewData, "roomId", roomID)
setRequired(previewData, "multiple", normalizeMultiple(multiple))
setRequired(previewData, "winMultiple", normalizeMultiple(multiple))
setRequired(previewData, "winAmount", amount)
setRequired(previewData, "awardAmount", amount)
setRequired(previewData, "msg", highWinMessage(previewData, messageType, strconv.FormatInt(userID, 10), multiple, amount))
return &BroadcastSimulationResponse{
SysOrigin: strings.ToUpper(strings.TrimSpace(req.SysOrigin)),
RegionCode: strings.TrimSpace(req.RegionCode),
Type: messageType,
Data: previewData,
}, nil
}
func simulationMessageType(kind string) (string, error) {
switch strings.ToUpper(strings.TrimSpace(kind)) {
case SimulationKindLuckyGift:
return MessageTypeLuckyGift, nil
case SimulationKindGameWin:
return MessageTypeBaishunWin, nil
default:
return "", common.NewAppError(http.StatusBadRequest, "invalid_broadcast_kind", "kind must be LUCKY_GIFT or GAME_WIN")
}
}
func simulationData(messageType string, req BroadcastSimulationRequest, amount int64) map[string]any {
now := strconv.FormatInt(time.Now().UnixMilli(), 10)
switch messageType {
case MessageTypeLuckyGift:
data := map[string]any{
"businessId": firstNonBlank(req.ProviderOrder, "admin-sim-lucky-gift-"+now),
"orderId": firstNonBlank(req.ProviderOrder, "admin-sim-order-"+now),
"globalNews": true,
"multipleType": "WIN",
}
setOptional(data, "acceptUserId", req.AcceptUserID)
setOptional(data, "giftId", req.GiftID)
setOptionalInt64(data, "giftCandy", req.GiftCandy)
setOptionalInt64(data, "giftQuantity", req.GiftQuantity)
return data
default:
data := map[string]any{
"gameRoundId": firstNonBlank(req.GameRoundID, "admin-sim-game-round-"+now),
"currencyDiff": amount,
}
setOptional(data, "gameId", req.GameID)
setOptional(data, "gameUrl", req.GameURL)
setOptionalInt64(data, "betAmount", req.BetAmount)
return data
}
}
func setOptional(data map[string]any, key string, value string) {
if value = strings.TrimSpace(value); value != "" {
data[key] = value
}
}
func setOptionalInt64(data map[string]any, key string, value string) {
parsed, err := parseOptionalInt64(value)
if err == nil && parsed > 0 {
data[key] = parsed
}
}
func firstNonBlank(values ...string) string {
for _, value := range values {
if trimmed := strings.TrimSpace(value); trimmed != "" {
return trimmed
}
}
return ""
}
func parseRequiredInt64(value string, field string) (int64, error) {
parsed, err := parseOptionalInt64(value)
if err != nil || parsed <= 0 {
return 0, common.NewAppError(http.StatusBadRequest, "invalid_"+field, fmt.Sprintf("%s must be a positive integer", field))
}
return parsed, nil
}
func parseOptionalInt64(value string) (int64, error) {
raw := strings.TrimSpace(value)
if raw == "" {
return 0, nil
}
return strconv.ParseInt(raw, 10, 64)
}
func parseRequiredFloat64(value string, field string) (float64, error) {
raw := strings.TrimSpace(value)
if raw == "" {
return 0, common.NewAppError(http.StatusBadRequest, "invalid_"+field, fmt.Sprintf("%s must be a positive number", field))
}
parsed, err := strconv.ParseFloat(raw, 64)
if err != nil || parsed <= 0 {
return 0, common.NewAppError(http.StatusBadRequest, "invalid_"+field, fmt.Sprintf("%s must be a positive number", field))
}
return parsed, nil
}
var _ RegionBroadcaster = (*regionimgroup.Service)(nil)

View File

@ -22,13 +22,14 @@ type RegionBroadcaster interface {
} }
type BroadcastEvent struct { type BroadcastEvent struct {
SysOrigin string SysOrigin string
Type string RegionCode string
UserID int64 Type string
RoomID string UserID int64
Multiple float64 RoomID string
Amount int64 Multiple float64
Data map[string]any Amount int64
Data map[string]any
} }
func IntegralMultiple(amount, base int64) int64 { func IntegralMultiple(amount, base int64) int64 {
@ -78,9 +79,10 @@ func SendRegionBroadcast(ctx context.Context, broadcaster RegionBroadcaster, eve
setRequired(data, "msg", highWinMessage(data, messageType, userIDText, event.Multiple, event.Amount)) setRequired(data, "msg", highWinMessage(data, messageType, userIDText, event.Multiple, event.Amount))
_, err := broadcaster.SendRegionBroadcast(ctx, regionimgroup.RegionBroadcastRequest{ _, err := broadcaster.SendRegionBroadcast(ctx, regionimgroup.RegionBroadcastRequest{
SysOrigin: strings.TrimSpace(event.SysOrigin), SysOrigin: strings.TrimSpace(event.SysOrigin),
Type: messageType, RegionCode: strings.TrimSpace(event.RegionCode),
Data: data, Type: messageType,
Data: data,
}) })
return err return err
} }