package model

const (
	UserStatusActive   = "active"
	UserStatusLocked   = "locked"
	UserStatusDisabled = "disabled"

	UserAppScopeModeAll      = "all"
	UserAppScopeModeSelected = "selected"
	UserAppScopeModeNone     = "none"

	TeamProductResearchID uint = 1
	TeamOperationsID      uint = 2

	JobStatusPending   = "pending"
	JobStatusRunning   = "running"
	JobStatusSucceeded = "succeeded"
	JobStatusFailed    = "failed"
	JobStatusCanceled  = "canceled"

	CoinSellerRechargeOrderStatusCreated      = "created"
	CoinSellerRechargeOrderStatusVerifyFailed = "verify_failed"
	CoinSellerRechargeOrderStatusVerified     = "verified"
	CoinSellerRechargeOrderStatusGranting     = "granting"
	CoinSellerRechargeOrderStatusGrantFailed  = "grant_failed"
	CoinSellerRechargeOrderStatusGranted      = "granted"

	CoinSellerRechargeVerifyStatusPending  = "pending"
	CoinSellerRechargeVerifyStatusVerified = "verified"
	CoinSellerRechargeVerifyStatusFailed   = "failed"

	CoinSellerRechargeGrantStatusPending = "pending"
	CoinSellerRechargeGrantStatusRunning = "processing"
	CoinSellerRechargeGrantStatusGranted = "granted"
	CoinSellerRechargeGrantStatusFailed  = "failed"

	WithdrawalApplicationStatusPending  = "pending"
	WithdrawalApplicationStatusApproved = "approved"
	WithdrawalApplicationStatusRejected = "rejected"

	WithdrawalOperationsStatusSkipped   = "skipped"
	WithdrawalOperationsStatusPending   = "pending"
	WithdrawalOperationsStatusRejecting = "rejecting"
	WithdrawalOperationsStatusApproved  = "approved"
	WithdrawalOperationsStatusRejected  = "rejected"

	WithdrawalExternalFinanceClaimStatusClaimed   = "claimed"
	WithdrawalExternalFinanceClaimStatusFinalized = "finalized"

	ExternalAdminStatusActive   = "active"
	ExternalAdminStatusDisabled = "disabled"

	// External-admin identities live only in the isolated portal hierarchy. They
	// must never be inferred from or written into App manager/BD-leader profiles.
	ExternalAdminIdentityLocal              = "local"
	ExternalAdminIdentityCountryManager     = "country_manager"
	ExternalAdminIdentityExternalSuperAdmin = "external_super_admin"
)

// ExternalAdminAccount is intentionally not related to admin_users. External operators
// use an isolated credential lifecycle and can never exchange this row for a main-admin JWT.
type ExternalAdminAccount struct {
	ID                     uint64  `gorm:"primaryKey" json:"id"`
	AppCode                string  `gorm:"size:32;uniqueIndex:uk_external_admin_accounts_app_linked_user;index:idx_external_admin_accounts_app_status_updated,priority:1;index:idx_external_admin_accounts_hierarchy,priority:1;not null" json:"appCode"`
	LinkedAppUserID        int64   `gorm:"column:linked_app_user_id;uniqueIndex:uk_external_admin_accounts_app_linked_user;not null" json:"-"`
	Username               string  `gorm:"size:64;uniqueIndex:uk_external_admin_accounts_username;not null" json:"username"`
	PasswordHash           string  `gorm:"size:255;not null" json:"-"`
	PermissionsJSON        string  `gorm:"column:permissions_json;type:json;not null" json:"-"`
	PermissionRevision     uint64  `gorm:"column:permission_revision;not null;default:1" json:"permissionRevision"`
	IdentityType           string  `gorm:"column:identity_type;size:32;index:idx_external_admin_accounts_hierarchy,priority:3;not null;default:country_manager" json:"identityType"`
	ParentAccountID        *uint64 `gorm:"column:parent_account_id;index:idx_external_admin_accounts_hierarchy,priority:2" json:"parentAccountId,omitempty"`
	Status                 string  `gorm:"size:24;index:idx_external_admin_accounts_app_status_updated,priority:2;index:idx_external_admin_accounts_hierarchy,priority:4;not null;default:active" json:"status"`
	PasswordChangeRequired bool    `gorm:"not null;default:true" json:"passwordChangeRequired"`
	FailedLoginCount       uint    `gorm:"not null;default:0" json:"-"`
	LockedUntilMS          int64   `gorm:"column:locked_until_ms;not null;default:0" json:"-"`
	LastLoginAtMS          *int64  `gorm:"column:last_login_at_ms" json:"lastLoginAtMs"`
	CreatedByAdminID       uint    `gorm:"column:created_by_admin_id;index;not null" json:"createdByAdminId"`
	CreatedAtMS            int64   `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
	UpdatedAtMS            int64   `gorm:"column:updated_at_ms;autoUpdateTime:milli;index:idx_external_admin_accounts_app_status_updated,priority:3" json:"updatedAtMs"`
}

func (ExternalAdminAccount) TableName() string { return "external_admin_accounts" }

// ExternalAdminSession stores only hashes of the session and CSRF secrets. The browser
// receives the opaque values, so a read-only database leak cannot be replayed as a session.
type ExternalAdminSession struct {
	ID            uint64               `gorm:"primaryKey"`
	AccountID     uint64               `gorm:"column:account_id;index:idx_external_admin_sessions_account_active,priority:1;not null"`
	Account       ExternalAdminAccount `gorm:"foreignKey:AccountID;references:ID"`
	AppCode       string               `gorm:"size:32;index:idx_external_admin_sessions_app_expiry,priority:1;not null"`
	TokenHash     string               `gorm:"size:64;uniqueIndex:uk_external_admin_sessions_token_hash;not null"`
	CSRFTokenHash string               `gorm:"column:csrf_token_hash;size:64;not null"`
	IP            string               `gorm:"size:64;not null;default:''"`
	UserAgent     string               `gorm:"size:512;not null;default:''"`
	ExpiresAtMS   int64                `gorm:"column:expires_at_ms;index:idx_external_admin_sessions_account_active,priority:3;index:idx_external_admin_sessions_app_expiry,priority:2;not null"`
	LastSeenAtMS  int64                `gorm:"column:last_seen_at_ms;not null"`
	RevokedAtMS   int64                `gorm:"column:revoked_at_ms;index:idx_external_admin_sessions_account_active,priority:2;not null;default:0"`
	RevokeReason  string               `gorm:"size:64;not null;default:''"`
	CreatedAtMS   int64                `gorm:"column:created_at_ms;autoCreateTime:milli"`
}

func (ExternalAdminSession) TableName() string { return "external_admin_sessions" }

type ExternalAdminLoginLog struct {
	ID          uint64  `gorm:"primaryKey"`
	AccountID   *uint64 `gorm:"column:account_id;index:idx_external_admin_login_logs_account_time,priority:1"`
	AppCode     string  `gorm:"size:32;index:idx_external_admin_login_logs_app_username_time,priority:1;not null"`
	Username    string  `gorm:"size:64;index:idx_external_admin_login_logs_app_username_time,priority:2;not null"`
	IP          string  `gorm:"size:64;not null;default:''"`
	UserAgent   string  `gorm:"size:512;not null;default:''"`
	Status      string  `gorm:"size:24;index:idx_external_admin_login_logs_status_time,priority:1;not null"`
	Message     string  `gorm:"size:128;not null;default:''"`
	CreatedAtMS int64   `gorm:"column:created_at_ms;autoCreateTime:milli;index:idx_external_admin_login_logs_app_username_time,priority:3;index:idx_external_admin_login_logs_account_time,priority:2;index:idx_external_admin_login_logs_status_time,priority:2"`
}

func (ExternalAdminLoginLog) TableName() string { return "external_admin_login_logs" }

type ExternalAdminOperationLog struct {
	ID          uint64 `gorm:"primaryKey"`
	AccountID   uint64 `gorm:"column:account_id;index:idx_external_admin_operation_logs_account_time,priority:1;not null"`
	AppCode     string `gorm:"size:32;index:idx_external_admin_operation_logs_app_time,priority:1;not null"`
	Username    string `gorm:"size:64;not null"`
	RequestID   string `gorm:"size:64;index:idx_external_admin_operation_logs_request;not null;default:''"`
	Action      string `gorm:"size:160;not null"`
	Resource    string `gorm:"size:120;not null;default:''"`
	ResourceID  string `gorm:"size:128;not null;default:''"`
	Method      string `gorm:"size:12;not null"`
	Path        string `gorm:"size:255;not null"`
	IP          string `gorm:"size:64;not null;default:''"`
	UserAgent   string `gorm:"size:512;not null;default:''"`
	Status      string `gorm:"size:24;not null"`
	HTTPStatus  int    `gorm:"not null"`
	LatencyMS   int64  `gorm:"not null;default:0"`
	CreatedAtMS int64  `gorm:"column:created_at_ms;autoCreateTime:milli;index:idx_external_admin_operation_logs_account_time,priority:2;index:idx_external_admin_operation_logs_app_time,priority:2"`
}

func (ExternalAdminOperationLog) TableName() string { return "external_admin_operation_logs" }

type User struct {
	ID            uint   `gorm:"primaryKey" json:"id"`
	Username      string `gorm:"size:64;uniqueIndex;not null" json:"account"`
	Name          string `gorm:"size:80;not null" json:"name"`
	PasswordHash  string `gorm:"size:255;not null" json:"-"`
	Team          string `gorm:"size:80" json:"team"`
	TeamID        *uint  `gorm:"column:team_id;index" json:"teamId"`
	TeamRecord    Team   `gorm:"foreignKey:TeamID;references:ID" json:"-"`
	Status        string `gorm:"size:24;index;not null;default:active" json:"status"`
	AppScopeMode  string `gorm:"size:16;not null;default:none" json:"appScopeMode"`
	MFAEnabled    bool   `gorm:"not null;default:false" json:"mfaEnabled"`
	LastLoginAtMS *int64 `gorm:"column:last_login_at_ms" json:"lastLoginAtMs"`
	Roles         []Role `gorm:"many2many:admin_user_roles;" json:"roles"`
	CreatedAtMS   int64  `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
	UpdatedAtMS   int64  `gorm:"column:updated_at_ms;autoUpdateTime:milli" json:"updatedAtMs"`
}

