fix(ops): resolve lucky gift profile short IDs
This commit is contained in:
parent
98d7743d7a
commit
dba3cada59
File diff suppressed because it is too large
Load Diff
@ -1545,6 +1545,18 @@ message ListAppsResponse {
|
||||
repeated App apps = 1;
|
||||
}
|
||||
|
||||
// ResolveAdminUserIdentifierRequest 是后台精确查人的内部契约。与客户端展示号解析不同,
|
||||
// user_identifier 可以是永久默认短号、当前有效展示号或系统长 user_id。
|
||||
message ResolveAdminUserIdentifierRequest {
|
||||
RequestMeta meta = 1;
|
||||
string user_identifier = 2;
|
||||
}
|
||||
|
||||
// ResolveAdminUserIdentifierResponse 返回匹配用户的当前有效身份投影。
|
||||
message ResolveAdminUserIdentifierResponse {
|
||||
UserIdentity identity = 1;
|
||||
}
|
||||
|
||||
// AppRegistryService 是 gateway 解析 package_name -> app_code 的内部入口。
|
||||
service AppRegistryService {
|
||||
rpc ResolveApp(ResolveAppRequest) returns (ResolveAppResponse);
|
||||
@ -1577,6 +1589,8 @@ service RegionAdminService {
|
||||
service UserIdentityService {
|
||||
rpc GetUserIdentity(GetUserIdentityRequest) returns (GetUserIdentityResponse);
|
||||
rpc ResolveDisplayUserID(ResolveDisplayUserIDRequest) returns (ResolveDisplayUserIDResponse);
|
||||
// ResolveAdminUserIdentifier 仅供内部后台精确搜索;不得替代客户端 active 展示号解析语义。
|
||||
rpc ResolveAdminUserIdentifier(ResolveAdminUserIdentifierRequest) returns (ResolveAdminUserIdentifierResponse);
|
||||
rpc ChangeDisplayUserID(ChangeDisplayUserIDRequest) returns (ChangeDisplayUserIDResponse);
|
||||
rpc ApplyPrettyDisplayUserID(ApplyPrettyDisplayUserIDRequest) returns (ApplyPrettyDisplayUserIDResponse);
|
||||
rpc ListAvailablePrettyDisplayIDs(ListAvailablePrettyDisplayIDsRequest) returns (ListAvailablePrettyDisplayIDsResponse);
|
||||
|
||||
@ -3175,6 +3175,7 @@ var RegionAdminService_ServiceDesc = grpc.ServiceDesc{
|
||||
const (
|
||||
UserIdentityService_GetUserIdentity_FullMethodName = "/hyapp.user.v1.UserIdentityService/GetUserIdentity"
|
||||
UserIdentityService_ResolveDisplayUserID_FullMethodName = "/hyapp.user.v1.UserIdentityService/ResolveDisplayUserID"
|
||||
UserIdentityService_ResolveAdminUserIdentifier_FullMethodName = "/hyapp.user.v1.UserIdentityService/ResolveAdminUserIdentifier"
|
||||
UserIdentityService_ChangeDisplayUserID_FullMethodName = "/hyapp.user.v1.UserIdentityService/ChangeDisplayUserID"
|
||||
UserIdentityService_ApplyPrettyDisplayUserID_FullMethodName = "/hyapp.user.v1.UserIdentityService/ApplyPrettyDisplayUserID"
|
||||
UserIdentityService_ListAvailablePrettyDisplayIDs_FullMethodName = "/hyapp.user.v1.UserIdentityService/ListAvailablePrettyDisplayIDs"
|
||||
@ -3190,6 +3191,8 @@ const (
|
||||
type UserIdentityServiceClient interface {
|
||||
GetUserIdentity(ctx context.Context, in *GetUserIdentityRequest, opts ...grpc.CallOption) (*GetUserIdentityResponse, error)
|
||||
ResolveDisplayUserID(ctx context.Context, in *ResolveDisplayUserIDRequest, opts ...grpc.CallOption) (*ResolveDisplayUserIDResponse, error)
|
||||
// ResolveAdminUserIdentifier 仅供内部后台精确搜索;不得替代客户端 active 展示号解析语义。
|
||||
ResolveAdminUserIdentifier(ctx context.Context, in *ResolveAdminUserIdentifierRequest, opts ...grpc.CallOption) (*ResolveAdminUserIdentifierResponse, error)
|
||||
ChangeDisplayUserID(ctx context.Context, in *ChangeDisplayUserIDRequest, opts ...grpc.CallOption) (*ChangeDisplayUserIDResponse, error)
|
||||
ApplyPrettyDisplayUserID(ctx context.Context, in *ApplyPrettyDisplayUserIDRequest, opts ...grpc.CallOption) (*ApplyPrettyDisplayUserIDResponse, error)
|
||||
ListAvailablePrettyDisplayIDs(ctx context.Context, in *ListAvailablePrettyDisplayIDsRequest, opts ...grpc.CallOption) (*ListAvailablePrettyDisplayIDsResponse, error)
|
||||
@ -3225,6 +3228,16 @@ func (c *userIdentityServiceClient) ResolveDisplayUserID(ctx context.Context, in
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *userIdentityServiceClient) ResolveAdminUserIdentifier(ctx context.Context, in *ResolveAdminUserIdentifierRequest, opts ...grpc.CallOption) (*ResolveAdminUserIdentifierResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ResolveAdminUserIdentifierResponse)
|
||||
err := c.cc.Invoke(ctx, UserIdentityService_ResolveAdminUserIdentifier_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *userIdentityServiceClient) ChangeDisplayUserID(ctx context.Context, in *ChangeDisplayUserIDRequest, opts ...grpc.CallOption) (*ChangeDisplayUserIDResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ChangeDisplayUserIDResponse)
|
||||
@ -3283,6 +3296,8 @@ func (c *userIdentityServiceClient) ExpirePrettyDisplayUserID(ctx context.Contex
|
||||
type UserIdentityServiceServer interface {
|
||||
GetUserIdentity(context.Context, *GetUserIdentityRequest) (*GetUserIdentityResponse, error)
|
||||
ResolveDisplayUserID(context.Context, *ResolveDisplayUserIDRequest) (*ResolveDisplayUserIDResponse, error)
|
||||
// ResolveAdminUserIdentifier 仅供内部后台精确搜索;不得替代客户端 active 展示号解析语义。
|
||||
ResolveAdminUserIdentifier(context.Context, *ResolveAdminUserIdentifierRequest) (*ResolveAdminUserIdentifierResponse, error)
|
||||
ChangeDisplayUserID(context.Context, *ChangeDisplayUserIDRequest) (*ChangeDisplayUserIDResponse, error)
|
||||
ApplyPrettyDisplayUserID(context.Context, *ApplyPrettyDisplayUserIDRequest) (*ApplyPrettyDisplayUserIDResponse, error)
|
||||
ListAvailablePrettyDisplayIDs(context.Context, *ListAvailablePrettyDisplayIDsRequest) (*ListAvailablePrettyDisplayIDsResponse, error)
|
||||
@ -3304,6 +3319,9 @@ func (UnimplementedUserIdentityServiceServer) GetUserIdentity(context.Context, *
|
||||
func (UnimplementedUserIdentityServiceServer) ResolveDisplayUserID(context.Context, *ResolveDisplayUserIDRequest) (*ResolveDisplayUserIDResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ResolveDisplayUserID not implemented")
|
||||
}
|
||||
func (UnimplementedUserIdentityServiceServer) ResolveAdminUserIdentifier(context.Context, *ResolveAdminUserIdentifierRequest) (*ResolveAdminUserIdentifierResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ResolveAdminUserIdentifier not implemented")
|
||||
}
|
||||
func (UnimplementedUserIdentityServiceServer) ChangeDisplayUserID(context.Context, *ChangeDisplayUserIDRequest) (*ChangeDisplayUserIDResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ChangeDisplayUserID not implemented")
|
||||
}
|
||||
@ -3376,6 +3394,24 @@ func _UserIdentityService_ResolveDisplayUserID_Handler(srv interface{}, ctx cont
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _UserIdentityService_ResolveAdminUserIdentifier_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ResolveAdminUserIdentifierRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(UserIdentityServiceServer).ResolveAdminUserIdentifier(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: UserIdentityService_ResolveAdminUserIdentifier_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(UserIdentityServiceServer).ResolveAdminUserIdentifier(ctx, req.(*ResolveAdminUserIdentifierRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _UserIdentityService_ChangeDisplayUserID_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ChangeDisplayUserIDRequest)
|
||||
if err := dec(in); err != nil {
|
||||
@ -3481,6 +3517,10 @@ var UserIdentityService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "ResolveDisplayUserID",
|
||||
Handler: _UserIdentityService_ResolveDisplayUserID_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ResolveAdminUserIdentifier",
|
||||
Handler: _UserIdentityService_ResolveAdminUserIdentifier_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ChangeDisplayUserID",
|
||||
Handler: _UserIdentityService_ChangeDisplayUserID_Handler,
|
||||
|
||||
@ -177,6 +177,13 @@ type ResolveDisplayUserIDRequest struct {
|
||||
DisplayUserID string
|
||||
}
|
||||
|
||||
// ResolveAdminUserIdentifierRequest 仅供后台精确搜索永久默认短号、当前有效展示号或系统长 user_id。
|
||||
type ResolveAdminUserIdentifierRequest struct {
|
||||
RequestID string
|
||||
Caller string
|
||||
UserIdentifier string
|
||||
}
|
||||
|
||||
type UserIdentity struct {
|
||||
UserID int64 `json:"userId,string"`
|
||||
DisplayUserID string `json:"displayUserId"`
|
||||
@ -437,6 +444,19 @@ func (c *GRPCClient) ResolveDisplayUserID(ctx context.Context, req ResolveDispla
|
||||
return fromProtoUserIdentity(resp.GetIdentity()), nil
|
||||
}
|
||||
|
||||
// ResolveAdminUserIdentifier 调用 user-service owner 的后台专用解析契约;admin-server 不读取用户库,
|
||||
// 也不会为了搜索默认短号而改变客户端 ResolveDisplayUserID 的 active-only 语义。
|
||||
func (c *GRPCClient) ResolveAdminUserIdentifier(ctx context.Context, req ResolveAdminUserIdentifierRequest) (*UserIdentity, error) {
|
||||
resp, err := c.identityClient.ResolveAdminUserIdentifier(ctx, &userv1.ResolveAdminUserIdentifierRequest{
|
||||
Meta: requestMeta(ctx, req.RequestID, req.Caller),
|
||||
UserIdentifier: req.UserIdentifier,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fromProtoUserIdentity(resp.GetIdentity()), nil
|
||||
}
|
||||
|
||||
func fromProtoRegisterRiskConfig(config *userv1.RegisterRiskConfig) *RegisterRiskConfig {
|
||||
if config == nil {
|
||||
return nil
|
||||
|
||||
@ -22,11 +22,18 @@ import (
|
||||
|
||||
type Handler struct {
|
||||
luckyGift luckygiftadmin.Client
|
||||
users userclient.Client
|
||||
users luckyGiftUserClient
|
||||
requestTimeout time.Duration
|
||||
audit shared.OperationLogger
|
||||
}
|
||||
|
||||
// luckyGiftUserClient 只暴露画像页面需要的 owner 能力。后台搜索必须走专用精确解析 RPC,
|
||||
// 用户资料仍批量读取,避免给全局 userclient.Client 增加画像模块专属依赖。
|
||||
type luckyGiftUserClient interface {
|
||||
BatchGetUsers(ctx context.Context, req userclient.BatchGetUsersRequest) (map[int64]*userclient.User, error)
|
||||
ResolveAdminUserIdentifier(ctx context.Context, req userclient.ResolveAdminUserIdentifierRequest) (*userclient.UserIdentity, error)
|
||||
}
|
||||
|
||||
func New(luckyGift luckygiftadmin.Client, requestTimeout time.Duration, audit shared.OperationLogger) *Handler {
|
||||
if requestTimeout <= 0 {
|
||||
requestTimeout = 3 * time.Second
|
||||
@ -36,7 +43,7 @@ func New(luckyGift luckygiftadmin.Client, requestTimeout time.Duration, audit sh
|
||||
|
||||
// BindUserClient 注入 user-service 主数据读取能力。幸运礼物 owner 只保存不可变的内部 user_id,
|
||||
// 短号、默认号、靓号和用户资料必须在 admin-server 展示边界实时解析,不能复制进幸运礼物画像表。
|
||||
func (h *Handler) BindUserClient(users userclient.Client) *Handler {
|
||||
func (h *Handler) BindUserClient(users luckyGiftUserClient) *Handler {
|
||||
h.users = users
|
||||
return h
|
||||
}
|
||||
|
||||
@ -335,38 +335,21 @@ func (h *Handler) resolveLuckyGiftProfileKeyword(ctx context.Context, appCode, i
|
||||
if h.users == nil {
|
||||
return 0, externalUserID, false, fmt.Errorf("lucky gift user profile resolver is not configured")
|
||||
}
|
||||
identity, err := h.users.ResolveDisplayUserID(appctx.WithContext(ctx, appCode), userclient.ResolveDisplayUserIDRequest{
|
||||
RequestID: requestID, Caller: "admin-server", DisplayUserID: keyword,
|
||||
identity, err := h.users.ResolveAdminUserIdentifier(appctx.WithContext(ctx, appCode), userclient.ResolveAdminUserIdentifierRequest{
|
||||
RequestID: requestID, Caller: "admin-server", UserIdentifier: keyword,
|
||||
})
|
||||
if err == nil && identity != nil && identity.UserID > 0 {
|
||||
return identity.UserID, externalUserID, true, nil
|
||||
}
|
||||
if err != nil {
|
||||
switch grpcstatus.Code(err) {
|
||||
case codes.NotFound:
|
||||
// 继续尝试把纯数字解释为系统长 user_id。
|
||||
case codes.InvalidArgument:
|
||||
case codes.NotFound, codes.InvalidArgument:
|
||||
// all 模式下 keyword 也可能是任意 external_user_id;内部展示号格式不合法只代表内部未命中,不能阻断外部精确搜索。
|
||||
return 0, externalUserID, false, nil
|
||||
default:
|
||||
return 0, externalUserID, false, err
|
||||
}
|
||||
}
|
||||
|
||||
// 纯数字先按展示号解析;只有展示号确实不存在时才回退为 64 位长 user_id,避免短号与长号碰撞时查错人。
|
||||
longUserID, parseErr := strconv.ParseInt(keyword, 10, 64)
|
||||
if parseErr != nil || longUserID <= 0 {
|
||||
return 0, externalUserID, false, nil
|
||||
}
|
||||
user, getErr := h.users.GetUser(appctx.WithContext(ctx, appCode), userclient.GetUserRequest{
|
||||
RequestID: requestID, Caller: "admin-server", UserID: longUserID,
|
||||
})
|
||||
if getErr == nil && user != nil {
|
||||
return user.UserID, externalUserID, true, nil
|
||||
}
|
||||
if getErr != nil && grpcstatus.Code(getErr) != codes.NotFound {
|
||||
return 0, externalUserID, false, getErr
|
||||
}
|
||||
return 0, externalUserID, false, nil
|
||||
}
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@ package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"hyapp/pkg/appcode"
|
||||
"hyapp/pkg/xerr"
|
||||
@ -51,6 +52,26 @@ func (s *Service) ResolveDisplayUserID(ctx context.Context, displayUserID string
|
||||
return s.identityRepository.ResolveDisplayUserID(ctx, displayUserID, s.now().UnixMilli())
|
||||
}
|
||||
|
||||
// ResolveAdminUserIdentifier 供内部后台按默认短号、当前有效展示号或系统长 user_id 精确查人。
|
||||
// 它与客户端 ResolveDisplayUserID 分离,避免把 held 默认号意外恢复成登录或普通业务可解析身份。
|
||||
func (s *Service) ResolveAdminUserIdentifier(ctx context.Context, userIdentifier string) (userdomain.Identity, error) {
|
||||
userIdentifier = strings.TrimSpace(userIdentifier)
|
||||
if !userdomain.ValidResolvableDisplayUserID(userIdentifier) {
|
||||
return userdomain.Identity{}, xerr.New(xerr.InvalidArgument, "user_identifier format is invalid")
|
||||
}
|
||||
if s.identityRepository == nil {
|
||||
return userdomain.Identity{}, xerr.New(xerr.Unavailable, "identity repository is not configured")
|
||||
}
|
||||
|
||||
userID, err := s.identityRepository.ResolveAdminUserIdentifier(ctx, userIdentifier, s.now().UnixMilli())
|
||||
if err != nil {
|
||||
return userdomain.Identity{}, err
|
||||
}
|
||||
// repository 只决定搜索别名归属;最终复用 GetUserIdentity 返回当前 active 投影,
|
||||
// 因此用 held 默认号搜索到有靓号的用户时,响应仍展示当前有效靓号。
|
||||
return s.GetUserIdentity(ctx, userID)
|
||||
}
|
||||
|
||||
// ChangeDisplayUserID 修改用户默认短号,并由 repository 在事务内维护当前展示快照和审计。
|
||||
func (s *Service) ChangeDisplayUserID(ctx context.Context, userID int64, newDisplayUserID string, reason string, operatorUserID int64, requestID string) (userdomain.Identity, error) {
|
||||
if userID <= 0 {
|
||||
|
||||
@ -124,6 +124,8 @@ type IdentityRepository interface {
|
||||
GetUserIdentity(ctx context.Context, userID int64) (userdomain.Identity, error)
|
||||
// ResolveDisplayUserID 把短号解析成 user_id,并按 nowMs 触发懒过期。
|
||||
ResolveDisplayUserID(ctx context.Context, displayUserID string, nowMs int64) (userdomain.Identity, error)
|
||||
// ResolveAdminUserIdentifier 为内部后台精确解析永久默认短号、当前有效展示号或系统长 user_id。
|
||||
ResolveAdminUserIdentifier(ctx context.Context, userIdentifier string, nowMs int64) (int64, error)
|
||||
// ChangeDisplayUserID 原子修改默认短号并写变更日志。
|
||||
ChangeDisplayUserID(ctx context.Context, command userdomain.DisplayUserIDChangeCommand) (userdomain.Identity, error)
|
||||
// ApplyPrettyDisplayUserID 原子创建靓号租约并覆盖当前展示号。
|
||||
|
||||
@ -3,6 +3,8 @@ package identity
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"hyapp/pkg/appcode"
|
||||
"hyapp/pkg/xerr"
|
||||
@ -52,6 +54,97 @@ func (r *Repository) ResolveDisplayUserID(ctx context.Context, displayUserID str
|
||||
return identity, nil
|
||||
}
|
||||
|
||||
// ResolveAdminUserIdentifier 是后台精确查人的 owner 入口。它刻意不复用 ResolveDisplayUserID:
|
||||
// 客户端只能解析当前 active 展示号,而运营搜索还必须能用被有效靓号覆盖的永久默认短号定位同一用户。
|
||||
func (r *Repository) ResolveAdminUserIdentifier(ctx context.Context, userIdentifier string, nowMs int64) (int64, error) {
|
||||
userIdentifier = strings.TrimSpace(userIdentifier)
|
||||
appCode := appcode.FromContext(ctx)
|
||||
|
||||
// 输入本身是已过期靓号时先执行既有懒过期流程;这样过期靓号不会继续作为后台搜索别名,
|
||||
// 同时用户的默认短号会恢复 active,后续 GetUserIdentity 返回的始终是当前身份。
|
||||
if err := r.expirePrettyByDisplayUserID(ctx, userIdentifier, nowMs, "lazy_admin_resolve"); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var userID int64
|
||||
// live_display_user_id 是 active/held 事实的生成列并有 app 级唯一索引。active 表示当前有效展示号;
|
||||
// held 只接受仍等于 users.default_display_user_id 的永久默认号,避免 reserved/blocked 或历史释放号被解析。
|
||||
err := r.db.QueryRowContext(ctx, `
|
||||
SELECT du.user_id
|
||||
FROM user_display_user_ids du FORCE INDEX (uk_user_display_user_ids_live)
|
||||
INNER JOIN users u
|
||||
ON u.app_code = du.app_code
|
||||
AND u.user_id = du.user_id
|
||||
WHERE du.app_code = ?
|
||||
AND du.live_display_user_id = ?
|
||||
AND (
|
||||
(du.status = ? AND (du.expires_at_ms IS NULL OR du.expires_at_ms = 0 OR du.expires_at_ms > ?))
|
||||
OR (du.status = ? AND du.display_user_id = u.default_display_user_id)
|
||||
)
|
||||
LIMIT 1
|
||||
`, appCode, userIdentifier, string(userdomain.DisplayUserIDStatusActive), nowMs, string(userdomain.DisplayUserIDStatusHeld)).Scan(&userID)
|
||||
if err == nil {
|
||||
return userID, nil
|
||||
}
|
||||
if err != sql.ErrNoRows {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// 兼容 identity 事实表补齐前的旧用户:两次独立等值查询分别命中 current/default 唯一索引,
|
||||
// 不使用 OR 扫描 app 用户集合。过期 pretty 快照不能作为当前展示号命中。
|
||||
err = r.db.QueryRowContext(ctx, `
|
||||
SELECT user_id
|
||||
FROM users FORCE INDEX (uk_users_current_display_user_id)
|
||||
WHERE app_code = ?
|
||||
AND current_display_user_id = ?
|
||||
AND (
|
||||
current_display_user_id_kind <> ?
|
||||
OR current_display_user_id_expires_at_ms IS NULL
|
||||
OR current_display_user_id_expires_at_ms = 0
|
||||
OR current_display_user_id_expires_at_ms > ?
|
||||
)
|
||||
LIMIT 1
|
||||
`, appCode, userIdentifier, string(userdomain.DisplayUserIDKindPretty), nowMs).Scan(&userID)
|
||||
if err == nil {
|
||||
return userID, nil
|
||||
}
|
||||
if err != sql.ErrNoRows {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
err = r.db.QueryRowContext(ctx, `
|
||||
SELECT user_id
|
||||
FROM users FORCE INDEX (uk_users_default_display_user_id)
|
||||
WHERE app_code = ? AND default_display_user_id = ?
|
||||
LIMIT 1
|
||||
`, appCode, userIdentifier).Scan(&userID)
|
||||
if err == nil {
|
||||
return userID, nil
|
||||
}
|
||||
if err != sql.ErrNoRows {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
// 展示号优先于长 user_id,保持既有后台搜索的碰撞决策;只有所有展示别名都未命中时才查主键。
|
||||
parsedUserID, parseErr := strconv.ParseInt(userIdentifier, 10, 64)
|
||||
if parseErr == nil && parsedUserID > 0 {
|
||||
err = r.db.QueryRowContext(ctx, `
|
||||
SELECT user_id
|
||||
FROM users FORCE INDEX (PRIMARY)
|
||||
WHERE user_id = ? AND app_code = ?
|
||||
LIMIT 1
|
||||
`, parsedUserID, appCode).Scan(&userID)
|
||||
if err == nil {
|
||||
return userID, nil
|
||||
}
|
||||
if err != sql.ErrNoRows {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
return 0, xerr.New(xerr.DisplayUserIDNotFound, "user identifier not found")
|
||||
}
|
||||
|
||||
func (r *Repository) queryActiveIdentity(ctx context.Context, clause string, args ...any) (userdomain.Identity, error) {
|
||||
// user_display_user_ids 的 active 行是展示号归属事实,users.current_display_user_id 只是高频读取快照。
|
||||
// 解析入口优先读取事实表,可以容忍历史脚本或异常中断导致 users 快照暂时没有同步到最新靓号。
|
||||
|
||||
@ -214,6 +214,10 @@ func (r *Repository) ResolveDisplayUserID(ctx context.Context, displayUserID str
|
||||
return r.Repository.IdentityRepository().ResolveDisplayUserID(ctx, displayUserID, nowMs)
|
||||
}
|
||||
|
||||
func (r *Repository) ResolveAdminUserIdentifier(ctx context.Context, userIdentifier string, nowMs int64) (int64, error) {
|
||||
return r.Repository.IdentityRepository().ResolveAdminUserIdentifier(ctx, userIdentifier, nowMs)
|
||||
}
|
||||
|
||||
// ChangeDisplayUserID 让测试 wrapper 继续满足短号接口。
|
||||
func (r *Repository) ChangeDisplayUserID(ctx context.Context, command userdomain.DisplayUserIDChangeCommand) (userdomain.Identity, error) {
|
||||
return r.Repository.IdentityRepository().ChangeDisplayUserID(ctx, command)
|
||||
|
||||
@ -1116,6 +1116,18 @@ func (s *Server) ResolveDisplayUserID(ctx context.Context, req *userv1.ResolveDi
|
||||
return &userv1.ResolveDisplayUserIDResponse{Identity: toProtoIdentity(identity)}, nil
|
||||
}
|
||||
|
||||
// ResolveAdminUserIdentifier 为内部后台精确解析默认短号、当前有效展示号或系统长 user_id。
|
||||
func (s *Server) ResolveAdminUserIdentifier(ctx context.Context, req *userv1.ResolveAdminUserIdentifierRequest) (*userv1.ResolveAdminUserIdentifierResponse, error) {
|
||||
ctx = contextWithApp(ctx, req.GetMeta())
|
||||
// 后台搜索语义由独立 service 用例承载,禁止扩大客户端 ResolveDisplayUserID 的 active-only 边界。
|
||||
identity, err := s.userSvc.ResolveAdminUserIdentifier(ctx, req.GetUserIdentifier())
|
||||
if err != nil {
|
||||
return nil, xerr.ToGRPCError(err)
|
||||
}
|
||||
|
||||
return &userv1.ResolveAdminUserIdentifierResponse{Identity: toProtoIdentity(identity)}, nil
|
||||
}
|
||||
|
||||
// ChangeDisplayUserID 修改当前 active 短号并返回新绑定。
|
||||
func (s *Server) ChangeDisplayUserID(ctx context.Context, req *userv1.ChangeDisplayUserIDRequest) (*userv1.ChangeDisplayUserIDResponse, error) {
|
||||
ctx = contextWithApp(ctx, req.GetMeta())
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user