328 lines
10 KiB
Go
328 lines
10 KiB
Go
package cprelationbroadcast
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"chatapp3-golang/internal/common"
|
|
"chatapp3-golang/internal/model"
|
|
"chatapp3-golang/internal/service/regionimgroup"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// ProcessPayload 解码 Java MQ 消息并发送区域飘屏。
|
|
func (s *Service) ProcessPayload(ctx context.Context, payload string) (*regionimgroup.RegionBroadcastResponse, error) {
|
|
req, err := decodeEventPayload(payload, s.cfg.CPRelationBroadcast.MQ.Tag)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if req.isEmpty() {
|
|
return nil, nil
|
|
}
|
|
return s.ProcessEvent(ctx, req)
|
|
}
|
|
|
|
// ProcessEvent 处理标准化后的 CP 关系事件。
|
|
func (s *Service) ProcessEvent(ctx context.Context, req EventRequest) (*regionimgroup.RegionBroadcastResponse, error) {
|
|
if s == nil || s.broadcaster == nil {
|
|
return nil, common.NewAppError(http.StatusInternalServerError, "cp_relation_broadcaster_missing", "cp relation broadcaster is missing")
|
|
}
|
|
if s.db == nil {
|
|
return nil, common.NewAppError(http.StatusInternalServerError, "cp_relation_db_missing", "cp relation db is missing")
|
|
}
|
|
req = normalizeEventRequest(req)
|
|
|
|
userID := req.UserID.Int64()
|
|
cpUserID := req.CpUserID.Int64()
|
|
if userID <= 0 || cpUserID <= 0 {
|
|
return nil, common.NewAppError(http.StatusBadRequest, "invalid_cp_relation_users", "userId and cpUserId are required")
|
|
}
|
|
relationType, err := normalizeRelationType(req.RelationType)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sysOrigin := normalizeSysOrigin(req.SysOrigin, s.cfg.CPRelationBroadcast.DefaultSysOrigin)
|
|
|
|
user, err := s.loadEventUser(ctx, userID, req.User)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
cpUser, err := s.loadEventUser(ctx, cpUserID, req.CpUser)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
msg := relationMessage(relationType, user.displayName(), cpUser.displayName())
|
|
data := buildBroadcastData(req, sysOrigin, relationType, user, cpUser, msg)
|
|
return s.broadcaster.SendRegionBroadcast(ctx, regionimgroup.RegionBroadcastRequest{
|
|
SysOrigin: sysOrigin,
|
|
Type: MessageTypeCPRelationBroadcast,
|
|
Data: data,
|
|
})
|
|
}
|
|
|
|
func (s *Service) loadEventUser(ctx context.Context, userID int64, provided *EventUser) (EventUser, error) {
|
|
var user model.UserBaseInfo
|
|
err := s.db.WithContext(ctx).Where("id = ?", userID).First(&user).Error
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
account := strconv.FormatInt(userID, 10)
|
|
user = model.UserBaseInfo{
|
|
ID: userID,
|
|
Account: account,
|
|
UserNickname: account,
|
|
}
|
|
} else if err != nil {
|
|
return EventUser{}, err
|
|
}
|
|
if strings.TrimSpace(user.Account) == "" {
|
|
user.Account = strconv.FormatInt(userID, 10)
|
|
}
|
|
if strings.TrimSpace(user.UserNickname) == "" {
|
|
user.UserNickname = user.Account
|
|
}
|
|
return mergeEventUser(userID, user, provided), nil
|
|
}
|
|
|
|
func buildBroadcastData(req EventRequest, sysOrigin, relationType string, user EventUser, cpUser EventUser, msg string) map[string]any {
|
|
userID := user.normalizedID()
|
|
cpUserID := cpUser.normalizedID()
|
|
userIDText := strconv.FormatInt(userID, 10)
|
|
cpUserIDText := strconv.FormatInt(cpUserID, 10)
|
|
|
|
data := map[string]any{
|
|
"sysOrigin": sysOrigin,
|
|
"type": MessageTypeCPRelationBroadcast,
|
|
"relationType": relationType,
|
|
"relationLabel": relationLabel(relationType),
|
|
"msg": msg,
|
|
"userId": userIDText,
|
|
"sendUserId": userIDText,
|
|
"senderUserId": userIDText,
|
|
"cpUserId": cpUserIDText,
|
|
"acceptUserId": cpUserIDText,
|
|
"toUserId": cpUserIDText,
|
|
"user": user.toMap(),
|
|
"cpUser": cpUser.toMap(),
|
|
|
|
"account": user.Account,
|
|
"actualAccount": user.Account,
|
|
"userNickname": user.displayName(),
|
|
"nickname": user.displayName(),
|
|
"userAvatar": firstNonBlank(user.UserAvatar, user.Avatar),
|
|
"countryCode": user.CountryCode,
|
|
"countryName": user.CountryName,
|
|
"cpUserAccount": cpUser.Account,
|
|
"cpUserNickname": cpUser.displayName(),
|
|
"cpUserAvatar": firstNonBlank(cpUser.UserAvatar, cpUser.Avatar),
|
|
"cpUserCountryCode": cpUser.CountryCode,
|
|
"cpUserCountryName": cpUser.CountryName,
|
|
"acceptAccount": cpUser.Account,
|
|
"acceptNickname": cpUser.displayName(),
|
|
"acceptUserAvatar": firstNonBlank(cpUser.UserAvatar, cpUser.Avatar),
|
|
"toUserName": cpUser.displayName(),
|
|
"toUserAvatarUrl": firstNonBlank(cpUser.UserAvatar, cpUser.Avatar),
|
|
}
|
|
if eventID := strings.TrimSpace(req.EventID); eventID != "" {
|
|
data["eventId"] = eventID
|
|
}
|
|
if applyID := req.ApplyID.Int64(); applyID > 0 {
|
|
data["applyId"] = strconv.FormatInt(applyID, 10)
|
|
}
|
|
if occurredAt := strings.TrimSpace(req.OccurredAt); occurredAt != "" {
|
|
data["occurredAt"] = occurredAt
|
|
}
|
|
if len(req.Payload) > 0 {
|
|
data["payload"] = req.Payload
|
|
}
|
|
return data
|
|
}
|
|
|
|
func normalizeEventRequest(req EventRequest) EventRequest {
|
|
if req.UserID.Int64() <= 0 {
|
|
req.UserID = req.SendUserID
|
|
}
|
|
if req.CpUserID.Int64() <= 0 {
|
|
req.CpUserID = req.AcceptUserID
|
|
}
|
|
if req.UserID.Int64() <= 0 && req.User != nil {
|
|
req.UserID = flexibleInt64(req.User.normalizedID())
|
|
}
|
|
if req.CpUserID.Int64() <= 0 && req.CpUser != nil {
|
|
req.CpUserID = flexibleInt64(req.CpUser.normalizedID())
|
|
}
|
|
return req
|
|
}
|
|
|
|
func (req EventRequest) isEmpty() bool {
|
|
return strings.TrimSpace(req.EventID) == "" &&
|
|
strings.TrimSpace(req.RelationType) == "" &&
|
|
req.UserID.Int64() <= 0 &&
|
|
req.CpUserID.Int64() <= 0 &&
|
|
req.SendUserID.Int64() <= 0 &&
|
|
req.AcceptUserID.Int64() <= 0
|
|
}
|
|
|
|
func normalizeRelationType(value string) (string, error) {
|
|
normalized := strings.ToUpper(strings.TrimSpace(value))
|
|
if normalized == "" {
|
|
return RelationTypeCP, nil
|
|
}
|
|
switch normalized {
|
|
case RelationTypeCP:
|
|
return RelationTypeCP, nil
|
|
case RelationTypeBrother, "BROTHERS":
|
|
return RelationTypeBrother, nil
|
|
case RelationTypeSisters, "SISTER":
|
|
return RelationTypeSisters, nil
|
|
default:
|
|
return "", common.NewAppError(http.StatusBadRequest, "invalid_cp_relation_type", fmt.Sprintf("invalid cp relationType: %s", value))
|
|
}
|
|
}
|
|
|
|
func relationLabel(relationType string) string {
|
|
switch relationType {
|
|
case RelationTypeBrother:
|
|
return "brothers"
|
|
case RelationTypeSisters:
|
|
return "sisters"
|
|
default:
|
|
return "cp_relation"
|
|
}
|
|
}
|
|
|
|
func relationMessage(relationType, userName, cpUserName string) string {
|
|
switch relationType {
|
|
case RelationTypeBrother:
|
|
return fmt.Sprintf("%s与%s结为兄弟", userName, cpUserName)
|
|
case RelationTypeSisters:
|
|
return fmt.Sprintf("%s与%s结为姐妹", userName, cpUserName)
|
|
default:
|
|
return fmt.Sprintf("%s与%s get cp relation", userName, cpUserName)
|
|
}
|
|
}
|
|
|
|
func normalizeSysOrigin(values ...string) string {
|
|
for _, value := range values {
|
|
if text := strings.ToUpper(strings.TrimSpace(value)); text != "" {
|
|
return text
|
|
}
|
|
}
|
|
return defaultSysOrigin
|
|
}
|
|
|
|
func decodeEventPayload(payload string, expectedTag string) (EventRequest, error) {
|
|
payload = strings.TrimSpace(payload)
|
|
if payload == "" {
|
|
return EventRequest{}, nil
|
|
}
|
|
req, err := decodeEventObject([]byte(payload))
|
|
if err != nil {
|
|
return EventRequest{}, err
|
|
}
|
|
if !req.isEmpty() {
|
|
return req, nil
|
|
}
|
|
|
|
var envelope messageEventEnvelope
|
|
if err := json.Unmarshal([]byte(payload), &envelope); err != nil {
|
|
return EventRequest{}, err
|
|
}
|
|
if shouldSkipEnvelope(envelope.Tag, expectedTag) {
|
|
return EventRequest{}, nil
|
|
}
|
|
bodyPayload, ok, err := envelope.bodyPayload()
|
|
if err != nil || !ok {
|
|
return EventRequest{}, err
|
|
}
|
|
return decodeEventObject([]byte(bodyPayload))
|
|
}
|
|
|
|
type rawEventRequest struct {
|
|
EventRequest
|
|
SysOriginSnake string `json:"sys_origin"`
|
|
EventIDSnake string `json:"event_id"`
|
|
ApplyIDSnake flexibleInt64 `json:"apply_id"`
|
|
RelationTypeSnake string `json:"relation_type"`
|
|
UserIDSnake flexibleInt64 `json:"user_id"`
|
|
CpUserIDSnake flexibleInt64 `json:"cp_user_id"`
|
|
SendUserIDSnake flexibleInt64 `json:"send_user_id"`
|
|
AcceptUserIDSnake flexibleInt64 `json:"accept_user_id"`
|
|
OccurredAtSnake string `json:"occurred_at"`
|
|
TargetUserID flexibleInt64 `json:"targetUserId"`
|
|
TargetUserIDSnake flexibleInt64 `json:"target_user_id"`
|
|
ToUserIDSnake flexibleInt64 `json:"to_user_id"`
|
|
}
|
|
|
|
func decodeEventObject(data []byte) (EventRequest, error) {
|
|
var raw rawEventRequest
|
|
if err := json.Unmarshal(data, &raw); err != nil {
|
|
return EventRequest{}, err
|
|
}
|
|
req := raw.EventRequest
|
|
req.SysOrigin = firstNonBlank(req.SysOrigin, raw.SysOriginSnake)
|
|
req.EventID = firstNonBlank(req.EventID, raw.EventIDSnake)
|
|
req.RelationType = firstNonBlank(req.RelationType, raw.RelationTypeSnake)
|
|
req.OccurredAt = firstNonBlank(req.OccurredAt, raw.OccurredAtSnake)
|
|
if req.ApplyID.Int64() <= 0 {
|
|
req.ApplyID = raw.ApplyIDSnake
|
|
}
|
|
if req.UserID.Int64() <= 0 {
|
|
req.UserID = firstFlexible(raw.UserIDSnake, req.SendUserID, raw.SendUserIDSnake)
|
|
}
|
|
if req.CpUserID.Int64() <= 0 {
|
|
req.CpUserID = firstFlexible(raw.CpUserIDSnake, req.AcceptUserID, raw.AcceptUserIDSnake, raw.TargetUserID, raw.TargetUserIDSnake, raw.ToUserIDSnake)
|
|
}
|
|
if req.SendUserID.Int64() <= 0 {
|
|
req.SendUserID = firstFlexible(req.UserID, raw.SendUserIDSnake)
|
|
}
|
|
if req.AcceptUserID.Int64() <= 0 {
|
|
req.AcceptUserID = firstFlexible(req.CpUserID, raw.AcceptUserIDSnake, raw.TargetUserID, raw.TargetUserIDSnake, raw.ToUserIDSnake)
|
|
}
|
|
return req, nil
|
|
}
|
|
|
|
func firstFlexible(values ...flexibleInt64) flexibleInt64 {
|
|
for _, value := range values {
|
|
if value.Int64() > 0 {
|
|
return value
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
type messageEventEnvelope struct {
|
|
Tag string `json:"tag"`
|
|
Body json.RawMessage `json:"body"`
|
|
}
|
|
|
|
func (e messageEventEnvelope) bodyPayload() (string, bool, error) {
|
|
raw := strings.TrimSpace(string(e.Body))
|
|
if raw == "" || raw == "null" {
|
|
return "", false, nil
|
|
}
|
|
if strings.HasPrefix(raw, "\"") {
|
|
var body string
|
|
if err := json.Unmarshal(e.Body, &body); err != nil {
|
|
return "", false, err
|
|
}
|
|
body = strings.TrimSpace(body)
|
|
return body, body != "", nil
|
|
}
|
|
return raw, true, nil
|
|
}
|
|
|
|
func shouldSkipEnvelope(actualTag, expectedTag string) bool {
|
|
actualTag = strings.TrimSpace(actualTag)
|
|
expectedTag = strings.TrimSpace(expectedTag)
|
|
if actualTag == "" || expectedTag == "" || expectedTag == "*" {
|
|
return false
|
|
}
|
|
return actualTag != expectedTag
|
|
}
|