topfans/backend/services/taskService/model/task_models.go
zerosaturation 89d5d12450 fix(task): backfill id=1/2 trigger_event + cleanup misleading NULL comments
daily_browse_asset (id=2) / daily_login (id=1) 之前永远不触发,根因:
2026_07_21_004 迁移只 backfill 了 id=3/4 的 trigger_event=task_key,
id=1/2 留 NULL。但 ProcessTaskEvent 引擎按 trigger_event = ? 严格匹配
(spec §4 单一隔离单元),NULL 不会命中,前端 reportEvent 后引擎跑空
循环、daily-tasks 任务永远 pending。

修复:
- 新增 2026_07_27_005 迁移:回填 id=1/2 的 trigger_event=task_key(幂等)
- 修订原 004 迁移的错误注释('trigger_event 留 NULL'误导后人)
- model/task_models.go:删除 'NULL 仍走 def.TaskKey 兜底' 不实承诺
- impl plan §37/39:修订错描述('暂不依赖 trigger_event 列')
- docker/init-db.sql:124668/124671:重建 dev DB 时也带 trigger_event
- daily_task_repo_test.go:补 2 个回归测试
  - NullTriggerEvent_NotMatched:trigger_event=NULL 时查不到
  - TriggerEventEqualsTaskKey_Matches:trigger_event=task_key 时能命中

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-27 16:36:40 +08:00