func (User) TableName() string {
	return "admin_users"
}

type Team struct {
	ID          uint   `gorm:"primaryKey" json:"id"`
	ParentID    *uint  `gorm:"column:parent_id;index" json:"parentId"`
	Parent      *Team  `gorm:"foreignKey:ParentID;references:ID" json:"-"`
	Name        string `gorm:"size:80;uniqueIndex;not null" json:"name"`
	Description string `gorm:"size:255" json:"description"`
	Sort        int    `gorm:"not null;default:0" json:"sort"`
	ParentName  string `gorm:"column:parent_name;->;-:migration" json:"parentName,omitempty"`
	UserCount   int64  `gorm:"column:user_count;->;-:migration" json:"userCount"`
	CreatedAtMS int64  `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
	UpdatedAtMS int64  `gorm:"column:updated_at_ms;autoUpdateTime:milli" json:"updatedAtMs"`
}

func (Team) TableName() string {
	return "admin_teams"
}

type Role struct {
	ID          uint         `gorm:"primaryKey" json:"id"`
	Name        string       `gorm:"size:80;uniqueIndex;not null" json:"name"`
	Code        string       `gorm:"size:80;uniqueIndex;not null" json:"code"`
	Description string       `gorm:"size:255" json:"description"`
	Users       []User       `gorm:"many2many:admin_user_roles;" json:"-"`
	Permissions []Permission `gorm:"many2many:admin_role_permissions;" json:"permissions"`
	CreatedAtMS int64        `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
	UpdatedAtMS int64        `gorm:"column:updated_at_ms;autoUpdateTime:milli" json:"updatedAtMs"`
}

func (Role) TableName() string {
	return "admin_roles"
}

type Permission struct {
	ID          uint   `gorm:"primaryKey" json:"id"`
	Name        string `gorm:"size:100;not null" json:"name"`
	Code        string `gorm:"size:100;uniqueIndex;not null" json:"code"`
	Kind        string `gorm:"size:32;index;not null" json:"kind"`
	Description string `gorm:"size:255" json:"description"`
	CreatedAtMS int64  `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
	UpdatedAtMS int64  `gorm:"column:updated_at_ms;autoUpdateTime:milli" json:"updatedAtMs"`
}

func (Permission) TableName() string {
	return "admin_permissions"
}

type Menu struct {
	ID             uint   `gorm:"primaryKey" json:"id"`
	ParentID       *uint  `gorm:"index" json:"parentId"`
	Title          string `gorm:"size:80;not null" json:"label"`
	Code           string `gorm:"size:100;uniqueIndex;not null" json:"code"`
	Path           string `gorm:"size:160" json:"path"`
	Icon           string `gorm:"size:64" json:"icon"`
	PermissionCode string `gorm:"size:100;index" json:"permissionCode"`
	Sort           int    `gorm:"not null;default:0" json:"sort"`
	Visible        bool   `gorm:"not null;default:true" json:"visible"`
	CreatedAtMS    int64  `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
	UpdatedAtMS    int64  `gorm:"column:updated_at_ms;autoUpdateTime:milli" json:"updatedAtMs"`
}

func (Menu) TableName() string {
	return "admin_menus"
}

type AppConfig struct {
	ID          uint   `gorm:"primaryKey" json:"id"`
	AppCode     string `gorm:"size:32;uniqueIndex:uk_admin_app_config_app_group_key;index:idx_admin_app_configs_app_group;not null" json:"appCode"`
	Group       string `gorm:"size:64;uniqueIndex:uk_admin_app_config_app_group_key;index:idx_admin_app_configs_app_group;not null" json:"group"`
	Key         string `gorm:"size:100;uniqueIndex:uk_admin_app_config_app_group_key;not null" json:"key"`
	Value       string `gorm:"type:text" json:"value"`
	Description string `gorm:"size:255" json:"description"`
	IsDeleted   bool   `gorm:"column:is_deleted;not null;default:false" json:"-"`
	CreatedAtMS int64  `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
	UpdatedAtMS int64  `gorm:"column:updated_at_ms;autoUpdateTime:milli" json:"updatedAtMs"`
}

func (AppConfig) TableName() string {
	return "admin_app_configs"
}

type AppBanner struct {
	ID                   uint   `gorm:"primaryKey;index:idx_admin_app_banners_region_status_sort,priority:5" json:"id"`
	AppCode              string `gorm:"size:32;index:idx_admin_app_banners_app_sort;index:idx_admin_app_banner_activity_target,priority:1;index:idx_admin_app_banners_region_status_sort,priority:1;not null;default:lalu" json:"appCode"`
	CoverURL             string `gorm:"size:1024;not null" json:"coverUrl"`
	RoomSmallImageURL    string `gorm:"column:room_small_image_url;size:1024;not null;default:'';comment:房间内小屏图 URL" json:"roomSmallImageUrl"`
	DiscoverImageURL     string `gorm:"column:discover_image_url;size:1024;not null;default:'';comment:发现页横幅图 URL" json:"discoverImageUrl"`
	BannerType           string `gorm:"size:16;not null" json:"bannerType"`
	TargetType           string `gorm:"column:target_type;size:32;not null;default:'';index:idx_admin_app_banner_activity_target,priority:2" json:"targetType"`
	ActivityTemplateID   string `gorm:"column:activity_template_id;size:64;not null;default:'';index:idx_admin_app_banner_activity_target,priority:3" json:"activityTemplateId"`
	ActivityTemplateCode string `gorm:"column:activity_template_code;size:64;not null;default:''" json:"activityTemplateCode"`
	DisplayScope         string `gorm:"size:128;index:idx_admin_app_banners_display,not null;default:home;comment:显示范围，多选逗号分隔：首页、房间内、发现页、充值页或我的页" json:"displayScope"`
	Param                string `gorm:"size:2048" json:"param"`
	Status               string `gorm:"size:24;index:idx_admin_app_banners_app_sort;index:idx_admin_app_banner_activity_target,priority:4;index:idx_admin_app_banners_region_status_sort,priority:3;not null;default:active" json:"status"`
	Platform             string `gorm:"size:24;index:idx_admin_app_banners_scope,not null" json:"platform"`
	SortOrder            int    `gorm:"index:idx_admin_app_banners_app_sort;index:idx_admin_app_banners_region_status_sort,priority:4;not null;default:0" json:"sortOrder"`
	RegionID             int64  `gorm:"index:idx_admin_app_banners_scope;index:idx_admin_app_banners_region_status_sort,priority:2;not null;default:0" json:"regionId"`
	CountryCode          string `gorm:"size:8;index:idx_admin_app_banners_scope" json:"countryCode"`
	Description          string `gorm:"size:255" json:"description"`
	StartsAtMS           int64  `gorm:"column:starts_at_ms;not null;default:0;comment:投放开始时间，UTC epoch ms" json:"startsAtMs"`
	EndsAtMS             int64  `gorm:"column:ends_at_ms;not null;default:0;comment:投放结束时间，UTC epoch ms" json:"endsAtMs"`
	CreatedAtMS          int64  `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
	UpdatedAtMS          int64  `gorm:"column:updated_at_ms;autoUpdateTime:milli" json:"updatedAtMs"`
}

