diff --git a/services/user-service/internal/service/user/country_outbox_test.go b/services/user-service/internal/service/user/country_outbox_test.go index 1344307f..a95a54e4 100644 --- a/services/user-service/internal/service/user/country_outbox_test.go +++ b/services/user-service/internal/service/user/country_outbox_test.go @@ -84,3 +84,54 @@ func TestAdminChangeUserCountryOverridesProtectedRoleAndWritesOutbox(t *testing. t.Fatalf("country change session revoke mismatch: at=%d reason=%q request=%q by=%q", revokedAtMs, revokedReason, revokedRequestID, revokedBy) } } + +func TestAdminChangeUserCountryWritesOutboxWhenRegionUnchanged(t *testing.T) { + ctx := appcode.WithContext(context.Background(), appcode.Default) + repository := mysqltest.NewRepository(t) + repository.PutCountry(userdomain.Country{CountryCode: "US", Enabled: true}) + repository.PutCountry(userdomain.Country{CountryCode: "CA", Enabled: true}) + repository.PutRegion(userdomain.Region{RegionID: 303, RegionCode: "NORTH-AMERICA", Name: "North America", Countries: []string{"US", "CA"}}) + repository.PutUser(userdomain.User{ + UserID: 91002, + DefaultDisplayUserID: "91002", + CurrentDisplayUserID: "91002", + Country: "US", + RegionID: 303, + ProfileCompleted: true, + ProfileCompletedAtMs: 1, + OnboardingStatus: userdomain.OnboardingStatusCompleted, + Status: userdomain.StatusActive, + }) + svc := userservice.New(repository, + userservice.WithCountryRegionRepository(repository), + userservice.WithClock(func() time.Time { return time.UnixMilli(1_700_000_456_000).UTC() }), + ) + + updated, nextAllowedAt, err := svc.AdminChangeUserCountry(ctx, 91002, "CA", "req-admin-country-same-region") + if err != nil { + t.Fatalf("AdminChangeUserCountry failed: %v", err) + } + if nextAllowedAt != 0 || updated.Country != "CA" || updated.RegionID != 303 { + t.Fatalf("admin same-region country result mismatch: user=%+v next=%d", updated, nextAllowedAt) + } + + var payloadJSON string + if err := repository.RawDB().QueryRowContext(ctx, ` + SELECT CAST(payload_json AS CHAR) + FROM user_outbox + WHERE app_code = ? AND event_type = 'UserRegionChanged' AND aggregate_id = ? + `, appcode.Default, int64(91002)).Scan(&payloadJSON); err != nil { + t.Fatalf("query same-region UserRegionChanged outbox failed: %v", err) + } + var payload map[string]any + if err := json.Unmarshal([]byte(payloadJSON), &payload); err != nil { + t.Fatalf("decode same-region outbox payload failed: %v", err) + } + if payload["source"] != "admin" || + payload["old_country"] != "US" || + payload["new_country"] != "CA" || + int64(payload["old_region_id"].(float64)) != 303 || + int64(payload["new_region_id"].(float64)) != 303 { + t.Fatalf("same-region outbox payload mismatch: %v", payload) + } +} diff --git a/services/user-service/internal/storage/mysql/user/repository.go b/services/user-service/internal/storage/mysql/user/repository.go index 4a94d1ec..9ab24e4e 100644 --- a/services/user-service/internal/storage/mysql/user/repository.go +++ b/services/user-service/internal/storage/mysql/user/repository.go @@ -185,10 +185,6 @@ func insertUserRegisteredOutbox(ctx context.Context, tx *sql.Tx, user userdomain } func insertUserRegionChangedOutbox(ctx context.Context, tx *sql.Tx, command userdomain.CountryChangeCommand, oldCountry string, oldRegionID int64) error { - if oldRegionID == command.NewRegionID { - // 下游只同步当前区域归属读模型;同区域改国家不产生区域迁移事实,避免无意义重放刷新房间和 IM 分组。 - return nil - } source := strings.TrimSpace(command.Source) if source == "" { source = "unknown"