Files
past-go/past-go/internal/model/models.go
T
Blizzard 8be6d9b1ab feat: add account deletion, announcements and subscription renewal
Support personal records and persistent smoking visibility. Add upload quota preflight, file cleanup, privacy entry, image caching and revision-based page refresh across the backend, admin and mini program.
2026-11-03 22:06:47 +08:00

507 lines
36 KiB
Go

// Package model defines the PostgreSQL schema shared by all repositories.
package model
import (
"gorm.io/gorm"
"time"
)
// BaseModel supplies identity, automatic timestamps and GORM soft deletion.
type BaseModel struct {
Id string `gorm:"type:uuid;primaryKey;default:gen_random_uuid()" json:"id"`
CreatedAt time.Time `gorm:"not null;default:CURRENT_TIMESTAMP" json:"created_at"`
UpdatedAt time.Time `gorm:"not null;default:CURRENT_TIMESTAMP" json:"updated_at"`
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
}
type UnbindingFileCleanup struct {
BaseModel
CoupleId string `gorm:"column:couple_id;type:uuid;not null" json:"couple_id"`
Path string `gorm:"column:path;type:varchar(500);not null" json:"path"`
}
type User struct {
BaseModel
UserId string `gorm:"column:user_id;type:varchar(20);uniqueIndex:ux_users_user_id,where:deleted_at IS NULL" json:"user_id"`
Openid string `gorm:"column:openid;type:varchar(100);uniqueIndex:ux_users_openid,where:deleted_at IS NULL" json:"openid"`
Phone string `gorm:"column:phone;type:varchar(50);default:''" json:"phone"`
Username string `gorm:"column:username;type:varchar(100);default:'';uniqueIndex:ux_users_username,where:deleted_at IS NULL" json:"username"`
Password string `gorm:"column:password;type:varchar(255);default:''" json:"password"`
Nickname string `gorm:"column:nickname;type:varchar(100);default:''" json:"nickname"`
Avatar string `gorm:"column:avatar;type:varchar(500);default:''" json:"avatar"`
Gender string `gorm:"column:gender;type:varchar(10);default:''" json:"gender"`
CoupleId *string `gorm:"column:couple_id;type:uuid" json:"couple_id"`
LastLoginAt *time.Time `gorm:"column:last_login_at;type:timestamptz" json:"last_login_at"`
PeriodVisible int64 `gorm:"column:period_visible;type:bigint;not null;default:1" json:"period_visible"`
Age int64 `gorm:"column:age;type:bigint;default:0" json:"age"`
Birthday string `gorm:"column:birthday;type:varchar(20);default:''" json:"birthday"`
Region string `gorm:"column:region;type:varchar(100);default:''" json:"region"`
Location string `gorm:"column:location;type:varchar(200);default:''" json:"location"`
Lat float64 `gorm:"column:lat;type:numeric(10,6);default:0" json:"lat"`
Lng float64 `gorm:"column:lng;type:numeric(10,6);default:0" json:"lng"`
LastLoginLocation string `gorm:"column:last_login_location;type:varchar(200);default:''" json:"last_login_location"`
IpGeoDate *string `gorm:"column:ip_geo_date;type:date" json:"ip_geo_date"`
LastIp string `gorm:"column:last_ip;type:varchar(64);default:''" json:"last_ip"`
SessionKey string `gorm:"column:session_key;type:varchar(128);default:''" json:"session_key"`
StorageVipExpireAt *time.Time `gorm:"column:storage_vip_expire_at;type:timestamptz" json:"storage_vip_expire_at"`
StorageQuotaMb int64 `gorm:"column:storage_quota_mb;type:bigint;default:0" json:"storage_quota_mb"`
StoragePlanName string `gorm:"type:varchar(80);not null;default:''" json:"storage_plan_name"`
StoragePlan string `gorm:"column:storage_plan;type:varchar(64);default:''" json:"storage_plan"`
StoragePlanStartedAt *time.Time `gorm:"column:storage_plan_started_at;type:timestamptz" json:"storage_plan_started_at"`
SmokingCardVisible int64 `gorm:"column:smoking_card_visible;type:bigint;not null;default:1" json:"smoking_card_visible"`
AvatarFileId *string `gorm:"type:uuid;index" json:"avatar_file_id"`
AvatarFileIdRecord *File `gorm:"foreignKey:AvatarFileId;references:Id;constraint:OnUpdate:CASCADE,OnDelete:RESTRICT" json:"-"`
}
type Couple struct {
BaseModel
CoupleSpaceId string `gorm:"column:couple_space_id;type:varchar(50);uniqueIndex:ux_couples_couple_space_id,where:deleted_at IS NULL" json:"couple_space_id"`
User1Id string `gorm:"column:user1_id;type:uuid;not null" json:"user1_id"`
User2Id string `gorm:"column:user2_id;type:uuid;not null" json:"user2_id"`
LoveStartDate string `gorm:"column:love_start_date;type:varchar(20);default:''" json:"love_start_date"`
LoveStory string `gorm:"column:love_story;type:text" json:"love_story"`
Status string `gorm:"column:status;type:varchar(20);default:'active'" json:"status"`
DataDeleted int64 `gorm:"column:data_deleted;type:bigint;default:0" json:"data_deleted"`
PeriodVisible int64 `gorm:"column:period_visible;type:bigint;default:0" json:"period_visible"`
PeriodSourceUserId *string `gorm:"column:period_source_user_id;type:uuid" json:"period_source_user_id"`
}
type Invitation struct {
BaseModel
FromUserId string `gorm:"column:from_user_id;type:uuid;not null" json:"from_user_id"`
ToUserId *string `gorm:"column:to_user_id;type:uuid" json:"to_user_id"`
ToPhone string `gorm:"column:to_phone;type:varchar(50);default:''" json:"to_phone"`
Status string `gorm:"column:status;type:varchar(20);default:'pending'" json:"status"`
Message string `gorm:"column:message;type:text" json:"message"`
InviteCode string `gorm:"column:invite_code;type:varchar(100);default:''" json:"invite_code"`
Type string `gorm:"column:type;type:varchar(20);default:'phone'" json:"type"`
RespondedAt *time.Time `gorm:"column:responded_at;type:timestamptz" json:"responded_at"`
}
type UnbindRequest struct {
BaseModel
CoupleId string `gorm:"column:couple_id;type:uuid;not null" json:"couple_id"`
RequesterId string `gorm:"column:requester_id;type:uuid;not null" json:"requester_id"`
Status string `gorm:"column:status;type:varchar(20);default:'pending'" json:"status"`
RespondedAt *time.Time `gorm:"column:responded_at;type:timestamptz" json:"responded_at"`
CancelledAt *time.Time `gorm:"column:cancelled_at;type:timestamptz" json:"cancelled_at"`
CancelledBy *string `gorm:"column:cancelled_by;type:uuid" json:"cancelled_by"`
CoolingUntil *time.Time `gorm:"column:cooling_until;type:timestamptz" json:"cooling_until"`
CompletedAt *time.Time `gorm:"column:completed_at;type:timestamptz" json:"completed_at"`
}
type InviteLink struct {
BaseModel
UserId string `gorm:"column:user_id;type:uuid;not null" json:"user_id"`
Code string `gorm:"column:code;type:varchar(100);not null;uniqueIndex:ux_invite_links_code,where:deleted_at IS NULL" json:"code"`
Used int64 `gorm:"column:used;type:bigint;default:0" json:"used"`
}
type Timeline struct {
BaseModel
CoupleId *string `gorm:"column:couple_id;type:uuid;index" json:"couple_id"`
Title string `gorm:"column:title;type:varchar(255);not null" json:"title"`
Content string `gorm:"column:content;type:text" json:"content"`
EventDate string `gorm:"column:event_date;type:varchar(20);not null" json:"event_date"`
Location string `gorm:"column:location;type:varchar(200);default:''" json:"location"`
Images string `gorm:"column:images;type:text" json:"images"`
Icon string `gorm:"column:icon;type:varchar(20);default:'❤️'" json:"icon"`
SortOrder int64 `gorm:"column:sort_order;type:bigint;default:0" json:"sort_order"`
CreatedBy *string `gorm:"column:created_by;type:uuid" json:"created_by"`
RepeatInterval int64 `gorm:"column:repeat_interval;type:bigint;default:0" json:"repeat_interval"`
RepeatUnit string `gorm:"column:repeat_unit;type:varchar(10);default:''" json:"repeat_unit"`
BgImage string `gorm:"column:bg_image;type:varchar(500);default:''" json:"bg_image"`
BgImageFileId *string `gorm:"type:uuid;index" json:"bg_image_file_id"`
BgImageFileIdRecord *File `gorm:"foreignKey:BgImageFileId;references:Id;constraint:OnUpdate:CASCADE,OnDelete:RESTRICT" json:"-"`
}
type Album struct {
BaseModel
CoupleId *string `gorm:"column:couple_id;type:uuid;index" json:"couple_id"`
Title string `gorm:"column:title;type:varchar(255);not null" json:"title"`
Description string `gorm:"column:description;type:text" json:"description"`
Cover string `gorm:"column:cover;type:varchar(500);default:''" json:"cover"`
SortOrder int64 `gorm:"column:sort_order;type:bigint;default:0" json:"sort_order"`
Lat float64 `gorm:"column:lat;type:numeric(10,6);default:0" json:"lat"`
Lng float64 `gorm:"column:lng;type:numeric(10,6);default:0" json:"lng"`
Location string `gorm:"column:location;type:varchar(200);default:''" json:"location"`
CreatedBy *string `gorm:"column:created_by;type:uuid" json:"created_by"`
CoverFileId *string `gorm:"type:uuid;index" json:"cover_file_id"`
CoverFileIdRecord *File `gorm:"foreignKey:CoverFileId;references:Id;constraint:OnUpdate:CASCADE,OnDelete:RESTRICT" json:"-"`
}
type Photo struct {
BaseModel
AlbumId string `gorm:"column:album_id;type:uuid;not null" json:"album_id"`
Path string `gorm:"column:path;type:varchar(500);not null" json:"path"`
Description string `gorm:"column:description;type:text" json:"description"`
TakenAt string `gorm:"column:taken_at;type:varchar(50);default:''" json:"taken_at"`
Location string `gorm:"column:location;type:varchar(200);default:''" json:"location"`
Lat float64 `gorm:"column:lat;type:numeric(10,6);default:0" json:"lat"`
Lng float64 `gorm:"column:lng;type:numeric(10,6);default:0" json:"lng"`
Activity string `gorm:"column:activity;type:varchar(200);default:''" json:"activity"`
BatchId string `gorm:"column:batch_id;type:varchar(50);default:''" json:"batch_id"`
SortOrder int64 `gorm:"column:sort_order;type:bigint;default:0" json:"sort_order"`
CreatedBy *string `gorm:"column:created_by;type:uuid" json:"created_by"`
FileId *string `gorm:"type:uuid;index" json:"file_id"`
FileIdRecord *File `gorm:"foreignKey:FileId;references:Id;constraint:OnUpdate:CASCADE,OnDelete:RESTRICT" json:"-"`
}
type Diary struct {
BaseModel
CoupleId *string `gorm:"column:couple_id;type:uuid;index" json:"couple_id"`
Title string `gorm:"column:title;type:varchar(255);not null" json:"title"`
Content string `gorm:"column:content;type:text" json:"content"`
Mood string `gorm:"column:mood;type:varchar(20);default:'😊'" json:"mood"`
Weather string `gorm:"column:weather;type:varchar(20);default:'☀️'" json:"weather"`
DiaryDate string `gorm:"column:diary_date;type:varchar(20);not null" json:"diary_date"`
Images string `gorm:"column:images;type:text" json:"images"`
IsAnniversary int64 `gorm:"column:is_anniversary;type:bigint;default:0" json:"is_anniversary"`
Region string `gorm:"column:region;type:varchar(100);default:''" json:"region"`
Lat float64 `gorm:"column:lat;type:numeric(10,6);default:0" json:"lat"`
Lng float64 `gorm:"column:lng;type:numeric(10,6);default:0" json:"lng"`
Location string `gorm:"column:location;type:varchar(200);default:''" json:"location"`
CreatedBy *string `gorm:"column:created_by;type:uuid" json:"created_by"`
}
type Song struct {
BaseModel
CoupleId string `gorm:"column:couple_id;type:uuid;not null" json:"couple_id"`
SongName string `gorm:"column:song_name;type:varchar(255);not null" json:"song_name"`
Artist string `gorm:"column:artist;type:varchar(255);not null" json:"artist"`
Note string `gorm:"column:note;type:varchar(120);default:''" json:"note"`
CreatedBy *string `gorm:"column:created_by;type:uuid" json:"created_by"`
}
type Checkin struct {
BaseModel
CoupleId string `gorm:"column:couple_id;type:uuid;not null" json:"couple_id"`
Title string `gorm:"column:title;type:varchar(255);not null" json:"title"`
Icon string `gorm:"column:icon;type:varchar(20);default:'✅'" json:"icon"`
Type string `gorm:"column:type;type:varchar(10);default:'dual'" json:"type"`
Frequency string `gorm:"column:frequency;type:varchar(10);default:'daily'" json:"frequency"`
IsCompleted int64 `gorm:"column:is_completed;type:bigint;default:0" json:"is_completed"`
CompletedByMe int64 `gorm:"column:completed_by_me;type:bigint;default:0" json:"completed_by_me"`
CompletedByPartner int64 `gorm:"column:completed_by_partner;type:bigint;default:0" json:"completed_by_partner"`
MyCheckinTime *time.Time `gorm:"column:my_checkin_time;type:timestamptz" json:"my_checkin_time"`
PartnerCheckinTime *time.Time `gorm:"column:partner_checkin_time;type:timestamptz" json:"partner_checkin_time"`
MyStreak int64 `gorm:"column:my_streak;type:bigint;default:0" json:"my_streak"`
PartnerStreak int64 `gorm:"column:partner_streak;type:bigint;default:0" json:"partner_streak"`
MyPhoto string `gorm:"column:my_photo;type:varchar(500);default:''" json:"my_photo"`
PartnerPhoto string `gorm:"column:partner_photo;type:varchar(500);default:''" json:"partner_photo"`
MyNote string `gorm:"column:my_note;type:varchar(200);default:''" json:"my_note"`
PartnerNote string `gorm:"column:partner_note;type:varchar(200);default:''" json:"partner_note"`
CompletedAt *time.Time `gorm:"column:completed_at;type:timestamptz" json:"completed_at"`
SortOrder int64 `gorm:"column:sort_order;type:bigint;default:0" json:"sort_order"`
CreatedBy *string `gorm:"column:created_by;type:uuid" json:"created_by"`
MyPhotoFileId *string `gorm:"type:uuid;index" json:"my_photo_file_id"`
MyPhotoFileIdRecord *File `gorm:"foreignKey:MyPhotoFileId;references:Id;constraint:OnUpdate:CASCADE,OnDelete:RESTRICT" json:"-"`
PartnerPhotoFileId *string `gorm:"type:uuid;index" json:"partner_photo_file_id"`
PartnerPhotoFileIdRecord *File `gorm:"foreignKey:PartnerPhotoFileId;references:Id;constraint:OnUpdate:CASCADE,OnDelete:RESTRICT" json:"-"`
}
type CheckinLog struct {
BaseModel
CheckinId string `gorm:"column:checkin_id;type:uuid;not null" json:"checkin_id"`
CoupleId string `gorm:"column:couple_id;type:uuid;not null" json:"couple_id"`
UserId string `gorm:"column:user_id;type:uuid;not null" json:"user_id"`
Photo string `gorm:"column:photo;type:varchar(500);default:''" json:"photo"`
Note string `gorm:"column:note;type:varchar(200);default:''" json:"note"`
PhotoFileId *string `gorm:"type:uuid;index" json:"photo_file_id"`
PhotoFileIdRecord *File `gorm:"foreignKey:PhotoFileId;references:Id;constraint:OnUpdate:CASCADE,OnDelete:RESTRICT" json:"-"`
}
type SmokingAccount struct {
BaseModel
CoupleId string `gorm:"column:couple_id;type:uuid;not null;uniqueIndex:ix_smoking_accounts_smoking_owner,where:deleted_at IS NULL,priority:1" json:"couple_id"`
UserId string `gorm:"column:user_id;type:uuid;not null;uniqueIndex:ix_smoking_accounts_smoking_owner,where:deleted_at IS NULL,priority:2" json:"user_id"`
Stock int64 `gorm:"column:stock;type:bigint;not null;default:0" json:"stock"`
CheckinId *string `gorm:"column:checkin_id;type:uuid" json:"checkin_id"`
}
type SmokingLog struct {
BaseModel
AccountId string `gorm:"column:account_id;type:uuid;not null;uniqueIndex:ix_smoking_logs_smoking_request,where:deleted_at IS NULL,priority:1;index:ix_smoking_logs_smoking_recent,priority:1" json:"account_id"`
Kind string `gorm:"column:kind;type:varchar(20);not null" json:"kind"`
Quantity int64 `gorm:"column:quantity;type:bigint;not null" json:"quantity"`
Delta int64 `gorm:"column:delta;type:bigint;not null" json:"delta"`
Balance int64 `gorm:"column:balance;type:bigint;not null" json:"balance"`
AmountCents int64 `gorm:"column:amount_cents;type:bigint;not null;default:0" json:"amount_cents"`
Note string `gorm:"column:note;type:varchar(200);not null;default:''" json:"note"`
RequestKey string `gorm:"column:request_key;type:varchar(64);not null;uniqueIndex:ix_smoking_logs_smoking_request,where:deleted_at IS NULL,priority:2" json:"request_key"`
PayloadHash string `gorm:"column:payload_hash;type:char(64);not null" json:"payload_hash"`
CheckinLogId *string `gorm:"column:checkin_log_id;type:uuid" json:"checkin_log_id"`
OriginalId *string `gorm:"column:original_id;type:uuid" json:"original_id"`
Undone int64 `gorm:"column:undone;type:bigint;not null;default:0" json:"undone"`
}
type PeriodRecord struct {
BaseModel
CoupleId *string `gorm:"column:couple_id;type:uuid" json:"couple_id"`
UserId string `gorm:"column:user_id;type:uuid;not null" json:"user_id"`
StartDate string `gorm:"column:start_date;type:date;not null" json:"start_date"`
EndDate string `gorm:"column:end_date;type:date;not null" json:"end_date"`
CycleLength int64 `gorm:"column:cycle_length;type:bigint;default:28" json:"cycle_length"`
PeriodLength int64 `gorm:"column:period_length;type:bigint;default:5" json:"period_length"`
Symptoms string `gorm:"column:symptoms;type:varchar(500);default:''" json:"symptoms"`
Note string `gorm:"column:note;type:text" json:"note"`
}
type PeriodDailyRecord struct {
BaseModel
CoupleId *string `gorm:"column:couple_id;type:uuid;uniqueIndex:ix_period_daily_records_uk_date_couple,where:deleted_at IS NULL,priority:2" json:"couple_id"`
UserId string `gorm:"column:user_id;type:uuid;not null" json:"user_id"`
RecordDate string `gorm:"column:record_date;type:date;not null;uniqueIndex:ix_period_daily_records_uk_date_couple,where:deleted_at IS NULL,priority:1" json:"record_date"`
Period string `gorm:"column:period;type:varchar(1);default:'0'" json:"period"`
Sex string `gorm:"column:sex;type:varchar(1);default:'0'" json:"sex"`
Symptoms string `gorm:"column:symptoms;type:varchar(200);default:''" json:"symptoms"`
Mood string `gorm:"column:mood;type:varchar(100);default:''" json:"mood"`
Discharge string `gorm:"column:discharge;type:varchar(100);default:''" json:"discharge"`
Temperature *float64 `gorm:"column:temperature;type:numeric(4,1)" json:"temperature"`
Weight *float64 `gorm:"column:weight;type:numeric(5,1)" json:"weight"`
Note string `gorm:"column:note;type:text" json:"note"`
PeriodDetail string `gorm:"column:period_detail;type:text" json:"period_detail"`
}
type PeriodSexRecord struct {
BaseModel
CoupleId *string `gorm:"column:couple_id;type:uuid;index:ix_period_sex_records_idx_date_couple,priority:2" json:"couple_id"`
UserId string `gorm:"column:user_id;type:uuid;not null" json:"user_id"`
RecordDate string `gorm:"column:record_date;type:date;not null;index:ix_period_sex_records_idx_date_couple,priority:1" json:"record_date"`
RecordTime *string `gorm:"column:record_time;type:time without time zone" json:"record_time"`
Measure string `gorm:"column:measure;type:varchar(100);default:''" json:"measure"`
MeasureCustom string `gorm:"column:measure_custom;type:varchar(200);default:''" json:"measure_custom"`
}
type Setting struct {
BaseModel
Key string `gorm:"column:key;type:varchar(255);uniqueIndex:ux_settings_key,where:deleted_at IS NULL" json:"key"`
Value string `gorm:"column:value;type:text" json:"value"`
FileId *string `gorm:"type:uuid;index" json:"file_id"`
FileIdRecord *File `gorm:"foreignKey:FileId;references:Id;constraint:OnUpdate:CASCADE,OnDelete:RESTRICT" json:"-"`
}
type DiaryComment struct {
BaseModel
DiaryId string `gorm:"column:diary_id;type:uuid;not null;index:ix_diary_comments_idx_diary,priority:1" json:"diary_id"`
CoupleId string `gorm:"column:couple_id;type:uuid;not null" json:"couple_id"`
UserId string `gorm:"column:user_id;type:uuid;not null" json:"user_id"`
Content string `gorm:"column:content;type:text;not null" json:"content"`
ParentId *string `gorm:"column:parent_id;type:uuid" json:"parent_id"`
ReplyToUserId *string `gorm:"column:reply_to_user_id;type:uuid" json:"reply_to_user_id"`
ReplyToUsername string `gorm:"column:reply_to_username;type:varchar(100);default:''" json:"reply_to_username"`
}
type SubscribeTemplate struct {
BaseModel
UserId string `gorm:"column:user_id;type:uuid;not null;uniqueIndex:ix_subscribe_templates_uk_user_template,where:deleted_at IS NULL,priority:1" json:"user_id"`
TemplateId string `gorm:"column:template_id;type:varchar(100);not null" json:"template_id"`
TemplateType string `gorm:"column:template_type;type:varchar(50);not null;uniqueIndex:ix_subscribe_templates_uk_user_template,where:deleted_at IS NULL,priority:2" json:"template_type"`
}
type ActivityLog struct {
BaseModel
CoupleId string `gorm:"column:couple_id;type:uuid;not null;index:ix_activity_log_idx_couple_time,priority:1" json:"couple_id"`
UserId string `gorm:"column:user_id;type:uuid;not null" json:"user_id"`
ActionType string `gorm:"column:action_type;type:varchar(50);not null" json:"action_type"`
Description string `gorm:"column:description;type:varchar(500);not null" json:"description"`
Detail string `gorm:"column:detail;type:text" json:"detail"`
}
type Admin struct {
BaseModel
Username string `gorm:"column:username;type:varchar(100);not null;uniqueIndex:ux_admins_username,where:deleted_at IS NULL" json:"username"`
Password string `gorm:"column:password;type:varchar(255);not null" json:"password"`
}
type UserToken struct {
BaseModel
Token string `gorm:"column:token;type:varchar(128);not null;uniqueIndex:ux_user_tokens_token,where:deleted_at IS NULL;index:ix_user_tokens_idx_token,priority:1" json:"token"`
UserId string `gorm:"column:user_id;type:uuid;not null" json:"user_id"`
ExpiresAt *time.Time `gorm:"column:expires_at;type:timestamptz;not null;index:ix_user_tokens_idx_expires,priority:1" json:"expires_at"`
}
type SubscribeMessageLog struct {
BaseModel
ActivityId *string `gorm:"column:activity_id;type:uuid;index:ix_subscribe_message_logs_idx_activity,priority:1" json:"activity_id"`
CoupleId *string `gorm:"column:couple_id;type:uuid;index:ix_subscribe_message_logs_idx_couple,priority:1" json:"couple_id"`
UserId *string `gorm:"column:user_id;type:uuid" json:"user_id"`
ToUserId string `gorm:"column:to_user_id;type:uuid;not null;index:ix_subscribe_message_logs_idx_recipient_time,priority:1" json:"to_user_id"`
ToOpenid string `gorm:"column:to_openid;type:varchar(128);not null;default:'';index:ix_subscribe_message_logs_idx_openid,priority:1" json:"to_openid"`
ActionType string `gorm:"column:action_type;type:varchar(50);not null;default:'';index:ix_subscribe_message_logs_idx_event_time,priority:1" json:"action_type"`
MessageTitle string `gorm:"column:message_title;type:varchar(255);not null;default:''" json:"message_title"`
MessageContent string `gorm:"column:message_content;type:text" json:"message_content"`
TemplateType string `gorm:"column:template_type;type:varchar(50);not null" json:"template_type"`
TemplateId string `gorm:"column:template_id;type:varchar(128);not null;default:''" json:"template_id"`
Page string `gorm:"column:page;type:varchar(255);not null;default:''" json:"page"`
Status string `gorm:"column:status;type:varchar(20);not null;index:ix_subscribe_message_logs_idx_status_time,priority:1" json:"status"`
ErrorCode *int64 `gorm:"column:error_code;type:bigint" json:"error_code"`
HttpStatus *int64 `gorm:"column:http_status;type:bigint" json:"http_status"`
ErrorMsg string `gorm:"column:error_msg;type:text" json:"error_msg"`
Attempts int64 `gorm:"column:attempts;type:bigint;not null;default:0" json:"attempts"`
DurationMs int64 `gorm:"column:duration_ms;type:bigint;not null;default:0" json:"duration_ms"`
SentAt *time.Time `gorm:"column:sent_at;type:timestamptz" json:"sent_at"`
}
type NotifyLog struct {
BaseModel
ActivityId string `gorm:"column:activity_id;type:uuid;not null;uniqueIndex:ix_notify_log_uk_activity,where:deleted_at IS NULL,priority:1" json:"activity_id"`
CoupleId string `gorm:"column:couple_id;type:uuid;not null" json:"couple_id"`
UserId string `gorm:"column:user_id;type:uuid;not null" json:"user_id"`
ActionType string `gorm:"column:action_type;type:varchar(50);not null" json:"action_type"`
TemplateType string `gorm:"column:template_type;type:varchar(50);not null" json:"template_type"`
ToUserId string `gorm:"column:to_user_id;type:uuid;not null;uniqueIndex:ix_notify_log_uk_activity,where:deleted_at IS NULL,priority:2" json:"to_user_id"`
Status string `gorm:"column:status;type:varchar(20);not null;default:'';index:ix_notify_log_idx_status_time,priority:1" json:"status"`
ErrorMsg string `gorm:"column:error_msg;type:text" json:"error_msg"`
SentAt *time.Time `gorm:"column:sent_at;type:timestamptz" json:"sent_at"`
}
type Pet struct {
BaseModel
CoupleId string `gorm:"column:couple_id;type:uuid;not null;uniqueIndex:ux_pets_couple_id,where:deleted_at IS NULL" json:"couple_id"`
Name string `gorm:"column:name;type:varchar(50);not null;default:'小狗狗'" json:"name"`
Breed string `gorm:"column:breed;type:varchar(20);not null;default:'golden'" json:"breed"`
Gender int64 `gorm:"column:gender;type:bigint;not null;default:1" json:"gender"`
Stage int64 `gorm:"column:stage;type:bigint;not null;default:1" json:"stage"`
Level int64 `gorm:"column:level;type:bigint;not null;default:1" json:"level"`
Exp int64 `gorm:"column:exp;type:bigint;not null;default:0" json:"exp"`
Happiness int64 `gorm:"column:happiness;type:bigint;not null;default:100" json:"happiness"`
Hunger int64 `gorm:"column:hunger;type:bigint;not null;default:100" json:"hunger"`
Energy int64 `gorm:"column:energy;type:bigint;not null;default:100" json:"energy"`
Cleanliness int64 `gorm:"column:cleanliness;type:bigint;not null;default:100" json:"cleanliness"`
AdoptedAt string `gorm:"column:adopted_at;type:date;not null" json:"adopted_at"`
CreatedBy string `gorm:"column:created_by;type:uuid;not null" json:"created_by"`
}
type PetDaily struct {
BaseModel
CoupleId string `gorm:"column:couple_id;type:uuid;not null;uniqueIndex:ix_pet_dailies_uk_couple_date,where:deleted_at IS NULL,priority:1" json:"couple_id"`
Date string `gorm:"column:date;type:date;not null;uniqueIndex:ix_pet_dailies_uk_couple_date,where:deleted_at IS NULL,priority:2" json:"date"`
FeedU1 int64 `gorm:"column:feed_u1;type:bigint;default:0" json:"feed_u1"`
FeedU2 int64 `gorm:"column:feed_u2;type:bigint;default:0" json:"feed_u2"`
WaterU1 int64 `gorm:"column:water_u1;type:bigint;default:0" json:"water_u1"`
WaterU2 int64 `gorm:"column:water_u2;type:bigint;default:0" json:"water_u2"`
WalkU1 int64 `gorm:"column:walk_u1;type:bigint;default:0" json:"walk_u1"`
WalkU2 int64 `gorm:"column:walk_u2;type:bigint;default:0" json:"walk_u2"`
BathU1 int64 `gorm:"column:bath_u1;type:bigint;default:0" json:"bath_u1"`
BathU2 int64 `gorm:"column:bath_u2;type:bigint;default:0" json:"bath_u2"`
SleepU1 int64 `gorm:"column:sleep_u1;type:bigint;default:0" json:"sleep_u1"`
SleepU2 int64 `gorm:"column:sleep_u2;type:bigint;default:0" json:"sleep_u2"`
PlayU1 int64 `gorm:"column:play_u1;type:bigint;default:0" json:"play_u1"`
PlayU2 int64 `gorm:"column:play_u2;type:bigint;default:0" json:"play_u2"`
AllDone int64 `gorm:"column:all_done;type:bigint;default:0" json:"all_done"`
}
type PetAchievement struct {
BaseModel
CoupleId string `gorm:"column:couple_id;type:uuid;not null;uniqueIndex:ix_pet_achievements_uk_couple_ach,where:deleted_at IS NULL,priority:1" json:"couple_id"`
AchievementKey string `gorm:"column:achievement_key;type:varchar(50);not null;uniqueIndex:ix_pet_achievements_uk_couple_ach,where:deleted_at IS NULL,priority:2" json:"achievement_key"`
UnlockedAt *time.Time `gorm:"column:unlocked_at;type:timestamptz;default:CURRENT_TIMESTAMP" json:"unlocked_at"`
}
type PetItem struct {
BaseModel
CoupleId string `gorm:"column:couple_id;type:uuid;not null;index:ix_pet_items_idx_couple,priority:1" json:"couple_id"`
ItemKey string `gorm:"column:item_key;type:varchar(50);not null" json:"item_key"`
ItemType string `gorm:"column:item_type;type:varchar(20);not null" json:"item_type"`
Quantity int64 `gorm:"column:quantity;type:bigint;default:1" json:"quantity"`
Equipped int64 `gorm:"column:equipped;type:bigint;default:0" json:"equipped"`
}
type PetDynamic struct {
BaseModel
CoupleId string `gorm:"column:couple_id;type:uuid;not null;index:ix_pet_dynamics_idx_couple_time,priority:1" json:"couple_id"`
UserId *string `gorm:"column:user_id;type:uuid" json:"user_id"`
Action string `gorm:"column:action;type:varchar(30);not null" json:"action"`
Message string `gorm:"column:message;type:text" json:"message"`
}
type StoragePlan struct {
BaseModel
Key string `gorm:"type:varchar(64);not null;uniqueIndex" json:"key"`
Name string `gorm:"type:varchar(80);not null" json:"name"`
QuotaMb int64 `gorm:"not null" json:"quota_mb"` // 0 = unlimited
Months int64 `gorm:"not null" json:"months"`
PriceCents int64 `gorm:"not null" json:"price_cents"`
Enabled bool `gorm:"not null" json:"enabled"`
SortOrder int64 `gorm:"not null;default:0" json:"sort_order"`
}
// Entitlements are immutable purchase snapshots; different capacities expire independently.
type StorageEntitlement struct {
BaseModel
UserId string `gorm:"type:uuid;not null;index" json:"user_id"`
OrderId *string `gorm:"type:uuid;uniqueIndex" json:"order_id"`
SourceKey string `gorm:"type:varchar(100);uniqueIndex;not null" json:"source_key"`
PlanKey string `gorm:"type:varchar(64);not null" json:"plan_key"`
PlanName string `gorm:"type:varchar(80);not null" json:"plan_name"`
QuotaMb int64 `gorm:"not null" json:"quota_mb"`
StartsAt time.Time `gorm:"type:timestamptz;not null" json:"starts_at"`
ExpiresAt time.Time `gorm:"type:timestamptz;not null;index" json:"expires_at"`
}
type StorageOrder struct {
PlanName string `gorm:"type:varchar(80);not null;default:''" json:"plan_name"`
PlanQuotaMb int64 `gorm:"not null;default:0" json:"plan_quota_mb"`
PlanMonths int64 `gorm:"not null;default:0" json:"plan_months"`
BaseModel
PayProvider string `gorm:"type:varchar(20);not null;default:'wechat'" json:"pay_provider"`
PayAppId string `gorm:"type:varchar(64);not null;default:''" json:"pay_app_id"`
PayMchId string `gorm:"type:varchar(32);not null;default:''" json:"pay_mch_id"`
PayerOpenId string `gorm:"type:varchar(128);not null;default:''" json:"payer_open_id"`
PrepayId string `gorm:"type:varchar(128);not null;default:''" json:"prepay_id"`
ExpiresAt *time.Time `gorm:"type:timestamptz" json:"expires_at"`
UserId string `gorm:"column:user_id;type:uuid;not null;index:ix_storage_orders_idx_user,priority:1" json:"user_id"`
CoupleId *string `gorm:"column:couple_id;type:uuid" json:"couple_id"`
OutTradeNo string `gorm:"column:out_trade_no;type:varchar(64);not null;uniqueIndex:ux_storage_orders_out_trade_no,where:deleted_at IS NULL" json:"out_trade_no"`
PlanKey string `gorm:"column:plan_key;type:varchar(64);not null" json:"plan_key"`
AmountCents int64 `gorm:"column:amount_cents;type:bigint;not null;default:0" json:"amount_cents"`
Status string `gorm:"column:status;type:varchar(20);not null;default:'pending';index:ix_storage_orders_idx_status,priority:1" json:"status"`
WxOrderId string `gorm:"column:wx_order_id;type:varchar(64);not null;default:''" json:"wx_order_id"`
Attach string `gorm:"column:attach;type:text" json:"attach"`
PaidAt *time.Time `gorm:"column:paid_at;type:timestamptz" json:"paid_at"`
}
type DiaryLike struct {
BaseModel
DiaryId string `gorm:"column:diary_id;type:uuid;not null;uniqueIndex:ix_diary_likes_diary_user,where:deleted_at IS NULL,priority:1" json:"diary_id"`
UserId string `gorm:"column:user_id;type:uuid;not null;uniqueIndex:ix_diary_likes_diary_user,where:deleted_at IS NULL,priority:2" json:"user_id"`
}
type DiaryFavorite struct {
BaseModel
DiaryId string `gorm:"column:diary_id;type:uuid;not null;uniqueIndex:ix_diary_favorites_diary_user,where:deleted_at IS NULL,priority:1" json:"diary_id"`
UserId string `gorm:"column:user_id;type:uuid;not null;uniqueIndex:ix_diary_favorites_diary_user,where:deleted_at IS NULL,priority:2" json:"user_id"`
}
type SmokingWallet struct {
BaseModel
UserId string `gorm:"column:user_id;type:uuid;not null;uniqueIndex:ux_smoking_wallets_user_id,where:deleted_at IS NULL" json:"user_id"`
Stock int64 `gorm:"column:stock;type:bigint;not null;default:0" json:"stock"`
}
type SmokingEntry struct {
BaseModel
UserId string `gorm:"column:user_id;type:uuid;not null;uniqueIndex:ix_smoking_entries_personal_request,where:deleted_at IS NULL,priority:1;index:ix_smoking_entries_personal_recent,priority:1" json:"user_id"`
Kind string `gorm:"column:kind;type:varchar(20);not null" json:"kind"`
Quantity int64 `gorm:"column:quantity;type:bigint;not null" json:"quantity"`
Delta int64 `gorm:"column:delta;type:bigint;not null" json:"delta"`
Balance int64 `gorm:"column:balance;type:bigint;not null" json:"balance"`
AmountCents int64 `gorm:"column:amount_cents;type:bigint;not null;default:0" json:"amount_cents"`
Note string `gorm:"column:note;type:varchar(200);not null;default:''" json:"note"`
RequestKey string `gorm:"column:request_key;type:varchar(64);not null;uniqueIndex:ix_smoking_entries_personal_request,where:deleted_at IS NULL,priority:2" json:"request_key"`
PayloadHash string `gorm:"column:payload_hash;type:char(64);not null" json:"payload_hash"`
OriginalId *string `gorm:"column:original_id;type:uuid" json:"original_id"`
Undone int64 `gorm:"column:undone;type:bigint;not null;default:0" json:"undone"`
}
// Models is the complete AutoMigrate registry.
func Models() []any {
return []any{&FileDeletion{}, &File{}, &FileUpload{}, &RecordFile{}, &UnbindingFileCleanup{}, &User{}, &Couple{}, &Invitation{}, &UnbindRequest{}, &InviteLink{}, &Timeline{}, &Album{}, &Photo{}, &Diary{}, &Song{}, &Checkin{}, &CheckinLog{}, &SmokingAccount{}, &SmokingLog{}, &PeriodRecord{}, &PeriodDailyRecord{}, &PeriodSexRecord{}, &Setting{}, &DiaryComment{}, &SubscribeTemplate{}, &ActivityLog{}, &Admin{}, &UserToken{}, &SubscribeMessageLog{}, &NotifyLog{}, &Pet{}, &PetDaily{}, &PetAchievement{}, &PetItem{}, &PetDynamic{}, &StorageOrder{}, &StoragePlan{}, &StorageEntitlement{}, &DiaryLike{}, &DiaryFavorite{}, &SmokingWallet{}, &SmokingEntry{}}
}
// LegacyTables names logical tables used by existing report queries.
func LegacyTables() map[string]any {
return map[string]any{"files": &File{}, "file_uploads": &FileUpload{}, "record_files": &RecordFile{}, "unbinding_file_cleanup": &UnbindingFileCleanup{}, "users": &User{}, "couples": &Couple{}, "invitations": &Invitation{}, "unbind_requests": &UnbindRequest{}, "invite_links": &InviteLink{}, "timeline": &Timeline{}, "albums": &Album{}, "photos": &Photo{}, "diaries": &Diary{}, "songs": &Song{}, "checkins": &Checkin{}, "checkin_logs": &CheckinLog{}, "smoking_accounts": &SmokingAccount{}, "smoking_logs": &SmokingLog{}, "period_records": &PeriodRecord{}, "period_daily_records": &PeriodDailyRecord{}, "period_sex_records": &PeriodSexRecord{}, "settings": &Setting{}, "diary_comments": &DiaryComment{}, "subscribe_templates": &SubscribeTemplate{}, "activity_log": &ActivityLog{}, "admins": &Admin{}, "user_tokens": &UserToken{}, "subscribe_message_logs": &SubscribeMessageLog{}, "notify_log": &NotifyLog{}, "pets": &Pet{}, "pet_dailies": &PetDaily{}, "pet_achievements": &PetAchievement{}, "pet_items": &PetItem{}, "pet_dynamics": &PetDynamic{}, "storage_orders": &StorageOrder{}, "storage_plans": &StoragePlan{}, "storage_entitlements": &StorageEntitlement{}, "diary_likes": &DiaryLike{}, "diary_favorites": &DiaryFavorite{}, "smoking_wallets": &SmokingWallet{}, "smoking_entries": &SmokingEntry{}}
}