func (AppBanner) TableName() string {
	return "admin_app_banners"
}

type AppSplashScreen struct {
	ID                uint   `gorm:"primaryKey" json:"id"`
	AppCode           string `gorm:"size:32;index:idx_admin_app_splash_screens_app_sort,not null;default:lalu" json:"appCode"`
	CoverURL          string `gorm:"size:1024;not null" json:"coverUrl"`
	SplashType        string `gorm:"size:16;not null" json:"splashType"`
	Param             string `gorm:"size:2048" json:"param"`
	Status            string `gorm:"size:24;index:idx_admin_app_splash_screens_app_sort,not null;default:active" json:"status"`
	Platform          string `gorm:"size:24;index:idx_admin_app_splash_screens_scope,not null" json:"platform"`
	SortOrder         int    `gorm:"index:idx_admin_app_splash_screens_app_sort,not null;default:0" json:"sortOrder"`
	RegionID          int64  `gorm:"index:idx_admin_app_splash_screens_scope,not null;default:0" json:"regionId"`
	CountryCode       string `gorm:"size:8;index:idx_admin_app_splash_screens_scope" json:"countryCode"`
	Description       string `gorm:"size:255" json:"description"`
	DisplayDurationMS int    `gorm:"column:display_duration_ms;not null;default:3000;comment:开屏展示时长，毫秒；0 表示客户端默认值" json:"displayDurationMs"`
	StartsAtMS        int64  `gorm:"column:starts_at_ms;not null;default:0;comment:投放开始时间，UTC epoch ms" json:"startsAtMs"`
	EndsAtMS          int64  `gorm:"column:ends_at_ms;not null;default:0;comment:投放结束时间，UTC epoch ms" json:"endsAtMs"`
	CreatedAtMS       int64  `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
	UpdatedAtMS       int64  `gorm:"column:updated_at_ms;autoUpdateTime:milli" json:"updatedAtMs"`
}

func (AppSplashScreen) TableName() string {
	return "admin_app_splash_screens"
}

type AppPopup struct {
	ID                uint   `gorm:"primaryKey" json:"id"`
	AppCode           string `gorm:"size:32;not null;default:lalu;index:idx_admin_app_popups_app_sort;uniqueIndex:uk_admin_app_popups_code" json:"appCode"`
	Code              string `gorm:"size:80;not null;uniqueIndex:uk_admin_app_popups_code;comment:弹窗编码" json:"code"`
	Name              string `gorm:"size:80;not null;comment:弹窗名称" json:"name"`
	ImageURL          string `gorm:"column:image_url;size:1024;not null;default:'';comment:弹窗图片 URL，可为空" json:"imageUrl"`
	JumpType          string `gorm:"size:16;not null;comment:跳转类型，h5 或 app" json:"jumpType"`
	JumpURL           string `gorm:"column:jump_url;size:2048;not null;default:'';comment:跳转地址" json:"jumpUrl"`
	DisplayPeriodDays int    `gorm:"column:display_period_days;not null;default:0;comment:展示周期天数，0 表示每次打开都展示" json:"displayPeriodDays"`
	Status            string `gorm:"size:24;index:idx_admin_app_popups_app_sort,not null;default:active" json:"status"`
	SortOrder         int    `gorm:"index:idx_admin_app_popups_app_sort,not null;default:0" json:"sortOrder"`
	StartsAtMS        int64  `gorm:"column:starts_at_ms;not null;default:0;comment:生效开始时间，UTC epoch ms" json:"startsAtMs"`
	EndsAtMS          int64  `gorm:"column:ends_at_ms;not null;default:0;comment:生效结束时间，UTC epoch ms" json:"endsAtMs"`
	CreatedAtMS       int64  `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
	UpdatedAtMS       int64  `gorm:"column:updated_at_ms;autoUpdateTime:milli" json:"updatedAtMs"`
}

func (AppPopup) TableName() string {
	return "admin_app_popups"
}

type AppVersion struct {
	ID          uint   `gorm:"primaryKey" json:"id"`
	AppCode     string `gorm:"size:32;uniqueIndex:uk_admin_app_versions_build,not null;default:lalu" json:"appCode"`
	Platform    string `gorm:"size:24;uniqueIndex:uk_admin_app_versions_build;index:idx_admin_app_versions_latest,not null" json:"platform"`
	Version     string `gorm:"size:40;not null" json:"version"`
	BuildNumber int64  `gorm:"uniqueIndex:uk_admin_app_versions_build;index:idx_admin_app_versions_latest,not null" json:"buildNumber"`
	ForceUpdate bool   `gorm:"not null;default:false" json:"forceUpdate"`
	DownloadURL string `gorm:"size:2048;not null" json:"downloadUrl"`
	Description string `gorm:"type:text" json:"description"`
	CreatedAtMS int64  `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
	UpdatedAtMS int64  `gorm:"column:updated_at_ms;autoUpdateTime:milli" json:"updatedAtMs"`
}

func (AppVersion) TableName() string {
	return "admin_app_versions"
}

type AppExploreTab struct {
	ID          uint   `gorm:"primaryKey" json:"id"`
	AppCode     string `gorm:"size:32;uniqueIndex:uk_admin_app_explore_tabs_tab;index:idx_admin_app_explore_tabs_app_sort,not null;default:lalu" json:"appCode"`
	Tab         string `gorm:"size:80;uniqueIndex:uk_admin_app_explore_tabs_tab,not null" json:"tab"`
	H5URL       string `gorm:"column:h5_url;size:2048;not null" json:"h5Url"`
	Enabled     bool   `gorm:"index:idx_admin_app_explore_tabs_app_sort,not null;default:true" json:"enabled"`
	SortOrder   int    `gorm:"index:idx_admin_app_explore_tabs_app_sort,not null;default:0" json:"sortOrder"`
	CreatedAtMS int64  `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
	UpdatedAtMS int64  `gorm:"column:updated_at_ms;autoUpdateTime:milli" json:"updatedAtMs"`
}

func (AppExploreTab) TableName() string {
	return "admin_app_explore_tabs"
}

type PolicyTemplate struct {
	TemplateID       uint64 `gorm:"column:template_id;primaryKey" json:"templateId"`
	TemplateCode     string `gorm:"column:template_code;size:96;uniqueIndex:uk_admin_policy_template_version;not null" json:"templateCode"`
	TemplateVersion  string `gorm:"column:template_version;size:64;uniqueIndex:uk_admin_policy_template_version;not null" json:"templateVersion"`
	Name             string `gorm:"size:160;not null" json:"name"`
	Status           string `gorm:"size:24;index:idx_admin_policy_templates_status,not null;default:active" json:"status"`
	RuleJSON         string `gorm:"column:rule_json;type:json;not null" json:"ruleJson"`
	Description      string `gorm:"size:512;not null;default:''" json:"description"`
	CreatedByAdminID uint64 `gorm:"column:created_by_admin_id;not null;default:0" json:"createdByAdminId"`
	UpdatedByAdminID uint64 `gorm:"column:updated_by_admin_id;not null;default:0" json:"updatedByAdminId"`
	CreatedAtMS      int64  `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
	UpdatedAtMS      int64  `gorm:"column:updated_at_ms;autoUpdateTime:milli;index:idx_admin_policy_templates_status" json:"updatedAtMs"`
}

func (PolicyTemplate) TableName() string {
	return "admin_policy_templates"
}

type PolicyTemplateVersion struct {
	VersionID        uint64 `gorm:"column:version_id;primaryKey" json:"versionId"`
	TemplateCode     string `gorm:"column:template_code;size:96;uniqueIndex:uk_admin_policy_template_version_row;index:idx_admin_policy_template_versions_status,not null" json:"templateCode"`
	TemplateVersion  string `gorm:"column:template_version;size:64;uniqueIndex:uk_admin_policy_template_version_row;not null" json:"templateVersion"`
	Status           string `gorm:"size:24;index:idx_admin_policy_template_versions_status,not null;default:active" json:"status"`
	RuleJSON         string `gorm:"column:rule_json;type:json;not null" json:"ruleJson"`
	CreatedByAdminID uint64 `gorm:"column:created_by_admin_id;not null;default:0" json:"createdByAdminId"`
	UpdatedByAdminID uint64 `gorm:"column:updated_by_admin_id;not null;default:0" json:"updatedByAdminId"`
	CreatedAtMS      int64  `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
	UpdatedAtMS      int64  `gorm:"column:updated_at_ms;autoUpdateTime:milli;index:idx_admin_policy_template_versions_status" json:"updatedAtMs"`
}

