2026-05-13 16:07:25 +08:00

496 lines
18 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 定义 user-service 的用户主数据、展示短号和临时靓号领域模型。
package user
import (
"regexp"
invitedomain "hyapp/services/user-service/internal/domain/invite"
)
// Status 是用户主状态,业务服务只应该依赖这里的稳定语义。
type Status string
const (
// StatusActive 表示用户可以正常登录和参与业务。
StatusActive Status = "active"
// StatusDisabled 表示用户被平台禁用。
StatusDisabled Status = "disabled"
// StatusBanned 表示用户被封禁。
StatusBanned Status = "banned"
)
// OnboardingStatus 表达用户是否完成 App 最小注册资料。
type OnboardingStatus string
const (
// OnboardingStatusProfileRequired 表示用户已创建但还不能进入房间、IM、RTC 或付费链路。
OnboardingStatusProfileRequired OnboardingStatus = "profile_required"
// OnboardingStatusCompleted 表示用户名、头像、国家和区域快照已经在注册页原子固化。
OnboardingStatusCompleted OnboardingStatus = "completed"
)
// DisplayUserIDStatus 表达短号历史记录状态。
type DisplayUserIDStatus string
const (
// DisplayUserIDStatusActive 表示当前可被普通业务解析的短号归属。
DisplayUserIDStatusActive DisplayUserIDStatus = "active"
// DisplayUserIDStatusHeld 表示默认短号仍归用户所有,但被临时靓号覆盖。
DisplayUserIDStatusHeld DisplayUserIDStatus = "held"
// DisplayUserIDStatusExpired 表示临时靓号租约已经到期。
DisplayUserIDStatusExpired DisplayUserIDStatus = "expired"
// DisplayUserIDStatusReleased 表示短号曾经属于用户,但已经释放。
DisplayUserIDStatusReleased DisplayUserIDStatus = "released"
// DisplayUserIDStatusReserved 表示短号被系统预留,不能被普通用户占用。
DisplayUserIDStatusReserved DisplayUserIDStatus = "reserved"
// DisplayUserIDStatusBlocked 表示短号被运营阻断,不能分配。
DisplayUserIDStatusBlocked DisplayUserIDStatus = "blocked"
)
// DisplayUserIDKind 表达当前展示号来源。
type DisplayUserIDKind string
const (
// DisplayUserIDKindDefault 表示当前展示号来自用户永久默认短号。
DisplayUserIDKindDefault DisplayUserIDKind = "default"
// DisplayUserIDKindPretty 表示当前展示号来自有效临时靓号租约。
DisplayUserIDKindPretty DisplayUserIDKind = "pretty"
)
// PrettyLeaseStatus 表达临时靓号租约生命周期。
type PrettyLeaseStatus string
const (
// PrettyLeaseStatusActive 表示租约当前覆盖用户展示号。
PrettyLeaseStatusActive PrettyLeaseStatus = "active"
// PrettyLeaseStatusExpired 表示租约已按到期流程恢复默认短号。
PrettyLeaseStatusExpired PrettyLeaseStatus = "expired"
// PrettyLeaseStatusCancelled 表示租约被业务主动取消。
PrettyLeaseStatusCancelled PrettyLeaseStatus = "cancelled"
// PrettyLeaseStatusRefunded 表示租约被退款终止。
PrettyLeaseStatusRefunded PrettyLeaseStatus = "refunded"
)
// displayUserIDPattern 限制短号为 5 到 11 位非 0 开头数字。
var displayUserIDPattern = regexp.MustCompile(`^[1-9][0-9]{4,10}$`)
// User 是 user-service 的主数据实体。
// 这里只保存注册时形成的用户资料和来源快照,不承载关系链、房间内临时态或连接态。
type User struct {
// AppCode 是用户所属 App所有短号、资料、Host 关系都在该 App 内隔离。
AppCode string
// UserID 是系统内部不可变用户 ID。
UserID int64
// DefaultDisplayUserID 是用户永久默认短号。
DefaultDisplayUserID string
// CurrentDisplayUserID 是当前对外展示短号,可能被有效靓号覆盖。
CurrentDisplayUserID string
// CurrentDisplayUserIDKind 表示当前展示号来源。
CurrentDisplayUserIDKind DisplayUserIDKind
// CurrentDisplayUserIDExpiresAtMs 是当前靓号过期时间,默认短号为 0。
CurrentDisplayUserIDExpiresAtMs int64
// Username 是注册入口传入的展示名快照,后续资料编辑可在专门用例中覆盖。
Username string
// Gender 是客户端上报的性别枚举原值v1 不在服务端推断性别含义。
Gender string
// Country 是客户端选择的国家码快照;值必须来自 countries.country_code。
Country string
// RegionID 是按 Country 当前 active 映射计算出的物化区域快照0 表示无区域。
RegionID int64
// CountryID 是按 Country 拼出的国家主数据 ID0 表示历史值未命中国家表。
CountryID int64
// CountryName 是按 Country 拼出的国家英文或拼音名称。
CountryName string
// CountryDisplayName 是按 Country 拼出的中文展示名。
CountryDisplayName string
// ISONumeric 是按 Country 拼出的三位 numeric/M49 字符串,客户端不能当数字解析。
ISONumeric string
// PhoneCountryCode 是按 Country 拼出的 E.164 国家呼叫码,可能为空或被多个国家共享。
PhoneCountryCode string
// CountryEnabled 表示当前国家是否仍对 App 注册和改国家开放,历史用户可能为 false。
CountryEnabled bool
// RegionCode 是按 RegionID 拼出的区域业务编码。
RegionCode string
// RegionName 是按 RegionID 拼出的区域展示名。
RegionName string
// InviteCode 是注册时使用的邀请码,空值表示未使用邀请关系。
InviteCode string
// InviteOverview 是我的页读取的邀请码和计数 read model不参与用户主状态决策。
InviteOverview invitedomain.Overview
// InviteBinding 是注册完成请求本次邀请归因结果,只服务入口响应,不作为关系事实来源。
InviteBinding invitedomain.Binding
// RegisterIP 是注册请求携带或 gateway 观察到的注册 IP 快照。
RegisterIP string
// RegisterUserAgent 是 gateway 从 HTTP header 观察到的注册 UA 快照。
RegisterUserAgent string
// CountryByIP 是服务端根据入口 IP 或可信边缘地域信息得到的国家快照。
CountryByIP string
// RegisterDeviceID 是注册时绑定 refresh session 的设备 ID。
RegisterDeviceID string
// RegisterDevice 是客户端上报的设备型号或设备描述。
RegisterDevice string
// RegisterOSVersion 是注册客户端系统版本。
RegisterOSVersion string
// Avatar 是注册入口传入的头像 HTTP(S) URL。
Avatar string
// BirthDate 使用 yyyy-mm-dd 字符串表达生日,避免时区把日期偏移成前后一天。
BirthDate string
// RegisterAppVersion 是注册客户端版本。
RegisterAppVersion string
// RegisterBuildNumber 是注册客户端构建号。
RegisterBuildNumber string
// RegisterSource 是注册来源,例如渠道、投放或活动入口。
RegisterSource string
// RegisterInstallChannel 是注册安装渠道,例如 play_store 或 app_store。
RegisterInstallChannel string
// RegisterCampaign 是注册投放活动标识。
RegisterCampaign string
// RegisterPlatform 是客户端平台,当前只接受 android 或 ios。
RegisterPlatform string
// Language 是客户端语言偏好。
Language string
// Timezone 是客户端 IANA 时区,例如 America/Los_Angeles。
Timezone string
// ProfileCompleted 表示用户是否已经完成 App 最小注册资料。
ProfileCompleted bool
// ProfileCompletedAtMs 是资料完成时间0 表示从未完成。
ProfileCompletedAtMs int64
// OnboardingStatus 是客户端准入状态,首版只使用 profile_required/completed。
OnboardingStatus OnboardingStatus
// Status 控制登录和业务可用性。
Status Status
// CreatedAtMs 是用户创建时间。
CreatedAtMs int64
// UpdatedAtMs 是用户主记录更新时间。
UpdatedAtMs int64
}
// ProfileStats 是我的页顶部统计 read model只保存预聚合计数。
type ProfileStats struct {
// AppCode 是统计所属 App关系链和礼物统计都必须在 App 内隔离。
AppCode string
// UserID 是统计所属用户。
UserID int64
// VisitorsCount 是访问过该用户主页的用户计数投影。
VisitorsCount int64
// FollowingCount 是该用户关注的人数计数投影。
FollowingCount int64
// FriendsCount 是该用户好友人数计数投影。
FriendsCount int64
// UpdatedAtMs 是这份计数投影最后更新时间。
UpdatedAtMs int64
}
const (
// SocialStatusActive 表示关注或好友关系当前有效。
SocialStatusActive = "active"
// SocialStatusCancelled 表示关注关系已取消。
SocialStatusCancelled = "cancelled"
// SocialStatusDeleted 表示好友关系已删除。
SocialStatusDeleted = "deleted"
// FriendApplicationStatusPending 表示好友申请等待对方处理。
FriendApplicationStatusPending = "pending"
// FriendApplicationStatusAccepted 表示好友申请已同意。
FriendApplicationStatusAccepted = "accepted"
)
// ProfileVisitRecord 是访问某个用户主页的去重记录。
type ProfileVisitRecord struct {
VisitorUserID int64
TargetUserID int64
VisitCount int64
LastVisitedAtMs int64
}
// FollowRecord 是单向关注关系。
type FollowRecord struct {
FollowerUserID int64
FolloweeUserID int64
FollowedAtMs int64
}
// FriendRecord 是双向好友关系在单用户视角下的一条投影。
type FriendRecord struct {
UserID int64
FriendUserID int64
FriendedAtMs int64
}
// FriendApplication 是好友申请事实。
type FriendApplication struct {
RequesterUserID int64
TargetUserID int64
Status string
CreatedAtMs int64
UpdatedAtMs int64
}
// BusinessUserLookupItem 是带业务场景权限裁剪后的用户查询结果。
// 它只包含选择目标用户所需字段,不能扩展手机号、登录身份或钱包资产。
type BusinessUserLookupItem struct {
UserID int64
DisplayUserID string
Username string
Avatar string
Status Status
RegionID int64
RegionCode string
RegionName string
}
// Identity 是 user_id 与当前 active display_user_id 的绑定投影。
type Identity struct {
// AppCode 是短号所属 App。
AppCode string
// UserID 是短号归属用户。
UserID int64
// DisplayUserID 是当前 active 短号。
DisplayUserID string
// DefaultDisplayUserID 是永久默认短号。
DefaultDisplayUserID string
// DisplayUserIDKind 表示 DisplayUserID 来源。
DisplayUserIDKind DisplayUserIDKind
// DisplayUserIDExpiresAtMs 是 active 靓号过期时间。
DisplayUserIDExpiresAtMs int64
// Status 是短号记录状态。
Status DisplayUserIDStatus
}
// DisplayUserIDChangeCommand 描述短号变更的事务输入。
type DisplayUserIDChangeCommand struct {
// AppCode 是本次短号变更所属 App。
AppCode string
// UserID 是要修改默认短号的用户。
UserID int64
// NewDisplayUserID 是新的默认短号。
NewDisplayUserID string
// Reason 是审计原因。
Reason string
// OperatorUserID 是操作者,用户自助修改时等于 UserID。
OperatorUserID int64
// RequestID 是入口请求 ID写入变更日志。
RequestID string
// ChangedAtMs 是本次变更时间。
ChangedAtMs int64
// CooldownMs 是默认短号修改冷却期。
CooldownMs int64
}
// ProfileUpdateCommand 描述用户基础资料自助修改。
type ProfileUpdateCommand struct {
// AppCode 是资料修改所属 App。
AppCode string
// UserID 是要修改资料的用户。
UserID int64
// Username 非 nil 时覆盖用户名nil 表示本次不修改。
Username *string
// Avatar 非 nil 时覆盖头像地址nil 表示本次不修改。
Avatar *string
// Gender 非 nil 时覆盖性别枚举原值nil 表示本次不修改。
Gender *string
// BirthDate 非 nil 时覆盖生日 yyyy-mm-dd空字符串表示清空生日。
BirthDate *string
// UpdatedAtMs 是本次修改时间。
UpdatedAtMs int64
}
// CompleteOnboardingCommand 描述注册页一次性补齐资料的事务输入。
type CompleteOnboardingCommand struct {
// AppCode 是注册资料固化所属 App。
AppCode string
// UserID 是已三方登录创建出的系统用户。
UserID int64
// Username 是注册页最终昵称,必填且已校验长度。
Username string
// Avatar 是注册页最终头像 URL必填且已校验为 HTTP(S) 绝对地址。
Avatar string
// Gender 是注册页最终性别枚举原值,必填且已校验长度。
Gender string
// Country 是 active countries.country_code 中的规范国家码。
Country string
// RegionID 是按 Country 当前 active 映射解析出的区域快照0 表示 GLOBAL 兜底桶。
RegionID int64
// InviteCode 是注册页可选的邀请码;非空时 repository 必须解析并绑定关系。
InviteCode string
// RequestID 是入口请求 ID写入邀请关系和 outbox 便于排查。
RequestID string
// CompletedAtMs 是资料完成时间,同时作为 updated_at_ms。
CompletedAtMs int64
}
// CountryChangeCommand 描述用户国家修改事务。
type CountryChangeCommand struct {
// AppCode 是国家修改所属 App。
AppCode string
// UserID 是要修改国家的用户。
UserID int64
// NewCountry 是新的国家或地区。
NewCountry string
// NewRegionID 是按 NewCountry 解析出的区域快照0 表示 GLOBAL 兜底桶。
NewRegionID int64
// RequestID 是入口请求 ID写入国家修改日志。
RequestID string
// ChangedAtMs 是本次修改时间。
ChangedAtMs int64
// CooldownMs 是国家修改冷却窗口,当前按滚动 30 天计算。
CooldownMs int64
}
// CountryChangeLog 是用户国家修改审计记录。
type CountryChangeLog struct {
// AppCode 是审计记录所属 App。
AppCode string
// UserID 是修改国家的用户。
UserID int64
// OldCountry 是修改前国家,空值表示历史未设置。
OldCountry string
// NewCountry 是修改后国家。
NewCountry string
// RequestID 是入口请求 ID。
RequestID string
// ChangedAtMs 是修改时间。
ChangedAtMs int64
}
// PushTokenStatus 表达用户设备推送 token 是否仍可用于离线触达。
type PushTokenStatus string
const (
// PushTokenStatusActive 表示该 token 当前可用于系统/活动消息离线推送。
PushTokenStatusActive PushTokenStatus = "active"
// PushTokenStatusInactive 表示该 token 已被用户登出、关闭通知或重新绑定替换。
PushTokenStatusInactive PushTokenStatus = "inactive"
)
// PushTokenCommand 描述一次设备 push token 绑定。
type PushTokenCommand struct {
// AppCode 是 token 所属 App避免不同包之间串推送。
AppCode string
// UserID 是当前登录用户,必须由 gateway 鉴权后传入。
UserID int64
// DeviceID 是 App 安装级稳定 ID。
DeviceID string
// PushToken 是系统推送服务返回的 token。
PushToken string
// Provider 是推送提供方,例如 fcm 或 apns。
Provider string
// Platform 是客户端平台,当前只接受 android/ios。
Platform string
// AppVersion 是上报 token 时的 App 版本快照。
AppVersion string
// Language 是用户当前客户端语言偏好。
Language string
// Timezone 是用户当前 IANA 时区。
Timezone string
// RequestID 是入口请求 ID便于排查重复绑定。
RequestID string
// UpdatedAtMs 是绑定事务时间。
UpdatedAtMs int64
}
// DeletePushTokenCommand 描述一次设备 push token 失效。
type DeletePushTokenCommand struct {
AppCode string
UserID int64
DeviceID string
PushToken string
RequestID string
UpdatedAtMs int64
}
// PrettyDisplayUserIDLease 是临时靓号覆盖关系的事实记录。
type PrettyDisplayUserIDLease struct {
// AppCode 是租约所属 App。
AppCode string
// LeaseID 是临时靓号租约主键。
LeaseID string
// DisplayUserID 是租约覆盖的靓号。
DisplayUserID string
// UserID 是租约所属用户。
UserID int64
// Status 是租约生命周期状态。
Status PrettyLeaseStatus
// StartsAtMs 是租约开始时间。
StartsAtMs int64
// ExpiresAtMs 是租约过期时间。
ExpiresAtMs int64
// PaymentReceiptID 是外部支付回执。
PaymentReceiptID string
// Source 标识租约来源,例如 payment。
Source string
// CreatedAtMs 是租约创建时间。
CreatedAtMs int64
// UpdatedAtMs 是租约更新时间。
UpdatedAtMs int64
}
// PrettyDisplayUserIDCommand 描述申请临时靓号的事务输入。
type PrettyDisplayUserIDCommand struct {
// AppCode 是靓号申请所属 App。
AppCode string
// LeaseID 是本次申请生成的租约 ID。
LeaseID string
// UserID 是申请用户。
UserID int64
// PrettyDisplayID 是申请覆盖的靓号。
PrettyDisplayID string
// LeaseDurationMs 是租期长度。
LeaseDurationMs int64
// PaymentReceiptID 是已完成支付的账务回执。
PaymentReceiptID string
// Source 是申请来源。
Source string
// RequestID 是入口请求 ID。
RequestID string
// OperatorUserID 是操作者。
OperatorUserID int64
// NowMs 是事务时间。
NowMs int64
}
// ExpirePrettyDisplayUserIDCommand 描述靓号过期恢复的事务输入。
type ExpirePrettyDisplayUserIDCommand struct {
// AppCode 是过期恢复所属 App。
AppCode string
// UserID 是需要恢复默认短号的用户。
UserID int64
// LeaseID 可选;传入时必须匹配当前 active lease。
LeaseID string
// RequestID 是触发过期的请求或任务 ID。
RequestID string
// NowMs 是过期事务时间。
NowMs int64
}
// CanLogin 判断用户状态是否允许签发新会话。
func (u User) CanLogin() bool {
// 只有 active 用户允许签发新 sessiondisabled/banned 都必须阻断登录。
return u.Status == StatusActive
}
// DisplayUserIDExpired 判断当前展示号快照是否已经因为靓号到期而失效。
func (u User) DisplayUserIDExpired(nowMs int64) bool {
// 只有 pretty 展示号有过期语义,默认短号永不过期。
return u.CurrentDisplayUserIDKind == DisplayUserIDKindPretty && u.CurrentDisplayUserIDExpiresAtMs > 0 && u.CurrentDisplayUserIDExpiresAtMs <= nowMs
}
// EffectiveIdentity 把用户当前展示号快照转换成对外身份投影。
func (u User) EffectiveIdentity() Identity {
// Identity 是对外短号投影,不暴露 users 表内部状态之外的资料字段。
return Identity{
AppCode: u.AppCode,
UserID: u.UserID,
DisplayUserID: u.CurrentDisplayUserID,
DefaultDisplayUserID: u.DefaultDisplayUserID,
DisplayUserIDKind: u.CurrentDisplayUserIDKind,
DisplayUserIDExpiresAtMs: u.CurrentDisplayUserIDExpiresAtMs,
Status: DisplayUserIDStatusActive,
}
}
// ValidDisplayUserID 校验短号格式。
func ValidDisplayUserID(value string) bool {
// 格式校验只判断形态,唯一性和可用性由 repository 事务决定。
return displayUserIDPattern.MatchString(value)
}