496 lines
18 KiB
Go
496 lines
18 KiB
Go
// 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 限制短号为 6 到 11 位非 0 开头数字。
|
||
var displayUserIDPattern = regexp.MustCompile(`^[1-9][0-9]{5,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 拼出的国家主数据 ID,0 表示历史值未命中国家表。
|
||
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 用户允许签发新 session,disabled/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)
|
||
}
|