func (PolicyTemplateVersion) TableName() string {
	return "admin_policy_template_versions"
}

type PolicyInstance struct {
	InstanceID        uint64 `gorm:"column:instance_id;primaryKey" json:"instanceId"`
	AppCode           string `gorm:"column:app_code;size:32;uniqueIndex:uk_admin_policy_instance_code;index:idx_admin_policy_instances_app_status,not null" json:"appCode"`
	InstanceCode      string `gorm:"column:instance_code;size:96;uniqueIndex:uk_admin_policy_instance_code;not null" json:"instanceCode"`
	TemplateCode      string `gorm:"column:template_code;size:96;index:idx_admin_policy_instances_template,not null" json:"templateCode"`
	TemplateVersion   string `gorm:"column:template_version;size:64;index:idx_admin_policy_instances_template,not null" json:"templateVersion"`
	RegionScope       string `gorm:"column:region_scope;size:128;not null;default:all_active_regions" json:"regionScope"`
	Status            string `gorm:"size:24;index:idx_admin_policy_instances_app_status,not null;default:active" json:"status"`
	EffectiveFromMS   int64  `gorm:"column:effective_from_ms;not null;default:0" json:"effectiveFromMs"`
	EffectiveToMS     int64  `gorm:"column:effective_to_ms;not null;default:0" json:"effectiveToMs"`
	PublishStatus     string `gorm:"column:publish_status;size:24;index:idx_admin_policy_instances_app_status,not null;default:draft" json:"publishStatus"`
	PublishError      string `gorm:"column:publish_error;size:512;not null;default:''" json:"publishError"`
	LastPublishedAtMS int64  `gorm:"column:last_published_at_ms;not null;default:0" json:"lastPublishedAtMs"`
	CreatedByAdminID  uint64 `gorm:"column:created_by_admin_id;not null;default:0" json:"createdByAdminId"`
	UpdatedByAdminID  uint64 `gorm:"column:updated_by_admin_id;not null;default:0" json:"updatedByAdminId"`
	CreatedAtMS       int64  `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
	UpdatedAtMS       int64  `gorm:"column:updated_at_ms;autoUpdateTime:milli" json:"updatedAtMs"`
}

func (PolicyInstance) TableName() string {
	return "admin_policy_instances"
}

type PolicyPublishJob struct {
	JobID           uint64 `gorm:"column:job_id;primaryKey" json:"jobId"`
	InstanceID      uint64 `gorm:"column:instance_id;index:idx_admin_policy_publish_jobs_instance,not null" json:"instanceId"`
	AppCode         string `gorm:"column:app_code;size:32;not null" json:"appCode"`
	InstanceCode    string `gorm:"column:instance_code;size:96;not null" json:"instanceCode"`
	TemplateCode    string `gorm:"column:template_code;size:96;not null" json:"templateCode"`
	TemplateVersion string `gorm:"column:template_version;size:64;not null" json:"templateVersion"`
	Status          string `gorm:"size:24;index:idx_admin_policy_publish_jobs_status,not null;default:pending" json:"status"`
	TargetCount     int    `gorm:"column:target_count;not null;default:0" json:"targetCount"`
	SuccessCount    int    `gorm:"column:success_count;not null;default:0" json:"successCount"`
	FailureCount    int    `gorm:"column:failure_count;not null;default:0" json:"failureCount"`
	ErrorMessage    string `gorm:"column:error_message;size:512;not null;default:''" json:"errorMessage"`
	OperatorAdminID uint64 `gorm:"column:operator_admin_id;not null;default:0" json:"operatorAdminId"`
	CreatedAtMS     int64  `gorm:"column:created_at_ms;autoCreateTime:milli;index:idx_admin_policy_publish_jobs_instance" json:"createdAtMs"`
	UpdatedAtMS     int64  `gorm:"column:updated_at_ms;autoUpdateTime:milli;index:idx_admin_policy_publish_jobs_status" json:"updatedAtMs"`
}

func (PolicyPublishJob) TableName() string {
	return "admin_policy_publish_jobs"
}

type PolicyPublishItem struct {
	ItemID       uint64 `gorm:"column:item_id;primaryKey" json:"itemId"`
	JobID        uint64 `gorm:"column:job_id;index:idx_admin_policy_publish_items_job,not null" json:"jobId"`
	OwnerService string `gorm:"column:owner_service;size:32;index:idx_admin_policy_publish_items_job,not null" json:"ownerService"`
	AppCode      string `gorm:"column:app_code;size:32;index:idx_admin_policy_publish_items_region,not null" json:"appCode"`
	RegionID     int64  `gorm:"column:region_id;index:idx_admin_policy_publish_items_region,not null;default:0" json:"regionId"`
	Status       string `gorm:"size:24;index:idx_admin_policy_publish_items_job,not null;default:pending" json:"status"`
	ErrorMessage string `gorm:"column:error_message;size:512;not null;default:''" json:"errorMessage"`
	CreatedAtMS  int64  `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
	UpdatedAtMS  int64  `gorm:"column:updated_at_ms;autoUpdateTime:milli" json:"updatedAtMs"`
}

func (PolicyPublishItem) TableName() string {
	return "admin_policy_publish_items"
}