147 lines
8.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package model
// TaskDefinition 任务定义表
type TaskDefinition struct {
ID int64 `gorm:"primaryKey;column:id;autoIncrement"`
StarID *int64 `gorm:"column:star_id;index"` // NULL=全局默认
TaskKey string `gorm:"column:task_key;size:50;not null"`
TaskType string `gorm:"column:task_type;size:20;not null"` // daily/onboarding
Name string `gorm:"column:name;size:100;not null"`
Description string `gorm:"column:description;type:text"`
CrystalReward int64 `gorm:"column:crystal_reward;default:0"`
SortOrder int `gorm:"column:sort_order;default:0"`
IsActive bool `gorm:"column:is_active;default:true"`
CreatedAt int64 `gorm:"column:created_at"`
UpdatedAt int64 `gorm:"column:updated_at"`
// 方案 Aspec §2.1 / §4trigger_event + target_count 取代内联 def.TaskKey 匹配
// ★ trigger_event 必须填ProcessTaskEvent 引擎按 trigger_event = ? 严格匹配
// spec §4 单一隔离单元NULL 不会命中。daily 任务的 trigger_event 由
// 2026_07_27_005 迁移回填(与 task_key 一致onboarding 任务 trigger_event 留 NULL。
TriggerEvent string `gorm:"column:trigger_event;size:64"`
TargetCount int `gorm:"column:target_count;default:1"` // =1 等价"首次">1 为计数型
}
func (TaskDefinition) TableName() string { return "task_definitions" }
// UserDailyTaskProgress 每日任务进度表
type UserDailyTaskProgress struct {
ID int64 `gorm:"primaryKey;column:id;autoIncrement"`
UserID int64 `gorm:"column:user_id;not null;index:idx_daily_user_star_key"`
StarID int64 `gorm:"column:star_id;not null;index:idx_daily_user_star_key"`
TaskKey string `gorm:"column:task_key;size:50;not null;index:idx_daily_user_star_key"`
Status string `gorm:"column:status;size:20;default:pending"` // pending/completed/claimed
CompletedAt *int64 `gorm:"column:completed_at"`
ClaimedAt *int64 `gorm:"column:claimed_at"`
CreatedAt int64 `gorm:"column:created_at"`
UpdatedAt int64 `gorm:"column:updated_at"`
// 方案 Aspec §2.2 / §4累计次数progress >= target_count → status=completed
Progress int `gorm:"column:progress;default:0"`
}
func (UserDailyTaskProgress) TableName() string { return "user_daily_task_progress" }
// UserOnboardingProgress 引导任务进度表
type UserOnboardingProgress struct {
ID int64 `gorm:"primaryKey;column:id;autoIncrement"`
UserID int64 `gorm:"column:user_id;not null;index:idx_onboard_user_key"`
TaskKey string `gorm:"column:task_key;size:50;not null;index:idx_onboard_user_key"`
Status string `gorm:"column:status;size:20;default:pending"`
CompletedAt *int64 `gorm:"column:completed_at"`
ClaimedAt *int64 `gorm:"column:claimed_at"`
CreatedAt int64 `gorm:"column:created_at"`
UpdatedAt int64 `gorm:"column:updated_at"`
}
func (UserOnboardingProgress) TableName() string { return "user_onboarding_progress" }
// UserOnboardingStatus 引导流程状态表per-user per-star
type UserOnboardingStatus struct {
ID int64 `gorm:"primaryKey;column:id;autoIncrement"`
UserID int64 `gorm:"column:user_id;not null;uniqueIndex:uk_user_star_onboarding"`
StarID int64 `gorm:"column:star_id;not null;uniqueIndex:uk_user_star_onboarding"`
IsOnboardingCompleted bool `gorm:"column:is_onboarding_completed;default:false"`
IsOnboardingClaimed bool `gorm:"column:is_onboarding_claimed;default:false"`
HasFriendDisplayBonus bool `gorm:"column:has_friend_display_bonus;default:false"`
OnboardingCompletedAt *int64 `gorm:"column:onboarding_completed_at"`
OnboardingClaimedAt *int64 `gorm:"column:onboarding_claimed_at"`
ClaimedStages []int64 `gorm:"column:claimed_stages;type:text;serializer:json"` // 已领取奖励的阶段列表
CreatedAt int64 `gorm:"column:created_at"`
UpdatedAt int64 `gorm:"column:updated_at"`
CurrentStage int64 `gorm:"column:current_stage;default:0"`
Status string `gorm:"column:status;size:20;default:pending"`
IsFirstLoginBonusClaimed bool `gorm:"column:is_first_login_bonus_claimed;default:false"` // 废弃字段
CompletedAt *int64 `gorm:"column:completed_at"` // 废弃字段
ClaimedAt *int64 `gorm:"column:claimed_at"` // 废弃字段
}
func (UserOnboardingStatus) TableName() string { return "user_onboarding_status" }
// OnboardingStageConfig 引导阶段配置表
type OnboardingStageConfig struct {
ID int64 `gorm:"primaryKey;column:id;autoIncrement"`
Stage int `gorm:"column:stage;not null;uniqueIndex"`
Name string `gorm:"column:name;size:100;not null"`
Description string `gorm:"column:description;type:text"`
RequiredTaskKeys []string `gorm:"column:required_task_keys;type:text;serializer:json"` // 存储为 JSON 字符串
CrystalReward int64 `gorm:"column:crystal_reward;default:0"`
SortOrder int `gorm:"column:sort_order;default:0"`
IsActive bool `gorm:"column:is_active;default:true"`
CreatedAt int64 `gorm:"column:created_at"`
UpdatedAt int64 `gorm:"column:updated_at"`
}
func (OnboardingStageConfig) TableName() string { return "onboarding_stage_config" }
// ExhibitionRevenueRecord 展示收益记录表
type ExhibitionRevenueRecord struct {
ID int64 `gorm:"primaryKey;column:id;autoIncrement"`
UserID int64 `gorm:"column:user_id;not null;index:idx_revenue_user_star"`
StarID int64 `gorm:"column:star_id;not null;index:idx_revenue_user_star"`
ExhibitionID int64 `gorm:"column:exhibition_id;not null"`
AssetID int64 `gorm:"column:asset_id;not null"`
SlotID int64 `gorm:"column:slot_id;not null"`
SlotOwnerUID int64 `gorm:"column:slot_owner_uid;not null"`
SlotType string `gorm:"column:slot_type;size:20;not null"` // own/friend
CrystalAmount int64 `gorm:"column:crystal_amount;not null"`
CycleStartTime int64 `gorm:"column:cycle_start_time;not null"`
CycleEndTime int64 `gorm:"column:cycle_end_time;not null"`
Status string `gorm:"column:status;size:20;default:claimable"` // claimable/claimed/failed
ClaimedAt *int64 `gorm:"column:claimed_at"`
CreatedAt int64 `gorm:"column:created_at"`
}
func (ExhibitionRevenueRecord) TableName() string { return "exhibition_revenue_records" }
// LikeBetRevenueRecord 点赞押注收益记录表
// 业务场景:用户点赞别人作品 → 展品到期时根据点赞排名bet_order发放水晶奖励。
// 数据写入方galleryService.cleanup_worker 调用 taskService.RecordLikeBetRevenue内部 RPC
// 幂等保证DB 唯一约束 uk_like_bet_unique(exhibition_id, like_id) —— cleanup_worker 重跑不会重复发奖
type LikeBetRevenueRecord struct {
ID int64 `gorm:"primaryKey;column:id;autoIncrement"`
UserID int64 `gorm:"column:user_id;not null;index:idx_like_bet_user_star"` // 点赞者
StarID int64 `gorm:"column:star_id;not null;index:idx_like_bet_user_star"` // 点赞者粉丝身份
ExhibitionID int64 `gorm:"column:exhibition_id;not null"`
AssetID int64 `gorm:"column:asset_id;not null"`
LikeID int64 `gorm:"column:like_id;not null"` // 关联 asset_likes.id幂等键
BetOrder int `gorm:"column:bet_order;not null"` // 第几位点赞(按 asset_likes.created_at ASC, id ASC
TotalLikes int `gorm:"column:total_likes;not null"` // 该展览内的总押注数审计字段NOT 藏品全局点赞数
CrystalAmount int64 `gorm:"column:crystal_amount;not null"`
CycleStartTime int64 `gorm:"column:cycle_start_time;not null"`
CycleEndTime int64 `gorm:"column:cycle_end_time;not null"`
Status string `gorm:"column:status;size:20;default:claimable"` // claimable/claimed/failed
ClaimedAt *int64 `gorm:"column:claimed_at"`
CreatedAt int64 `gorm:"column:created_at"`
}
func (LikeBetRevenueRecord) TableName() string { return "like_bet_revenue_records" }
// TaskResetLog 重置日志表
type TaskResetLog struct {
ID int64 `gorm:"primaryKey;column:id;autoIncrement"`
ResetType string `gorm:"column:reset_type;size:20;not null"` // daily
LastResetAt int64 `gorm:"column:last_reset_at;not null"`
CreatedAt int64 `gorm:"column:created_at"`
}
func (TaskResetLog) TableName() string { return "task_reset_log" }