// 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 拼出的国家主数据 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) }