type HostAgencySalaryPolicy struct {
	ID      uint   `gorm:"primaryKey" json:"id"`
	AppCode string `gorm:"size:32;uniqueIndex:uk_admin_host_agency_salary_policy_name;uniqueIndex:uk_admin_host_agency_salary_policy_version;index:idx_admin_host_agency_salary_policy_region;index:idx_admin_host_agency_salary_policy_cycle,not null;default:lalu" json:"appCode"`
	Name    string `gorm:"size:120;uniqueIndex:uk_admin_host_agency_salary_policy_name;not null" json:"name"`
	// PolicyType 决定送礼收益进入周期工资投影还是永久钻石积分钱包；业务代码只按该快照分流，不按 App 名称判断。
	PolicyType string `gorm:"column:policy_type;size:32;index:idx_admin_host_agency_salary_policy_cycle,not null;default:SALARY_DIAMOND" json:"policyType"`
	// RegionID 只保存 RegionIDs 中最小的兼容值；真实适用边界由关联表按用户区域匹配。
	RegionID int64 `gorm:"uniqueIndex:uk_admin_host_agency_salary_policy_name;uniqueIndex:uk_admin_host_agency_salary_policy_version;index:idx_admin_host_agency_salary_policy_region;index:idx_admin_host_agency_salary_policy_cycle,not null" json:"regionId"`
	// RegionIDs 是这份政策实际覆盖的区域集合；RegionID 只保留首个值用于旧客户端和旧索引兼容。
	RegionIDs []int64 `gorm:"-" json:"regionIds"`
	// CycleKey 是工资归属月；PolicyVersion 首次发布等于主键，当前/未来月份修订时递增并对应一份不可变 wallet 快照。
	CycleKey      string `gorm:"column:cycle_key;size:7;uniqueIndex:uk_admin_host_agency_salary_policy_name;uniqueIndex:uk_admin_host_agency_salary_policy_version;index:idx_admin_host_agency_salary_policy_cycle,not null" json:"cycleKey"`
	PolicyVersion uint64 `gorm:"column:policy_version;uniqueIndex:uk_admin_host_agency_salary_policy_version;not null;default:0" json:"policyVersion"`
	Status        string `gorm:"size:24;index:idx_admin_host_agency_salary_policy_region,not null;default:disabled" json:"status"`
	// SettlementMode 只配置结算节奏，等级金额仍按累计值保存，实际发放由后续结算任务计算差额。
	SettlementMode string `gorm:"size:24;not null;default:daily" json:"settlementMode"`
	// SettlementTriggerMode 区分自动任务与后台人工结算；手动政策发布后仍可被查询，但不会被 cron 自动发薪。
	SettlementTriggerMode string `gorm:"column:settlement_trigger_mode;size:24;not null;default:automatic" json:"settlementTriggerMode"`
	// 比例字段使用 decimal 字符串承载，避免金币、钻石、美元换算出现 float 精度误差。
	GiftCoinToDiamondRatio string `gorm:"column:gift_coin_to_diamond_ratio;type:decimal(18,6);not null;default:1.000000" json:"giftCoinToDiamondRatio"`
	PointDiamondsPerUSD    int64  `gorm:"column:point_diamonds_per_usd;not null;default:0" json:"pointDiamondsPerUsd"`
	CoinsPerUSD            int64  `gorm:"column:coins_per_usd;not null;default:0" json:"coinsPerUsd"`
	// MinimumWithdrawUSDMinor 保留为旧客户端平台门槛别名；新配置和运行校验必须使用下面两个渠道字段。
	MinimumWithdrawUSDMinor           int64  `gorm:"column:minimum_withdraw_usd_minor;not null;default:0" json:"minimumWithdrawUsdMinor"`
	CoinSellerMinimumWithdrawUSDMinor int64  `gorm:"column:coin_seller_minimum_withdraw_usd_minor;not null;default:0" json:"coinSellerMinimumWithdrawUsdMinor"`
	PlatformMinimumWithdrawUSDMinor   int64  `gorm:"column:platform_minimum_withdraw_usd_minor;not null;default:0" json:"platformMinimumWithdrawUsdMinor"`
	DiamondExchangeEnabled            bool   `gorm:"column:diamond_exchange_enabled;not null;default:true" json:"diamondExchangeEnabled"`
	WithdrawFeeBPS                    int32  `gorm:"column:withdraw_fee_bps;not null;default:0" json:"withdrawFeeBps"`
	AgencyPointShareBPS               int32  `gorm:"column:agency_point_share_bps;not null;default:0" json:"agencyPointShareBps"`
	ResidualDiamondToUSDRate          string `gorm:"column:residual_diamond_to_usd_rate;type:decimal(24,12);not null;default:0.000000000000" json:"residualDiamondToUsdRate"`
	// 提现次数限制随月度政策形成不可变快照；count=0 表示不限，period 只允许 day/week/month。
	CoinSellerWithdrawalLimitPeriod string `gorm:"column:coin_seller_withdrawal_limit_period;size:16;not null;default:month" json:"coinSellerWithdrawalLimitPeriod"`
	CoinSellerWithdrawalLimitCount  int64  `gorm:"column:coin_seller_withdrawal_limit_count;not null;default:0" json:"coinSellerWithdrawalLimitCount"`
	PlatformWithdrawalLimitPeriod   string `gorm:"column:platform_withdrawal_limit_period;size:16;not null;default:month" json:"platformWithdrawalLimitPeriod"`
	PlatformWithdrawalLimitCount    int64  `gorm:"column:platform_withdrawal_limit_count;not null;default:0" json:"platformWithdrawalLimitCount"`
	// PlatformWithdrawalAllowedDays 使用排序去重后的逗号串保存 UTC 月内日；空串兼容为每天可提现。
	PlatformWithdrawalAllowedDays string `gorm:"column:platform_withdrawal_allowed_days;size:96;not null;default:''" json:"platformWithdrawalAllowedDays"`
	// Effective* 仅保留兼容既有表结构；后台不再配置或展示，工资结算只按 CycleKey 绑定版本。
	EffectiveFromMS  int64  `gorm:"column:effective_from_ms;index:idx_admin_host_agency_salary_policy_region,not null;default:0" json:"effectiveFromMs"`
	EffectiveToMS    int64  `gorm:"column:effective_to_ms;not null;default:0" json:"effectiveToMs"`
	Description      string `gorm:"size:255;not null;default:''" json:"description"`
	CreatedByAdminID uint   `gorm:"column:created_by_admin_id;not null;default:0" json:"createdByAdminId"`
	UpdatedByAdminID uint   `gorm:"column:updated_by_admin_id;not null;default:0" json:"updatedByAdminId"`
	// 发布字段只记录 admin 配置同步到 wallet 运行快照的结果；结算仍只读取 wallet-service 自己的运行表。
	PublishStatus      string `gorm:"column:publish_status;size:24;not null;default:draft" json:"publishStatus"`
	PublishError       string `gorm:"column:publish_error;size:255;not null;default:''" json:"publishError"`
	PublishedAtMS      int64  `gorm:"column:published_at_ms;not null;default:0" json:"publishedAtMs"`
	PublishedByAdminID uint   `gorm:"column:published_by_admin_id;not null;default:0" json:"publishedByAdminId"`
	// CopiedFrom* 记录系统补齐缺失月份时的来源；新月份拥有独立 policy_version，后续编辑不会改动上月口径。
	CopiedFromPolicyID uint                    `gorm:"column:copied_from_policy_id;not null;default:0" json:"copiedFromPolicyId"`
	CopiedFromCycleKey string                  `gorm:"column:copied_from_cycle_key;size:7;not null;default:''" json:"copiedFromCycleKey"`
	CreatedAtMS        int64                   `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
	UpdatedAtMS        int64                   `gorm:"column:updated_at_ms;autoUpdateTime:milli" json:"updatedAtMs"`
	Levels             []HostAgencySalaryLevel `gorm:"foreignKey:PolicyID" json:"levels"`
}

func (HostAgencySalaryPolicy) TableName() string {
	return "admin_host_agency_salary_policies"
}

// HostAgencySalaryPolicyRegion 把一份政策关联到多个区域，避免复制多份可独立漂移的等级配置。
type HostAgencySalaryPolicyRegion struct {
	PolicyID    uint  `gorm:"column:policy_id;primaryKey"`
	RegionID    int64 `gorm:"column:region_id;primaryKey;index:idx_host_agency_policy_region,priority:1"`
	CreatedAtMS int64 `gorm:"column:created_at_ms;not null"`
}

func (HostAgencySalaryPolicyRegion) TableName() string {
	return "admin_host_agency_salary_policy_regions"
}

// HostAgencySalaryPolicyActiveScope 是启用政策的数据库级区域/月占位。
// 普通区域映射允许一份政策覆盖多区，但无法表达“同一区域同月只能有一份 active 政策”；
// 该表用业务主键把并发校验下沉到 MySQL，policy_id 索引用于政策更新、停用和删除时整包释放。
type HostAgencySalaryPolicyActiveScope struct {
	AppCode     string `gorm:"column:app_code;size:32;primaryKey"`
	RegionID    int64  `gorm:"column:region_id;primaryKey"`
	CycleKey    string `gorm:"column:cycle_key;size:7;primaryKey"`
	PolicyID    uint   `gorm:"column:policy_id;index:idx_host_agency_salary_policy_active_scope_policy,priority:1;not null"`
	CreatedAtMS int64  `gorm:"column:created_at_ms;not null"`
	UpdatedAtMS int64  `gorm:"column:updated_at_ms;not null"`
}

func (HostAgencySalaryPolicyActiveScope) TableName() string {
	return "admin_host_agency_salary_policy_active_scopes"
}

type HostAgencySalaryLevel struct {
	ID       uint `gorm:"primaryKey" json:"id"`
	PolicyID uint `gorm:"uniqueIndex:uk_admin_host_agency_salary_level;index:idx_admin_host_agency_salary_level_policy,not null" json:"policyId"`
	// LevelNo 是累计工资差额结算的顺序锚点，必须在同一 policy 下唯一。
	LevelNo int32 `gorm:"column:level_no;uniqueIndex:uk_admin_host_agency_salary_level;not null" json:"level"`
	// RequiredDiamonds 保存主播当月钻石门槛，月底清空逻辑不在 admin-server 第一阶段执行。
	RequiredDiamonds int64 `gorm:"column:required_diamonds;index:idx_admin_host_agency_salary_level_policy,not null" json:"requiredDiamonds"`
	// 三个权益字段保存“达到该等级后的累计值”，结算任务只发本次等级与已结算等级之间的差额。
	HostSalaryUSD   string `gorm:"column:host_salary_usd;type:decimal(12,2);not null;default:0.00" json:"hostSalaryUsd"`
	HostCoinReward  int64  `gorm:"column:host_coin_reward;not null;default:0" json:"hostCoinReward"`
	AgencySalaryUSD string `gorm:"column:agency_salary_usd;type:decimal(12,2);not null;default:0.00" json:"agencySalaryUsd"`
	Status          string `gorm:"size:24;not null;default:active" json:"status"`
	SortOrder       int32  `gorm:"column:sort_order;not null;default:0" json:"sortOrder"`
	CreatedAtMS     int64  `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
	UpdatedAtMS     int64  `gorm:"column:updated_at_ms;autoUpdateTime:milli" json:"updatedAtMs"`
}

func (HostAgencySalaryLevel) TableName() string {
	return "admin_host_agency_salary_policy_levels"
}

