2026-05-02 13:02:38 +08:00

100 lines
4.0 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)
}