package service import ( "context" "encoding/json" "errors" "strings" roomv1 "hyapp.local/api/proto/room/v1" "hyapp/pkg/appcode" "hyapp/pkg/usermq" "hyapp/services/room-service/internal/room/state" ) type userRegionChangedPayload struct { UserID int64 `json:"user_id"` OldCountry string `json:"old_country"` NewCountry string `json:"new_country"` OldRegionID int64 `json:"old_region_id"` NewRegionID int64 `json:"new_region_id"` Source string `json:"source"` RequestID string `json:"request_id"` ChangedAtMS int64 `json:"changed_at_ms"` } // HandleUserOutboxMessage 只消费 user-service 的当前区域迁移事实;历史注册、邀请或麦位事实不是 room-service 的状态输入。 func (s *Service) HandleUserOutboxMessage(ctx context.Context, message usermq.UserOutboxMessage) error { if strings.TrimSpace(message.EventType) != "UserRegionChanged" { return nil } var payload userRegionChangedPayload if err := json.Unmarshal([]byte(message.PayloadJSON), &payload); err != nil { return err } if payload.UserID <= 0 || payload.NewRegionID < 0 { // user_outbox 已经是 owner service 提交后的事实;结构错误交给 MQ 重试和告警,不在本地猜测修复。 return errors.New("user region changed payload is invalid") } if payload.UserID != message.AggregateID { // aggregate_id 是 outbox 幂等和审计键,必须和 payload 指向同一个用户,避免错误事件迁移别人的房间。 return errors.New("user region changed aggregate_id mismatch") } ctx = appcode.WithContext(ctx, message.AppCode) meta, exists, err := s.repository.GetRoomMetaByOwner(ctx, payload.UserID) if err != nil { return err } if !exists || meta.Status == state.RoomStatusDeleted || meta.VisibleRegionID == payload.NewRegionID { return nil } requestID := strings.TrimSpace(payload.RequestID) if requestID == "" { requestID = message.EventID } sentAtMS := payload.ChangedAtMS if sentAtMS <= 0 { sentAtMS = message.OccurredAtMS } _, err = s.AdminUpdateRoom(ctx, &roomv1.AdminUpdateRoomRequest{ Meta: &roomv1.RequestMeta{ AppCode: message.AppCode, RequestId: requestID, CommandId: message.EventID, ActorUserId: payload.UserID, RoomId: meta.RoomID, SentAtMs: sentAtMS, }, VisibleRegionId: &payload.NewRegionID, AdminName: "user-region-sync", }) return err }