type TeamSalaryPolicy struct {
	ID      uint   `gorm:"primaryKey" json:"id"`
	AppCode string `gorm:"size:32;uniqueIndex:uk_admin_team_salary_policy_name;index:idx_admin_team_salary_policy_scope,not null;default:lalu" json:"appCode"`
	// PolicyType 当前只允许 bd/admin；拆表会让两类策略后续结算扩展变重，所以用类型字段保持同一套配置 API。
	PolicyType string `gorm:"column:policy_type;size:24;uniqueIndex:uk_admin_team_salary_policy_name;index:idx_admin_team_salary_policy_scope,not null" json:"policyType"`
	Name       string `gorm:"size:120;uniqueIndex:uk_admin_team_salary_policy_name;not null" json:"name"`
	// RegionID 沿用 Host 政策的区域边界，保证同一区域组织链路可以命中同一套工资配置。
	RegionID int64  `gorm:"uniqueIndex:uk_admin_team_salary_policy_name;index:idx_admin_team_salary_policy_scope,not null" json:"regionId"`
	Status   string `gorm:"size:24;index:idx_admin_team_salary_policy_scope,not null;default:disabled" json:"status"`
	// SettlementTriggerMode 决定次月 5 日窗口由自动任务处理还是进入后台人工批量结算池。
	SettlementTriggerMode string `gorm:"column:settlement_trigger_mode;size:24;not null;default:automatic" json:"settlementTriggerMode"`
	// BD/Admin 都按完整自然月统计，结算日期固定为次月 5 日；这里不再提供日结/半月结节奏字段。
	EffectiveFromMS  int64             `gorm:"column:effective_from_ms;index:idx_admin_team_salary_policy_scope,not null;default:0" json:"effectiveFromMs"`
	EffectiveToMS    int64             `gorm:"column:effective_to_ms;not null;default:0" json:"effectiveToMs"`
	Description      string            `gorm:"size:255;not null;default:''" json:"description"`
	CreatedByAdminID uint              `gorm:"column:created_by_admin_id;not null;default:0" json:"createdByAdminId"`
	UpdatedByAdminID uint              `gorm:"column:updated_by_admin_id;not null;default:0" json:"updatedByAdminId"`
	CreatedAtMS      int64             `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
	UpdatedAtMS      int64             `gorm:"column:updated_at_ms;autoUpdateTime:milli" json:"updatedAtMs"`
	Levels           []TeamSalaryLevel `gorm:"foreignKey:PolicyID" json:"levels"`
}

func (TeamSalaryPolicy) TableName() string {
	return "admin_team_salary_policies"
}

type TeamSalaryLevel struct {
	ID       uint `gorm:"primaryKey" json:"id"`
	PolicyID uint `gorm:"uniqueIndex:uk_admin_team_salary_level;index:idx_admin_team_salary_level_policy,not null" json:"policyId"`
	// LevelNo 是差额结算锚点，当前等级累计工资减去已结算累计工资就是本次应发金额。
	LevelNo int32 `gorm:"column:level_no;uniqueIndex:uk_admin_team_salary_level;not null" json:"level"`
	// ThresholdUSD 对 BD 表示下属 Agency 管理主播 Host Salary 合计门槛，对 Admin 表示下属 BD Salary 合计门槛。
	ThresholdUSD string `gorm:"column:threshold_usd;type:decimal(12,2);index:idx_admin_team_salary_level_policy,not null;default:0.00" json:"thresholdUsd"`
	// RatePercent 保存运营看到的百分比数字，例如 8 表示 8%，结算时再换算成比例。
	RatePercent string `gorm:"column:rate_percent;type:decimal(9,4);not null;default:0.0000" json:"ratePercent"`
	// SalaryUSD 是达到该等级后的累计应得工资，实际入账仍按差额结算。
	SalaryUSD   string `gorm:"column:salary_usd;type:decimal(12,2);not null;default:0.00" json:"salaryUsd"`
	Status      string `gorm:"size:24;not null;default:active" json:"status"`
	SortOrder   int32  `gorm:"column:sort_order;not null;default:0" json:"sortOrder"`
	CreatedAtMS int64  `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
	UpdatedAtMS int64  `gorm:"column:updated_at_ms;autoUpdateTime:milli" json:"updatedAtMs"`
}

func (TeamSalaryLevel) TableName() string {
	return "admin_team_salary_policy_levels"
}

type RefreshToken struct {
	ID          uint   `gorm:"primaryKey" json:"id"`
	UserID      uint   `gorm:"index;not null" json:"userId"`
	TokenHash   string `gorm:"size:128;uniqueIndex;not null" json:"-"`
	UserAgent   string `gorm:"size:255" json:"userAgent"`
	IP          string `gorm:"size:64" json:"ip"`
	ExpiresAtMS int64  `gorm:"column:expires_at_ms;index;not null" json:"expiresAtMs"`
	RevokedAtMS *int64 `gorm:"column:revoked_at_ms" json:"revokedAtMs"`
	CreatedAtMS int64  `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
	UpdatedAtMS int64  `gorm:"column:updated_at_ms;autoUpdateTime:milli" json:"updatedAtMs"`
}

func (RefreshToken) TableName() string {
	return "admin_refresh_tokens"
}

type LoginLog struct {
	ID          uint   `gorm:"primaryKey" json:"id"`
	Username    string `gorm:"size:64;index" json:"username"`
	UserID      *uint  `gorm:"index" json:"userId"`
	IP          string `gorm:"size:64" json:"ip"`
	UserAgent   string `gorm:"size:255" json:"userAgent"`
	Status      string `gorm:"size:32;index;not null" json:"status"`
	Message     string `gorm:"size:255" json:"message"`
	CreatedAtMS int64  `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
}

func (LoginLog) TableName() string {
	return "admin_login_logs"
}

type OperationLog struct {
	ID          uint   `gorm:"primaryKey" json:"id"`
	RequestID   string `gorm:"size:64;index;not null;default:''" json:"requestId"`
	UserID      uint   `gorm:"index;not null" json:"userId"`
	Username    string `gorm:"size:64;index;not null" json:"username"`
	Action      string `gorm:"size:80;index;not null" json:"action"`
	Resource    string `gorm:"size:120;index" json:"resource"`
	ResourceID  string `gorm:"size:80;index;not null;default:''" json:"resourceId"`
	Method      string `gorm:"size:12" json:"method"`
	Path        string `gorm:"size:180" json:"path"`
	IP          string `gorm:"size:64" json:"ip"`
	UserAgent   string `gorm:"size:255;not null;default:''" json:"userAgent"`
	Status      string `gorm:"size:32;index;not null" json:"status"`
	HTTPStatus  int    `gorm:"not null;default:0" json:"httpStatus"`
	RiskLevel   string `gorm:"size:24;index;not null;default:normal" json:"riskLevel"`
	LatencyMS   int64  `gorm:"not null;default:0" json:"latencyMs"`
	Detail      string `gorm:"type:text" json:"detail"`
	CreatedAtMS int64  `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
}

func (OperationLog) TableName() string {
	return "admin_operation_logs"
}

type DataScope struct {
	ID          uint   `gorm:"primaryKey" json:"id"`
	RoleID      uint   `gorm:"index;not null" json:"roleId"`
	ScopeType   string `gorm:"size:40;index;not null" json:"scopeType"`
	ScopeValue  string `gorm:"size:120;index;not null" json:"scopeValue"`
	CreatedAtMS int64  `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
	UpdatedAtMS int64  `gorm:"column:updated_at_ms;autoUpdateTime:milli" json:"updatedAtMs"`
}

func (DataScope) TableName() string {
	return "admin_data_scopes"
}

type UserMoneyScope struct {
	ID          uint   `gorm:"primaryKey" json:"id"`
	UserID      uint   `gorm:"index:idx_admin_user_money_scopes_user;index:uk_admin_user_money_scope,unique;not null" json:"userId"`
	AppCode     string `gorm:"size:32;index:idx_admin_user_money_scopes_app_region;index:uk_admin_user_money_scope,unique;not null" json:"appCode"`
	RegionID    int64  `gorm:"index:idx_admin_user_money_scopes_app_region;index:uk_admin_user_money_scope,unique;not null;default:0" json:"regionId"`
	CreatedAtMS int64  `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
	UpdatedAtMS int64  `gorm:"column:updated_at_ms;autoUpdateTime:milli" json:"updatedAtMs"`
}

func (UserMoneyScope) TableName() string {
	return "admin_user_money_scopes"
}

// UserAppScope 是后台主站的 App 可见范围。它与财务区域范围彼此独立：前者决定主站可切换的 App，
// 后者继续决定财务工作台可查看的 App/区域，不能复用同一张表，否则无 App 用户将无法进入通用财务工作台。
type UserAppScope struct {
	ID          uint   `gorm:"primaryKey" json:"id"`
	UserID      uint   `gorm:"index:idx_admin_user_app_scopes_user;index:uk_admin_user_app_scope,unique;not null" json:"userId"`
	AppCode     string `gorm:"size:32;index:uk_admin_user_app_scope,unique;index:idx_admin_user_app_scopes_app;not null" json:"appCode"`
	CreatedAtMS int64  `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
	UpdatedAtMS int64  `gorm:"column:updated_at_ms;autoUpdateTime:milli" json:"updatedAtMs"`
}

func (UserAppScope) TableName() string {
	return "admin_user_app_scopes"
}

