feat: sync account deletion lifecycle
Sync telesrv 73f5c91 (feat(account): implement unified account deletion lifecycle). Skipped telesrv docs changes per public sync rules.
This commit is contained in:
parent
96a419b565
commit
edb7057757
32 changed files with 3236 additions and 88 deletions
|
|
@ -174,7 +174,12 @@ func DefaultAccountReactionSettings() AccountReactionSettings {
|
|||
}
|
||||
|
||||
// DefaultAccountTTLDays 是账号自毁默认期限(无显式设置时)。与历史固定回显一致。
|
||||
const DefaultAccountTTLDays = 365
|
||||
const (
|
||||
DefaultAccountTTLDays = 365
|
||||
// MaxAccountTTLDays prevents an untrusted int32 TL value from producing an
|
||||
// out-of-range PostgreSQL interval/timestamp during deadline maintenance.
|
||||
MaxAccountTTLDays = 3650
|
||||
)
|
||||
|
||||
// GlobalPrivacy 是 globalPrivacySettings 的业务层表达(账号级隐私开关)。
|
||||
// DisallowedGifts 依赖礼物资产模型(当前未实现),故不建模、保持默认。
|
||||
|
|
@ -212,7 +217,7 @@ func DefaultAccountSettings() AccountSettings {
|
|||
|
||||
// NormalizedTTLDays 返回钳制后的账号自毁期限(0/越界回落默认)。
|
||||
func (s AccountSettings) NormalizedTTLDays() int {
|
||||
if s.AccountTTLDays <= 0 {
|
||||
if s.AccountTTLDays <= 0 || s.AccountTTLDays > MaxAccountTTLDays {
|
||||
return DefaultAccountTTLDays
|
||||
}
|
||||
return s.AccountTTLDays
|
||||
|
|
|
|||
99
internal/domain/account_deletion.go
Normal file
99
internal/domain/account_deletion.go
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrAccountDeleted = errors.New("account deleted")
|
||||
ErrAccountDeletionForbidden = errors.New("account deletion forbidden")
|
||||
ErrAccountDeletionHashInvalid = errors.New("account deletion hash invalid")
|
||||
ErrAccountDeletionNotPending = errors.New("account deletion not pending")
|
||||
)
|
||||
|
||||
// AccountDeletionSource is the single audited reason attached to a user
|
||||
// tombstone. Different entry points share one execution and cleanup path.
|
||||
type AccountDeletionSource string
|
||||
|
||||
const (
|
||||
AccountDeletionManual AccountDeletionSource = "manual"
|
||||
AccountDeletionForgotPassword AccountDeletionSource = "forgot_password"
|
||||
AccountDeletionTOSDecline AccountDeletionSource = "tos_decline"
|
||||
AccountDeletionPasswordResetExpiry AccountDeletionSource = "password_reset_expiry"
|
||||
AccountDeletionAccountTTL AccountDeletionSource = "account_ttl"
|
||||
AccountDeletionFreezeExpiry AccountDeletionSource = "freeze_expiry"
|
||||
)
|
||||
|
||||
type AccountDeletionRequestState string
|
||||
|
||||
const (
|
||||
AccountDeletionPending AccountDeletionRequestState = "pending"
|
||||
AccountDeletionCancelled AccountDeletionRequestState = "cancelled"
|
||||
AccountDeletionExecuted AccountDeletionRequestState = "executed"
|
||||
)
|
||||
|
||||
// AccountDeletionRequest represents the seven-day 2FA confirmation window.
|
||||
// ConfirmHashDigest is SHA-256(raw link token); the raw token is only included
|
||||
// in the durable service message and is never persisted as a credential.
|
||||
type AccountDeletionRequest struct {
|
||||
ID int64
|
||||
UserID int64
|
||||
RequesterAuthKeyID [8]byte
|
||||
State AccountDeletionRequestState
|
||||
Reason string
|
||||
ConfirmHashDigest [32]byte
|
||||
RequestedAt time.Time
|
||||
ExecuteAt time.Time
|
||||
CompletedAt time.Time
|
||||
}
|
||||
|
||||
type AccountDeletionSnapshot struct {
|
||||
User User
|
||||
HasPassword bool
|
||||
PasswordUpdatedAt time.Time
|
||||
Pending *AccountDeletionRequest
|
||||
}
|
||||
|
||||
type ScheduleAccountDeletion struct {
|
||||
UserID int64
|
||||
RequesterAuthKeyID [8]byte
|
||||
Reason string
|
||||
ConfirmHashDigest [32]byte
|
||||
ServiceMessage string
|
||||
RequestedAt time.Time
|
||||
ExecuteAt time.Time
|
||||
}
|
||||
|
||||
type AccountDeletionResult struct {
|
||||
User User
|
||||
Changed bool
|
||||
RevokedAuthorizations []Authorization
|
||||
}
|
||||
|
||||
type AccountDeleteKind string
|
||||
|
||||
const (
|
||||
AccountDeleteImmediate AccountDeleteKind = "immediate"
|
||||
AccountDeleteDelayed AccountDeleteKind = "delayed"
|
||||
)
|
||||
|
||||
type AccountDeleteOutcome struct {
|
||||
Kind AccountDeleteKind
|
||||
WaitSeconds int
|
||||
ExecuteAt time.Time
|
||||
Deletion AccountDeletionResult
|
||||
}
|
||||
|
||||
type AccountDeletionCandidate struct {
|
||||
UserID int64
|
||||
Source AccountDeletionSource
|
||||
DueAt time.Time
|
||||
}
|
||||
|
||||
type AccountDeletionNotification struct {
|
||||
ID int64
|
||||
TargetUserID int64
|
||||
DeletedUserID int64
|
||||
Attempts int
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
package domain
|
||||
|
||||
import "time"
|
||||
|
||||
// UserIDSequenceBase 是普通用户 ID 的起始值。
|
||||
//
|
||||
// 取 2026-06-01 00:00:00 Asia/Shanghai 的 Unix 秒级时间戳。
|
||||
|
|
@ -63,6 +65,15 @@ type User struct {
|
|||
PhotoHasVideo bool
|
||||
LastSeenAt int
|
||||
Status UserStatus
|
||||
// Deleted is the durable tombstone state. Deleted users remain addressable by
|
||||
// ID so historical messages can render "Deleted Account", but all profile
|
||||
// and reusable identity fields are cleared at the store boundary.
|
||||
Deleted bool
|
||||
DeletedAt int64
|
||||
DeletionSource AccountDeletionSource
|
||||
DeletionReason string
|
||||
CreatedAt time.Time
|
||||
AccountDeleteAt time.Time
|
||||
}
|
||||
|
||||
// PremiumActiveAt 报告用户在 now(Unix 秒)时刻是否为有效会员。
|
||||
|
|
@ -81,6 +92,25 @@ func (u User) EmojiStatusActiveAt(now int64) bool {
|
|||
return u.EmojiStatusUntil == 0 || int64(u.EmojiStatusUntil) > now
|
||||
}
|
||||
|
||||
// DeletedTombstone strips every viewer-dependent or personally identifying
|
||||
// field while preserving the immutable id and lifecycle audit facts.
|
||||
func (u User) DeletedTombstone() User {
|
||||
if !u.Deleted {
|
||||
return u
|
||||
}
|
||||
return User{
|
||||
ID: u.ID,
|
||||
AccessHash: u.AccessHash,
|
||||
Deleted: true,
|
||||
DeletedAt: u.DeletedAt,
|
||||
DeletionSource: u.DeletionSource,
|
||||
DeletionReason: u.DeletionReason,
|
||||
CreatedAt: u.CreatedAt,
|
||||
AccountDeleteAt: u.AccountDeleteAt,
|
||||
Status: UserStatus{Kind: UserStatusEmpty},
|
||||
}
|
||||
}
|
||||
|
||||
// UserStatusKind is a protocol-neutral account presence state.
|
||||
type UserStatusKind int
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue