用户锁国家
This commit is contained in:
parent
d67213fca6
commit
a844c7b738
@ -3251,6 +3251,7 @@ definitions:
|
|||||||
- DISPLAY_USER_ID_EXISTS
|
- DISPLAY_USER_ID_EXISTS
|
||||||
- DISPLAY_USER_ID_COOLDOWN
|
- DISPLAY_USER_ID_COOLDOWN
|
||||||
- COUNTRY_CHANGE_COOLDOWN
|
- COUNTRY_CHANGE_COOLDOWN
|
||||||
|
- COUNTRY_CHANGE_IDENTITY_LOCKED
|
||||||
- DISPLAY_USER_ID_NOT_FOUND
|
- DISPLAY_USER_ID_NOT_FOUND
|
||||||
- DISPLAY_USER_ID_PRETTY_NOT_AVAILABLE
|
- DISPLAY_USER_ID_PRETTY_NOT_AVAILABLE
|
||||||
- DISPLAY_USER_ID_PRETTY_ACTIVE
|
- DISPLAY_USER_ID_PRETTY_ACTIVE
|
||||||
|
|||||||
@ -1682,6 +1682,7 @@ definitions:
|
|||||||
- DISPLAY_USER_ID_EXISTS
|
- DISPLAY_USER_ID_EXISTS
|
||||||
- DISPLAY_USER_ID_COOLDOWN
|
- DISPLAY_USER_ID_COOLDOWN
|
||||||
- COUNTRY_CHANGE_COOLDOWN
|
- COUNTRY_CHANGE_COOLDOWN
|
||||||
|
- COUNTRY_CHANGE_IDENTITY_LOCKED
|
||||||
- DISPLAY_USER_ID_NOT_FOUND
|
- DISPLAY_USER_ID_NOT_FOUND
|
||||||
- DISPLAY_USER_ID_ALLOCATE_FAILED
|
- DISPLAY_USER_ID_ALLOCATE_FAILED
|
||||||
- DISPLAY_USER_ID_PRETTY_NOT_AVAILABLE
|
- DISPLAY_USER_ID_PRETTY_NOT_AVAILABLE
|
||||||
|
|||||||
@ -161,6 +161,7 @@ User 专用 reason:
|
|||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| `INVALID_INVITE_CODE` | `InvalidArgument` | `400` |
|
| `INVALID_INVITE_CODE` | `InvalidArgument` | `400` |
|
||||||
| `COUNTRY_CHANGE_COOLDOWN` | `FailedPrecondition` | `409` |
|
| `COUNTRY_CHANGE_COOLDOWN` | `FailedPrecondition` | `409` |
|
||||||
|
| `COUNTRY_CHANGE_IDENTITY_LOCKED` | `PermissionDenied` | `403` |
|
||||||
| `USER_PROFILE_REQUIRED` | `FailedPrecondition` | `403` |
|
| `USER_PROFILE_REQUIRED` | `FailedPrecondition` | `403` |
|
||||||
|
|
||||||
Wallet 专用 reason:
|
Wallet 专用 reason:
|
||||||
|
|||||||
@ -1007,6 +1007,7 @@ gateway HTTP envelope 的 `code` 不使用数字。
|
|||||||
| `PROFILE_REQUIRED` | 403 | authenticated user has not completed required registration profile |
|
| `PROFILE_REQUIRED` | 403 | authenticated user has not completed required registration profile |
|
||||||
| `PASSWORD_ALREADY_SET` | 409 | user already has password identity |
|
| `PASSWORD_ALREADY_SET` | 409 | user already has password identity |
|
||||||
| `COUNTRY_CHANGE_COOLDOWN` | 409 | country change is within the 30-day cooldown window |
|
| `COUNTRY_CHANGE_COOLDOWN` | 409 | country change is within the 30-day cooldown window |
|
||||||
|
| `COUNTRY_CHANGE_IDENTITY_LOCKED` | 403 | an existing host or business identity requires customer-service country changes |
|
||||||
| `USER_DISABLED` | 403 | user is disabled or banned |
|
| `USER_DISABLED` | 403 | user is disabled or banned |
|
||||||
| `SESSION_EXPIRED` | 401 | refresh token expired |
|
| `SESSION_EXPIRED` | 401 | refresh token expired |
|
||||||
| `SESSION_REVOKED` | 401 | refresh token revoked |
|
| `SESSION_REVOKED` | 401 | refresh token revoked |
|
||||||
|
|||||||
@ -70,6 +70,12 @@ var catalog = map[Code]Spec{
|
|||||||
DisplayUserIDPaymentRequired: spec(codes.FailedPrecondition, httpStatusConflict, DisplayUserIDPaymentRequired, "pretty display user id payment required"),
|
DisplayUserIDPaymentRequired: spec(codes.FailedPrecondition, httpStatusConflict, DisplayUserIDPaymentRequired, "pretty display user id payment required"),
|
||||||
|
|
||||||
CountryChangeCooldown: spec(codes.FailedPrecondition, httpStatusConflict, CountryChangeCooldown, "country change is cooling down"),
|
CountryChangeCooldown: spec(codes.FailedPrecondition, httpStatusConflict, CountryChangeCooldown, "country change is cooling down"),
|
||||||
|
CountryChangeIdentityLocked: spec(
|
||||||
|
codes.PermissionDenied,
|
||||||
|
httpStatusForbidden,
|
||||||
|
CountryChangeIdentityLocked,
|
||||||
|
"You already have a host identity. Please contact customer service to change your country.",
|
||||||
|
),
|
||||||
CountryNotFound: spec(codes.NotFound, httpStatusNotFound, CountryNotFound, "not found"),
|
CountryNotFound: spec(codes.NotFound, httpStatusNotFound, CountryNotFound, "not found"),
|
||||||
RegionNotFound: spec(codes.NotFound, httpStatusNotFound, RegionNotFound, "not found"),
|
RegionNotFound: spec(codes.NotFound, httpStatusNotFound, RegionNotFound, "not found"),
|
||||||
RegionCountryConflict: spec(codes.FailedPrecondition, httpStatusConflict, RegionCountryConflict, "country already belongs to another region"),
|
RegionCountryConflict: spec(codes.FailedPrecondition, httpStatusConflict, RegionCountryConflict, "country already belongs to another region"),
|
||||||
|
|||||||
@ -66,6 +66,8 @@ const (
|
|||||||
DisplayUserIDPaymentRequired Code = "DISPLAY_USER_ID_PAYMENT_REQUIRED"
|
DisplayUserIDPaymentRequired Code = "DISPLAY_USER_ID_PAYMENT_REQUIRED"
|
||||||
// CountryChangeCooldown 表示用户仍处在国家修改冷却期。
|
// CountryChangeCooldown 表示用户仍处在国家修改冷却期。
|
||||||
CountryChangeCooldown Code = "COUNTRY_CHANGE_COOLDOWN"
|
CountryChangeCooldown Code = "COUNTRY_CHANGE_COOLDOWN"
|
||||||
|
// CountryChangeIdentityLocked 表示用户已有经营身份,国家只能由客服治理入口修改。
|
||||||
|
CountryChangeIdentityLocked Code = "COUNTRY_CHANGE_IDENTITY_LOCKED"
|
||||||
// CountryNotFound 表示国家主数据不存在或已经停用。
|
// CountryNotFound 表示国家主数据不存在或已经停用。
|
||||||
CountryNotFound Code = "COUNTRY_NOT_FOUND"
|
CountryNotFound Code = "COUNTRY_NOT_FOUND"
|
||||||
// RegionNotFound 表示区域主数据不存在。
|
// RegionNotFound 表示区域主数据不存在。
|
||||||
|
|||||||
@ -159,6 +159,14 @@ func TestCatalogMappings(t *testing.T) {
|
|||||||
publicCode: string(CountryChangeCooldown),
|
publicCode: string(CountryChangeCooldown),
|
||||||
publicMessage: "country change is cooling down",
|
publicMessage: "country change is cooling down",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "country identity lock tells user how to proceed",
|
||||||
|
code: CountryChangeIdentityLocked,
|
||||||
|
grpcCode: codes.PermissionDenied,
|
||||||
|
httpStatus: httpStatusForbidden,
|
||||||
|
publicCode: string(CountryChangeIdentityLocked),
|
||||||
|
publicMessage: "You already have a host identity. Please contact customer service to change your country.",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "registered device conflict exposes concrete login reason",
|
name: "registered device conflict exposes concrete login reason",
|
||||||
code: DeviceAlreadyRegistered,
|
code: DeviceAlreadyRegistered,
|
||||||
|
|||||||
@ -11105,6 +11105,27 @@ func TestChangeCountryUsesAuthenticatedUserIDAndMapsCooldown(t *testing.T) {
|
|||||||
router.ServeHTTP(recorder, request)
|
router.ServeHTTP(recorder, request)
|
||||||
|
|
||||||
assertEnvelopeMessage(t, recorder, http.StatusConflict, string(xerr.CountryChangeCooldown), "country change is cooling down", "req-country-cooldown")
|
assertEnvelopeMessage(t, recorder, http.StatusConflict, string(xerr.CountryChangeCooldown), "country change is cooling down", "req-country-cooldown")
|
||||||
|
|
||||||
|
profileClient = &fakeUserProfileClient{countryErr: xerr.ToGRPCError(xerr.New(
|
||||||
|
xerr.CountryChangeIdentityLocked,
|
||||||
|
"You already have a host identity. Please contact customer service to change your country.",
|
||||||
|
))}
|
||||||
|
router = NewHandlerWithClients(&fakeRoomClient{}, nil, nil, profileClient).Routes(auth.NewVerifier("secret"))
|
||||||
|
request = httptest.NewRequest(http.MethodPost, "/api/v1/users/me/country/change", bytes.NewReader(body))
|
||||||
|
request.Header.Set("Authorization", "Bearer "+signGatewayToken(t, "secret", 42))
|
||||||
|
request.Header.Set("X-Request-ID", "req-country-identity-locked")
|
||||||
|
recorder = httptest.NewRecorder()
|
||||||
|
|
||||||
|
router.ServeHTTP(recorder, request)
|
||||||
|
|
||||||
|
assertEnvelopeMessage(
|
||||||
|
t,
|
||||||
|
recorder,
|
||||||
|
http.StatusForbidden,
|
||||||
|
string(xerr.CountryChangeIdentityLocked),
|
||||||
|
"You already have a host identity. Please contact customer service to change your country.",
|
||||||
|
"req-country-identity-locked",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestChangeCountryRemovesOldRegionBroadcastMember(t *testing.T) {
|
func TestChangeCountryRemovesOldRegionBroadcastMember(t *testing.T) {
|
||||||
|
|||||||
@ -272,7 +272,11 @@ func (s *Service) ChangeUserCountry(ctx context.Context, userID int64, country s
|
|||||||
}
|
}
|
||||||
if role := blockedCountryChangeRole(summary); role != "" {
|
if role := blockedCountryChangeRole(summary); role != "" {
|
||||||
// App 自助入口不能改变已有经营身份的区域归属,否则房间、组织和币商读模型会在用户侧无审计地漂移。
|
// App 自助入口不能改变已有经营身份的区域归属,否则房间、组织和币商读模型会在用户侧无审计地漂移。
|
||||||
return userdomain.User{}, 0, xerr.New(xerr.PermissionDenied, "you have "+role+" id, not allow modify country")
|
// 使用独立公开 reason,避免 gateway 把普通 PERMISSION_DENIED 脱敏成语义不明的固定文案。
|
||||||
|
return userdomain.User{}, 0, xerr.New(
|
||||||
|
xerr.CountryChangeIdentityLocked,
|
||||||
|
"You already have a host identity. Please contact customer service to change your country.",
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
resolvedCountry, regionID, err := s.resolveCountryRegionForChange(ctx, country)
|
resolvedCountry, regionID, err := s.resolveCountryRegionForChange(ctx, country)
|
||||||
|
|||||||
@ -114,10 +114,10 @@ func TestChangeUserCountryRejectsProtectedRoles(t *testing.T) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
_, _, err := svc.ChangeUserCountry(appcode.WithContext(context.Background(), "lalu"), 10001, "ID", "req-protected")
|
_, _, err := svc.ChangeUserCountry(appcode.WithContext(context.Background(), "lalu"), 10001, "ID", "req-protected")
|
||||||
if xerr.CodeOf(err) != xerr.PermissionDenied {
|
if xerr.CodeOf(err) != xerr.CountryChangeIdentityLocked {
|
||||||
t.Fatalf("protected role must be permission denied: role=%s err=%v", tc.role, err)
|
t.Fatalf("protected role must return identity lock: role=%s err=%v", tc.role, err)
|
||||||
}
|
}
|
||||||
want := "you have " + tc.role + " id, not allow modify country"
|
want := "You already have a host identity. Please contact customer service to change your country."
|
||||||
if !strings.Contains(err.Error(), want) {
|
if !strings.Contains(err.Error(), want) {
|
||||||
t.Fatalf("protected role message mismatch: got=%q want contains=%q", err.Error(), want)
|
t.Fatalf("protected role message mismatch: got=%q want contains=%q", err.Error(), want)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user