type TemporaryPaymentLinkOwner struct {
	AppCode     string `gorm:"size:32;primaryKey" json:"appCode"`
	OrderID     string `gorm:"size:96;primaryKey" json:"orderId"`
	AdminUserID uint   `gorm:"index:idx_admin_temporary_payment_link_owners_user;not null" json:"adminUserId"`
	RegionID    int64  `gorm:"index:idx_admin_temporary_payment_link_owners_region;not null" json:"regionId"`
	CreatedAtMS int64  `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
	UpdatedAtMS int64  `gorm:"column:updated_at_ms;autoUpdateTime:milli" json:"updatedAtMs"`
}

func (TemporaryPaymentLinkOwner) TableName() string {
	return "admin_temporary_payment_link_owners"
}

// LegacyGooglePaidDetail 缓存 legacy App（Yumi/Aslan）谷歌账单经 Play Orders API 同步的实付明细；
// legacy 平台的 Mongo 对 admin 只读，实付事实只能落在 admin 自己的库里，按账单交易号回填展示。
type LegacyGooglePaidDetail struct {
	AppCode          string `gorm:"size:32;primaryKey;index:idx_legacy_google_paid_bill_time,priority:1" json:"appCode"`
	TransactionID    string `gorm:"size:96;primaryKey;column:transaction_id" json:"transactionId"`
	ProviderOrderID  string `gorm:"size:128;not null;default:''" json:"providerOrderId"`
	PaidCurrencyCode string `gorm:"size:8;not null;default:''" json:"paidCurrencyCode"`
	PaidAmountMicro  int64  `gorm:"not null;default:0" json:"paidAmountMicro"`
	PaidTaxMicro     int64  `gorm:"not null;default:0" json:"paidTaxMicro"`
	PaidNetMicro     int64  `gorm:"not null;default:0" json:"paidNetMicro"`
	// BillUSDMinor/BillCreatedAtMS 是同步时从 legacy 账单带过来的快照，让概览统计不用再回查 Mongo。
	BillUSDMinor    int64 `gorm:"column:bill_usd_minor;not null;default:0" json:"billUsdMinor"`
	BillCreatedAtMS int64 `gorm:"column:bill_created_at_ms;not null;default:0;index:idx_legacy_google_paid_bill_time,priority:2" json:"billCreatedAtMs"`
	SyncedAtMS      int64 `gorm:"column:synced_at_ms;not null" json:"syncedAtMs"`
	CreatedAtMS     int64 `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
	UpdatedAtMS     int64 `gorm:"column:updated_at_ms;autoUpdateTime:milli" json:"updatedAtMs"`
}

func (LegacyGooglePaidDetail) TableName() string {
	return "admin_legacy_google_paid_details"
}

