88 lines
3.0 KiB
Go
88 lines
3.0 KiB
Go
// Package gamemq defines RocketMQ payloads owned by game-service.
|
|
package gamemq
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"strings"
|
|
)
|
|
|
|
const (
|
|
MessageTypeGameOutboxEvent = "game_outbox_event"
|
|
|
|
TagGameOutboxEvent = "game_outbox_event"
|
|
|
|
EventTypeGameOrderSettled = "GameOrderSettled"
|
|
EventTypeSelfGameMatchCreated = "SelfGameMatchCreated"
|
|
EventTypeSelfGameMatchReady = "SelfGameMatchReady"
|
|
EventTypeSelfGameMatchSettled = "SelfGameMatchSettled"
|
|
EventTypeSelfGameMatchCanceled = "SelfGameMatchCanceled"
|
|
EventTypeSelfGameMatchFailed = "SelfGameMatchFailed"
|
|
EventTypeSelfGamePoolAdjusted = "SelfGamePoolAdjusted"
|
|
)
|
|
|
|
// GameOutboxMessage is the MQ representation of one committed game_outbox fact.
|
|
type GameOutboxMessage struct {
|
|
MessageType string `json:"message_type"`
|
|
AppCode string `json:"app_code"`
|
|
EventID string `json:"event_id"`
|
|
EventType string `json:"event_type"`
|
|
OrderID string `json:"order_id"`
|
|
UserID int64 `json:"user_id"`
|
|
PlatformCode string `json:"platform_code"`
|
|
GameID string `json:"game_id"`
|
|
OpType string `json:"op_type"`
|
|
CoinAmount int64 `json:"coin_amount"`
|
|
PayloadJSON string `json:"payload_json"`
|
|
OccurredAtMS int64 `json:"occurred_at_ms"`
|
|
}
|
|
|
|
// EncodeGameOutboxMessage serializes a game fact for fanout through RocketMQ.
|
|
func EncodeGameOutboxMessage(message GameOutboxMessage) ([]byte, error) {
|
|
message.MessageType = MessageTypeGameOutboxEvent
|
|
if err := validateGameOutboxMessage(message); err != nil {
|
|
return nil, err
|
|
}
|
|
if strings.TrimSpace(message.PayloadJSON) == "" {
|
|
message.PayloadJSON = "{}"
|
|
}
|
|
return json.Marshal(message)
|
|
}
|
|
|
|
// DecodeGameOutboxMessage validates the MQ body and restores the game fact.
|
|
func DecodeGameOutboxMessage(body []byte) (GameOutboxMessage, error) {
|
|
var message GameOutboxMessage
|
|
if err := json.Unmarshal(body, &message); err != nil {
|
|
return GameOutboxMessage{}, err
|
|
}
|
|
if message.MessageType != MessageTypeGameOutboxEvent {
|
|
return GameOutboxMessage{}, errors.New("unexpected game outbox message_type")
|
|
}
|
|
if err := validateGameOutboxMessage(message); err != nil {
|
|
return GameOutboxMessage{}, err
|
|
}
|
|
if strings.TrimSpace(message.PayloadJSON) == "" {
|
|
message.PayloadJSON = "{}"
|
|
}
|
|
return message, nil
|
|
}
|
|
|
|
func validateGameOutboxMessage(message GameOutboxMessage) error {
|
|
if strings.TrimSpace(message.AppCode) == "" ||
|
|
strings.TrimSpace(message.EventID) == "" ||
|
|
strings.TrimSpace(message.EventType) == "" ||
|
|
strings.TrimSpace(message.GameID) == "" ||
|
|
message.OccurredAtMS <= 0 {
|
|
return errors.New("game outbox message is incomplete")
|
|
}
|
|
if message.EventType == EventTypeGameOrderSettled && (strings.TrimSpace(message.OrderID) == "" ||
|
|
message.UserID <= 0 ||
|
|
strings.TrimSpace(message.OpType) == "" ||
|
|
message.CoinAmount < 0) {
|
|
// 厂商可能发送 0 金额派奖或 0 金额结算确认;这类订单是已落库事实,必须允许 MQ 投递。
|
|
// 负数仍然不是合法订单金额,扣减方向必须由 op_type=debit 表达,不能把符号塞进金额字段。
|
|
return errors.New("game order outbox message is incomplete")
|
|
}
|
|
return nil
|
|
}
|