100 lines
4.0 KiB
Go
100 lines
4.0 KiB
Go
package user
|
||
|
||
import (
|
||
"context"
|
||
|
||
"hyapp/pkg/appcode"
|
||
"hyapp/pkg/idgen"
|
||
"hyapp/pkg/xerr"
|
||
userdomain "hyapp/services/user-service/internal/domain/user"
|
||
)
|
||
|
||
// ApplyPrettyDisplayUserID 申请临时靓号并让它在租期内覆盖当前展示号。
|
||
func (s *Service) ApplyPrettyDisplayUserID(ctx context.Context, userID int64, prettyDisplayUserID string, leaseDurationMs int64, paymentReceiptID string, requestID string) (userdomain.Identity, string, error) {
|
||
if userID <= 0 {
|
||
// 靓号租约必须绑定明确用户。
|
||
return userdomain.Identity{}, "", xerr.New(xerr.InvalidArgument, "user_id is required")
|
||
}
|
||
if !userdomain.ValidDisplayUserID(prettyDisplayUserID) {
|
||
// 靓号也复用 display_user_id 格式规则。
|
||
return userdomain.Identity{}, "", xerr.New(xerr.DisplayUserIDInvalid, "display_user_id format is invalid")
|
||
}
|
||
if leaseDurationMs <= 0 {
|
||
// 没有正租期无法计算过期恢复时间。
|
||
return userdomain.Identity{}, "", xerr.New(xerr.InvalidArgument, "lease_duration_ms is required")
|
||
}
|
||
if paymentReceiptID == "" {
|
||
// v1 要求先完成支付再申请靓号,避免 user-service 自己处理账务。
|
||
return userdomain.Identity{}, "", xerr.New(xerr.DisplayUserIDPaymentRequired, "payment receipt is required")
|
||
}
|
||
if s.identityRepository == nil {
|
||
// 靓号涉及多表事务,必须由 identity repository 完成。
|
||
return userdomain.Identity{}, "", xerr.New(xerr.Unavailable, "identity repository is not configured")
|
||
}
|
||
|
||
// LeaseID 在服务层生成,repository 只负责原子保存和冲突判断。
|
||
nowMs := s.now().UnixMilli()
|
||
return s.identityRepository.ApplyPrettyDisplayUserID(ctx, userdomain.PrettyDisplayUserIDCommand{
|
||
AppCode: appcode.FromContext(ctx),
|
||
LeaseID: idgen.New("pdu_lease"),
|
||
UserID: userID,
|
||
PrettyDisplayID: prettyDisplayUserID,
|
||
LeaseDurationMs: leaseDurationMs,
|
||
PaymentReceiptID: paymentReceiptID,
|
||
Source: "payment",
|
||
RequestID: requestID,
|
||
OperatorUserID: userID,
|
||
NowMs: nowMs,
|
||
})
|
||
}
|
||
|
||
// ExpirePrettyDisplayUserID 主动触发靓号过期恢复;定时任务和懒过期应复用同一事务。
|
||
func (s *Service) ExpirePrettyDisplayUserID(ctx context.Context, userID int64, leaseID string, requestID string) (userdomain.Identity, error) {
|
||
if userID <= 0 {
|
||
// 过期恢复需要明确用户,lease_id 只是可选校验条件。
|
||
return userdomain.Identity{}, xerr.New(xerr.InvalidArgument, "user_id is required")
|
||
}
|
||
if s.identityRepository == nil {
|
||
// 没有 identity repository 无法恢复默认短号。
|
||
return userdomain.Identity{}, xerr.New(xerr.Unavailable, "identity repository is not configured")
|
||
}
|
||
|
||
return s.identityRepository.ExpirePrettyDisplayUserID(ctx, userdomain.ExpirePrettyDisplayUserIDCommand{
|
||
AppCode: appcode.FromContext(ctx),
|
||
UserID: userID,
|
||
LeaseID: leaseID,
|
||
RequestID: requestID,
|
||
NowMs: s.now().UnixMilli(),
|
||
})
|
||
}
|
||
|
||
func (s *Service) refreshExpiredUser(ctx context.Context, user userdomain.User, requestID string) (userdomain.User, error) {
|
||
nowMs := s.now().UnixMilli()
|
||
if !user.DisplayUserIDExpired(nowMs) {
|
||
// 当前展示号仍有效时直接返回用户快照。
|
||
return user, nil
|
||
}
|
||
|
||
if s.identityRepository == nil {
|
||
// 发现过期但无法恢复时不能继续返回过期靓号。
|
||
return userdomain.User{}, xerr.New(xerr.Unavailable, "identity repository is not configured")
|
||
}
|
||
|
||
// 过期恢复必须走 identity repository 事务,同步更新 users 和短号记录。
|
||
if _, err := s.identityRepository.ExpirePrettyDisplayUserID(ctx, userdomain.ExpirePrettyDisplayUserIDCommand{
|
||
AppCode: appcode.FromContext(ctx),
|
||
UserID: user.UserID,
|
||
RequestID: requestID,
|
||
NowMs: nowMs,
|
||
}); err != nil {
|
||
return userdomain.User{}, err
|
||
}
|
||
|
||
if s.userRepository == nil {
|
||
// 恢复后需要重新读取 users 快照,确保返回的展示号来自持久化结果。
|
||
return userdomain.User{}, xerr.New(xerr.Unavailable, "user repository is not configured")
|
||
}
|
||
|
||
return s.userRepository.GetUser(ctx, user.UserID)
|
||
}
|