// LegacyGooglePaidSyncAttempt persists retry state separately from paid details.
// A row here never means "synced"; successful synchronization deletes it after the paid-detail upsert commits.
type LegacyGooglePaidSyncAttempt struct {
	AppCode       string `gorm:"size:32;primaryKey" json:"appCode"`
	TransactionID string `gorm:"size:96;primaryKey;column:transaction_id" json:"transactionId"`
	AttemptCount  int    `gorm:"not null;default:0" json:"attemptCount"`
	LastError     string `gorm:"size:512;not null;default:''" json:"lastError"`
	NextRetryAtMS int64  `gorm:"column:next_retry_at_ms;not null;default:0;index:idx_legacy_google_paid_retry,priority:2" json:"nextRetryAtMs"`
	CreatedAtMS   int64  `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
	UpdatedAtMS   int64  `gorm:"column:updated_at_ms;autoUpdateTime:milli;index:idx_legacy_google_paid_retry,priority:3" json:"updatedAtMs"`
}

func (LegacyGooglePaidSyncAttempt) TableName() string {
	return "admin_legacy_google_paid_sync_attempts"
}

// CoinSellerRechargeOrder 是后台币商进货的统一审计入口；真实入账事实仍由 Lalu wallet-service 或 legacy 钱包账套持有。
type CoinSellerRechargeOrder struct {
	ID                   uint   `gorm:"primaryKey" json:"id"`
	AppCode              string `gorm:"size:32;not null;index:idx_admin_coin_seller_recharge_app_time;uniqueIndex:uk_admin_coin_seller_recharge_external,priority:1" json:"appCode"`
	TargetUserID         int64  `gorm:"column:target_user_id;not null;index:idx_admin_coin_seller_recharge_target" json:"targetUserId"`
	TargetDisplayUserID  string `gorm:"column:target_display_user_id;size:64;not null;default:'';index:idx_admin_coin_seller_recharge_display" json:"targetDisplayUserId"`
	TargetCountryID      int64  `gorm:"column:target_country_id;not null;default:0" json:"targetCountryId"`
	TargetRegionID       int64  `gorm:"column:target_region_id;not null;default:0" json:"targetRegionId"`
	USDAmount            string `gorm:"column:usd_amount;type:decimal(18,2);not null;default:0.00" json:"usdAmount"`
	USDMinorAmount       int64  `gorm:"column:usd_minor_amount;not null;default:0" json:"usdMinorAmount"`
	CoinAmount           int64  `gorm:"column:coin_amount;not null;default:0" json:"coinAmount"`
	ProviderCode         string `gorm:"column:provider_code;size:32;not null;uniqueIndex:uk_admin_coin_seller_recharge_external,priority:2;index:idx_admin_coin_seller_recharge_provider" json:"providerCode"`
	Chain                string `gorm:"size:16;not null;default:'';uniqueIndex:uk_admin_coin_seller_recharge_external,priority:3" json:"chain"`
	ExternalOrderNo      string `gorm:"column:external_order_no;size:128;not null;uniqueIndex:uk_admin_coin_seller_recharge_external,priority:4;index:idx_admin_coin_seller_recharge_external" json:"externalOrderNo"`
	ProviderOrderID      string `gorm:"column:provider_order_id;size:128;not null;default:''" json:"providerOrderId"`
	ProviderStatus       string `gorm:"column:provider_status;size:64;not null;default:''" json:"providerStatus"`
	ProviderCountryCode  string `gorm:"column:provider_country_code;size:16;not null;default:''" json:"providerCountryCode"`
	ProviderCurrencyCode string `gorm:"column:provider_currency_code;size:16;not null;default:''" json:"providerCurrencyCode"`
	ProviderAmountMinor  int64  `gorm:"column:provider_amount_minor;not null;default:0" json:"providerAmountMinor"`
	ProviderPayType      string `gorm:"column:provider_pay_type;size:64;not null;default:''" json:"providerPayType"`
	ReceiveAddress       string `gorm:"column:receive_address;size:128;not null;default:''" json:"receiveAddress"`
	OrderDateMS          int64  `gorm:"column:order_date_ms;not null;default:0" json:"orderDateMs"`
	Status               string `gorm:"size:32;not null;default:created;index:idx_admin_coin_seller_recharge_status_time" json:"status"`
	VerifyStatus         string `gorm:"column:verify_status;size:32;not null;default:pending;index:idx_admin_coin_seller_recharge_verify" json:"verifyStatus"`
	GrantStatus          string `gorm:"column:grant_status;size:32;not null;default:pending;index:idx_admin_coin_seller_recharge_grant" json:"grantStatus"`
	Remark               string `gorm:"type:text" json:"remark"`
	FailureReason        string `gorm:"column:failure_reason;type:text" json:"failureReason"`
	ProviderPayloadJSON  string `gorm:"column:provider_payload_json;type:text" json:"providerPayloadJson"`
	WalletCommandID      string `gorm:"column:wallet_command_id;size:128;not null;default:'';index:idx_admin_coin_seller_recharge_command" json:"walletCommandId"`
	WalletTransactionID  string `gorm:"column:wallet_transaction_id;size:128;not null;default:'';index:idx_admin_coin_seller_recharge_wallet_tx" json:"walletTransactionId"`
	WalletAssetType      string `gorm:"column:wallet_asset_type;size:64;not null;default:''" json:"walletAssetType"`
	WalletAmountDelta    int64  `gorm:"column:wallet_amount_delta;not null;default:0" json:"walletAmountDelta"`
	WalletBalanceAfter   int64  `gorm:"column:wallet_balance_after;not null;default:0" json:"walletBalanceAfter"`
	OperatorUserID       uint   `gorm:"column:operator_user_id;not null;index:idx_admin_coin_seller_recharge_operator" json:"operatorUserId"`
	OperatorName         string `gorm:"column:operator_name;size:64;not null;default:''" json:"operatorName"`
	VerifiedByUserID     *uint  `gorm:"column:verified_by_user_id;index:idx_admin_coin_seller_recharge_verifier" json:"verifiedByUserId"`
	VerifiedByName       string `gorm:"column:verified_by_name;size:64;not null;default:''" json:"verifiedByName"`
	GrantedByUserID      *uint  `gorm:"column:granted_by_user_id;index:idx_admin_coin_seller_recharge_granter" json:"grantedByUserId"`
	GrantedByName        string `gorm:"column:granted_by_name;size:64;not null;default:''" json:"grantedByName"`
	VerifiedAtMS         *int64 `gorm:"column:verified_at_ms" json:"verifiedAtMs"`
	GrantedAtMS          *int64 `gorm:"column:granted_at_ms" json:"grantedAtMs"`
	CreatedAtMS          int64  `gorm:"column:created_at_ms;autoCreateTime:milli;index:idx_admin_coin_seller_recharge_app_time;index:idx_admin_coin_seller_recharge_status_time" json:"createdAtMs"`
	UpdatedAtMS          int64  `gorm:"column:updated_at_ms;autoUpdateTime:milli" json:"updatedAtMs"`
}

func (CoinSellerRechargeOrder) TableName() string {
	return "admin_coin_seller_recharge_orders"
}

// BusinessTimeMS 是财务归档和统计切日使用的订单业务时间；历史订单未保存 order_date_ms 时回退到真实录入时间。
func (order CoinSellerRechargeOrder) BusinessTimeMS() int64 {
	if order.OrderDateMS > 0 {
		return order.OrderDateMS
	}
	return order.CreatedAtMS
}

type UserWithdrawalApplication struct {
	ID                  uint   `gorm:"primaryKey;index:idx_admin_withdrawal_app_operations_status_time,priority:4" json:"id"`
	AppCode             string `gorm:"size:32;index:idx_admin_withdrawal_app_status_time;index:idx_admin_withdrawal_app_operations_status_time,priority:1;not null" json:"appCode"`
	SourceSystem        string `gorm:"column:source_system;size:32;not null;default:hyapp_wallet" json:"sourceSystem"`
	SourceApplicationID string `gorm:"column:source_application_id;size:128;not null;default:''" json:"sourceApplicationId"`
	UserID              string `gorm:"size:64;index:idx_admin_withdrawal_user;not null" json:"userId"`
	SalaryAssetType     string `gorm:"size:64;not null;default:''" json:"salaryAssetType"`
	WithdrawAmount      string `gorm:"type:decimal(18,2);not null;default:0.00" json:"withdrawAmount"`
	WithdrawAmountMinor int64  `gorm:"not null;default:0" json:"withdrawAmountMinor"`
	// BalanceBefore 是来源钱包在扣款前固化的可用余额；nil 表示历史申请没有可信快照，禁止回查当前余额补写。
	BalanceBefore *string `gorm:"column:balance_before;type:decimal(18,2)" json:"balanceBefore"`
	// 外部来源的费率只是申请时政策快照；手续费和实际打款额才是财务审核与统计使用的不可变资金事实。
	ServiceChargeRatio           *string `gorm:"column:service_charge_ratio;type:decimal(9,4)" json:"serviceChargeRatio"`
	ServiceCharge                *string `gorm:"column:service_charge;type:decimal(18,2)" json:"serviceCharge"`
	ActualAmount                 *string `gorm:"column:actual_amount;type:decimal(18,2)" json:"actualAmount"`
	ActualAmountMinor            *int64  `gorm:"column:actual_amount_minor" json:"actualAmountMinor"`
	ExternalFinanceClaimDecision *string `gorm:"column:external_finance_claim_decision;size:16" json:"externalFinanceClaimDecision"`
	ExternalFinanceClaimStatus   *string `gorm:"column:external_finance_claim_status;size:16" json:"externalFinanceClaimStatus"`
	PointFeeAmount               int64   `gorm:"not null;default:0" json:"pointFeeAmount"`
	PointNetAmount               int64   `gorm:"not null;default:0" json:"pointNetAmount"`
	PointsPerUSD                 int64   `gorm:"not null;default:100000" json:"pointsPerUsd"`
	PointFeeBPS                  int32   `gorm:"not null;default:500" json:"pointFeeBps"`
	PointPolicyInstance          string  `gorm:"column:point_policy_instance_code;size:96;not null;default:''" json:"pointPolicyInstanceCode"`
	WithdrawMethod               string  `gorm:"size:64;index:idx_admin_withdrawal_method;not null;default:''" json:"withdrawMethod"`
	WithdrawAddress              string  `gorm:"size:255;not null;default:''" json:"withdrawAddress"`
	FreezeCommandID              string  `gorm:"size:128;not null;default:''" json:"freezeCommandId"`
	FreezeTransactionID          string  `gorm:"size:128;not null;default:''" json:"freezeTransactionId"`
	AuditCommandID               string  `gorm:"size:128;not null;default:''" json:"auditCommandId"`
	AuditTransactionID           string  `gorm:"size:128;not null;default:''" json:"auditTransactionId"`
	Status                       string  `gorm:"size:32;index:idx_admin_withdrawal_app_status_time;not null;default:pending" json:"status"`
	ApproverUserID               *uint   `gorm:"index:idx_admin_withdrawal_approver" json:"approverUserId"`
	ApproverName                 string  `gorm:"size:64;not null;default:''" json:"approverName"`
	AuditRemark                  string  `gorm:"type:text" json:"auditRemark"`
	AuditImageURL                string  `gorm:"column:audit_image_url;size:1024;not null;default:''" json:"auditImageUrl"`
	ApprovedAtMS                 *int64  `gorm:"column:approved_at_ms" json:"approvedAtMs"`
	OperationsStatus             string  `gorm:"column:operations_status;size:32;not null;default:pending;index:idx_admin_withdrawal_app_operations_status_time,priority:2" json:"operationsStatus"`
	OperationsReviewerUserID     *uint   `gorm:"column:operations_reviewer_user_id" json:"operationsReviewerUserId"`
	OperationsReviewerName       string  `gorm:"column:operations_reviewer_name;size:64;not null;default:''" json:"operationsReviewerName"`
	OperationsAuditRemark        string  `gorm:"column:operations_audit_remark;type:text" json:"operationsAuditRemark"`
	OperationsAuditCommandID     string  `gorm:"column:operations_audit_command_id;size:128;not null;default:''" json:"operationsAuditCommandId"`
	OperationsAuditTransactionID string  `gorm:"column:operations_audit_transaction_id;size:128;not null;default:''" json:"operationsAuditTransactionId"`
	OperationsReviewedAtMS       *int64  `gorm:"column:operations_reviewed_at_ms" json:"operationsReviewedAtMs"`
	CreatedAtMS                  int64   `gorm:"column:created_at_ms;autoCreateTime:milli;index:idx_admin_withdrawal_app_status_time;index:idx_admin_withdrawal_app_operations_status_time,priority:3" json:"createdAtMs"`
	UpdatedAtMS                  int64   `gorm:"column:updated_at_ms;autoUpdateTime:milli" json:"updatedAtMs"`
}

func (UserWithdrawalApplication) TableName() string {
	return "admin_user_withdrawal_applications"
}

type AdminJob struct {
	ID            uint   `gorm:"primaryKey" json:"id"`
	Type          string `gorm:"size:80;index;not null" json:"type"`
	Status        string `gorm:"size:32;index;not null" json:"status"`
	PayloadJSON   string `gorm:"type:text" json:"payload"`
	ResultJSON    string `gorm:"type:text" json:"result"`
	Error         string `gorm:"type:text" json:"error"`
	Attempt       int    `gorm:"not null;default:0" json:"attempt"`
	MaxAttempts   int    `gorm:"not null;default:3" json:"maxAttempts"`
	LockedBy      string `gorm:"size:120;index;not null;default:''" json:"lockedBy"`
	LockedUntilMS *int64 `gorm:"column:locked_until_ms;index" json:"lockedUntilMs"`
	ArtifactPath  string `gorm:"size:255;not null;default:''" json:"artifactPath"`
	CreatedBy     uint   `gorm:"index;not null" json:"createdBy"`
	CreatedByName string `gorm:"size:64;index;not null" json:"createdByName"`
	StartedAtMS   *int64 `gorm:"column:started_at_ms" json:"startedAtMs"`
	FinishedAtMS  *int64 `gorm:"column:finished_at_ms" json:"finishedAtMs"`
	CreatedAtMS   int64  `gorm:"column:created_at_ms;autoCreateTime:milli" json:"createdAtMs"`
	UpdatedAtMS   int64  `gorm:"column:updated_at_ms;autoUpdateTime:milli" json:"updatedAtMs"`
}

func (AdminJob) TableName() string {
	return "admin_jobs"
}
