topfans/backend/services/taskService/repository/onboarding_repo.go
2026-04-17 17:17:32 +08:00

236 lines
8.2 KiB
Go

package repository
import (
"errors"
"time"
"github.com/topfans/backend/pkg/logger"
"github.com/topfans/backend/services/taskService/model"
"go.uber.org/zap"
"gorm.io/gorm"
)
type OnboardingRepository interface {
GetOnboardingStatus(userID int64, starID int64) (*model.UserOnboardingStatus, error)
GetOrCreateOnboardingStatus(userID int64, starID int64) (*model.UserOnboardingStatus, error)
GetUserOnboardingStatuses(userID int64) ([]*model.UserOnboardingStatus, error)
UpdateOnboardingStatus(status *model.UserOnboardingStatus) error
UpdateOnboardingProgress(progress *model.UserOnboardingProgress) error
GetUserOnboardingProgress(userID int64, taskKey string) (*model.UserOnboardingProgress, error)
GetOrCreateOnboardingProgress(userID int64, taskKey string) (*model.UserOnboardingProgress, error)
ListActiveStageConfigs() ([]*model.OnboardingStageConfig, error)
ListUserOnboardingProgressByUser(userID int64) ([]*model.UserOnboardingProgress, error)
GetStageConfig(stage int64) (*model.OnboardingStageConfig, error)
SaveStageConfigs(configs []*model.OnboardingStageConfig) error
CountStageConfigs() (int64, error)
}
type onboardingRepository struct {
db *gorm.DB
}
func NewOnboardingRepository(db *gorm.DB) OnboardingRepository {
return &onboardingRepository{db: db}
}
func (r *onboardingRepository) GetOnboardingStatus(userID int64, starID int64) (*model.UserOnboardingStatus, error) {
var status model.UserOnboardingStatus
err := r.db.Where("user_id = ? AND star_id = ?", userID, starID).First(&status).Error
if err != nil {
return nil, err
}
return &status, nil
}
func (r *onboardingRepository) GetOrCreateOnboardingStatus(userID int64, starID int64) (*model.UserOnboardingStatus, error) {
var status model.UserOnboardingStatus
err := r.db.Where("user_id = ? AND star_id = ?", userID, starID).First(&status).Error
if err == nil {
return &status, nil
}
if !errors.Is(err, gorm.ErrRecordNotFound) {
logger.Logger.Error("Failed to GetOrCreateOnboardingStatus", zap.Int64("user_id", userID), zap.Int64("star_id", starID), zap.Error(err))
return nil, err
}
now := time.Now().Unix()
status = model.UserOnboardingStatus{
UserID: userID,
StarID: starID,
CurrentStage: 0,
Status: "pending",
CreatedAt: now,
UpdatedAt: now,
}
if err := r.db.Create(&status).Error; err != nil {
logger.Logger.Error("Failed to Create OnboardingStatus", zap.Int64("user_id", userID), zap.Int64("star_id", starID), zap.Error(err))
return nil, err
}
return &status, nil
}
func (r *onboardingRepository) GetUserOnboardingStatuses(userID int64) ([]*model.UserOnboardingStatus, error) {
var statuses []*model.UserOnboardingStatus
err := r.db.Where("user_id = ?", userID).Order("star_id DESC").Find(&statuses).Error
if err != nil {
logger.Logger.Error("Failed to GetUserOnboardingStatuses", zap.Int64("user_id", userID), zap.Error(err))
return nil, err
}
return statuses, nil
}
func (r *onboardingRepository) UpdateOnboardingStatus(status *model.UserOnboardingStatus) error {
status.UpdatedAt = time.Now().Unix()
if err := r.db.Save(status).Error; err != nil {
logger.Logger.Error("Failed to UpdateOnboardingStatus", zap.Int64("user_id", status.UserID), zap.Error(err))
return err
}
return nil
}
func (r *onboardingRepository) UpdateOnboardingProgress(progress *model.UserOnboardingProgress) error {
progress.UpdatedAt = time.Now().Unix()
if err := r.db.Save(progress).Error; err != nil {
logger.Logger.Error("Failed to UpdateOnboardingProgress", zap.Int64("user_id", progress.UserID), zap.String("task_key", progress.TaskKey), zap.Error(err))
return err
}
return nil
}
func (r *onboardingRepository) GetUserOnboardingProgress(userID int64, taskKey string) (*model.UserOnboardingProgress, error) {
var progress model.UserOnboardingProgress
err := r.db.Where("user_id = ? AND task_key = ?", userID, taskKey).First(&progress).Error
if err != nil {
return nil, err
}
return &progress, nil
}
func (r *onboardingRepository) GetOrCreateOnboardingProgress(userID int64, taskKey string) (*model.UserOnboardingProgress, error) {
var progress model.UserOnboardingProgress
err := r.db.Where("user_id = ? AND task_key = ?", userID, taskKey).First(&progress).Error
if err == nil {
return &progress, nil
}
if !errors.Is(err, gorm.ErrRecordNotFound) {
logger.Logger.Error("Failed to GetOrCreateOnboardingProgress", zap.Int64("user_id", userID), zap.String("task_key", taskKey), zap.Error(err))
return nil, err
}
now := time.Now().Unix()
progress = model.UserOnboardingProgress{
UserID: userID,
TaskKey: taskKey,
Status: "pending",
CreatedAt: now,
UpdatedAt: now,
}
if err := r.db.Create(&progress).Error; err != nil {
logger.Logger.Error("Failed to Create OnboardingProgress", zap.Int64("user_id", userID), zap.String("task_key", taskKey), zap.Error(err))
return nil, err
}
return &progress, nil
}
func (r *onboardingRepository) ListActiveStageConfigs() ([]*model.OnboardingStageConfig, error) {
var configs []*model.OnboardingStageConfig
err := r.db.Where("is_active = ?", true).Order("sort_order ASC").Find(&configs).Error
if err != nil {
logger.Logger.Error("Failed to ListActiveStageConfigs", zap.Error(err))
return nil, err
}
return configs, nil
}
func (r *onboardingRepository) ListUserOnboardingProgressByUser(userID int64) ([]*model.UserOnboardingProgress, error) {
var progressList []*model.UserOnboardingProgress
err := r.db.Where("user_id = ?", userID).Find(&progressList).Error
if err != nil {
logger.Logger.Error("Failed to ListUserOnboardingProgressByUser", zap.Int64("user_id", userID), zap.Error(err))
return nil, err
}
return progressList, nil
}
func (r *onboardingRepository) GetStageConfig(stage int64) (*model.OnboardingStageConfig, error) {
var config model.OnboardingStageConfig
logger.Logger.Info("GetStageConfig: querying",
zap.Int64("stage", stage))
err := r.db.Where("stage = ? AND is_active = ?", stage, true).First(&config).Error
if err != nil {
logger.Logger.Error("GetStageConfig: query failed",
zap.Int64("stage", stage),
zap.Error(err))
return nil, err
}
logger.Logger.Info("GetStageConfig: found",
zap.Int64("stage", stage),
zap.String("name", config.Name))
return &config, nil
}
func (r *onboardingRepository) SaveStageConfigs(configs []*model.OnboardingStageConfig) error {
if len(configs) == 0 {
logger.Logger.Warn("SaveStageConfigs: configs is empty, returning")
return nil
}
now := time.Now().Unix()
logger.Logger.Info("SaveStageConfigs: starting save",
zap.Int("count", len(configs)))
for _, cfg := range configs {
logger.Logger.Info("SaveStageConfigs: processing config",
zap.Int("stage", cfg.Stage),
zap.String("name", cfg.Name),
zap.Strings("required_task_keys", cfg.RequiredTaskKeys),
zap.Int64("crystal_reward", cfg.CrystalReward),
zap.Int64("exp_reward", cfg.ExpReward))
cfg.UpdatedAt = now
// First try to update existing record
// Use Select to specify fields so GORM properly handles JSON serialization
result := r.db.Model(&model.OnboardingStageConfig{}).
Where("stage = ?", cfg.Stage).
Select("name", "required_task_keys", "crystal_reward", "exp_reward", "is_active", "updated_at").
Updates(cfg)
if result.Error != nil {
logger.Logger.Error("SaveStageConfigs: failed to update config",
zap.Int("stage", cfg.Stage),
zap.Error(result.Error))
return result.Error
}
logger.Logger.Info("SaveStageConfigs: update result",
zap.Int("stage", cfg.Stage),
zap.Int64("rows_affected", result.RowsAffected))
// If no rows affected, create new record
if result.RowsAffected == 0 {
cfg.CreatedAt = now
if err := r.db.Create(cfg).Error; err != nil {
logger.Logger.Error("SaveStageConfigs: failed to create config",
zap.Int("stage", cfg.Stage),
zap.Error(err))
return err
}
logger.Logger.Info("SaveStageConfigs: created config",
zap.Int("stage", cfg.Stage),
zap.String("name", cfg.Name))
} else {
logger.Logger.Info("SaveStageConfigs: updated config",
zap.Int("stage", cfg.Stage),
zap.String("name", cfg.Name))
}
}
logger.Logger.Info("SaveStageConfigs: all configs saved successfully")
return nil
}
func (r *onboardingRepository) CountStageConfigs() (int64, error) {
var count int64
err := r.db.Model(&model.OnboardingStageConfig{}).Where("is_active = ?", true).Count(&count).Error
return count, err
}