2026-06-22 16:55:22 +08:00

121 lines
4.2 KiB
Go

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