// Package invite defines user-service invitation facts and read models. package invite import ( "regexp" "strings" ) const ( // CodeStatusActive means a code can be used by new registrations. CodeStatusActive = "active" // CodeTypePrimary is the shareable per-user permanent invite code. CodeTypePrimary = "primary" // ProgressStatusPending means the invited user has not reached the configured recharge threshold. ProgressStatusPending = "pending" // ProgressStatusValid means the invited user has reached the threshold and counted once. ProgressStatusValid = "valid" // EventTypeUserInvited is emitted when an invitation relation is first bound. EventTypeUserInvited = "UserInvited" // EventTypeUserInviteBecameValid is emitted when recharge progress first crosses the policy threshold. EventTypeUserInviteBecameValid = "UserInviteBecameValid" // RechargeEventWalletRecorded is the wallet outbox event that invitation validity consumes. RechargeEventWalletRecorded = "WalletRechargeRecorded" // RechargeTypeCoinSellerTransfer is the current recharge fact produced by wallet-service coin seller transfer. RechargeTypeCoinSellerTransfer = "coin_seller_transfer" // RechargeTypeGooglePlay is the recharge fact produced by Google Play Billing. RechargeTypeGooglePlay = "google_play" // RechargeTypeGoogle is the legacy channel value carried by some Google wallet payloads. RechargeTypeGoogle = "google" // RechargeTypeMifaPay is the third-party checkout provider value for MiFaPay. RechargeTypeMifaPay = "mifapay" // RechargeTypeV5Pay is the third-party checkout provider value for V5Pay. RechargeTypeV5Pay = "v5pay" // RechargeTypeUSDTTRC20 is the third-party checkout provider value for USDT TRC20. RechargeTypeUSDTTRC20 = "usdt_trc20" ) const ( // CodeMinLength keeps user-entered invite codes short enough for mobile manual entry. CodeMinLength = 6 // CodeMaxLength follows the current SQL VARCHAR boundary for invite code snapshots. CodeMaxLength = 64 ) var codePattern = regexp.MustCompile(`^[A-Z0-9]{6,64}$`) // Overview is the read model returned with user profile for my-page aggregation. type Overview struct { MyInviteCode string InviteEnabled bool InviteCount int64 ValidInviteCount int64 ValidInviteThresholdCoin int64 } // Binding is the result of one registration/onboarding invitation bind attempt. type Binding struct { Bound bool InviteCode string InviterUserID int64 } // Attribution 是被邀请用户到邀请人的归因快照;活动服务只依赖这个只读事实,不直接读取邀请表。 type Attribution struct { Found bool AppCode string InvitedUserID int64 InviterUserID int64 InviteCode string BoundAtMS int64 ValidityStatus string AccumulatedRechargeCoinAmount int64 RequiredRechargeCoinAmount int64 ValidAtMS int64 } // BindCommand describes one optional invitation attribution write. type BindCommand struct { AppCode string InvitedUserID int64 InvitedRegionID int64 InviteCode string Source string RequestID string BoundAtMs int64 } // RechargeEvent is the wallet recharge fact consumed by invitation validity. type RechargeEvent struct { AppCode string EventID string EventType string TransactionID string CommandID string InvitedUserID int64 AssetType string RechargeCoinAmount int64 RechargeType string OccurredAtMs int64 PayloadJSON string } // RechargeApplyResult summarizes idempotent invitation recharge processing. type RechargeApplyResult struct { Consumed bool Skipped bool Valid bool Reason string } // NormalizeCode trims and uppercases user-provided invite code text. func NormalizeCode(code string) string { return strings.ToUpper(strings.TrimSpace(code)) } // ValidCode checks only syntax; ownership and active status are repository facts. func ValidCode(code string) bool { code = NormalizeCode(code) return len(code) >= CodeMinLength && len(code) <= CodeMaxLength && codePattern.MatchString(code) }