2026-05-22 10:10:04 +08:00

235 lines
6.8 KiB
Go

package firstrechargereward
import (
"context"
"errors"
"net/http"
"sort"
"strings"
"time"
"chatapp3-golang/internal/model"
"chatapp3-golang/internal/utils"
"gorm.io/gorm"
)
func (s *Service) GetConfig(ctx context.Context, sysOrigin string) (*ConfigResponse, error) {
sysOrigin = s.normalizeSysOrigin(sysOrigin)
var configRow model.FirstRechargeRewardConfig
err := s.db.WithContext(ctx).
Where("sys_origin = ?", sysOrigin).
First(&configRow).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return &ConfigResponse{
Configured: false,
SysOrigin: sysOrigin,
Enabled: false,
LevelConfigs: []LevelPayload{},
}, nil
}
if err != nil {
return nil, err
}
levels, err := s.loadLevels(ctx, configRow.ID)
if err != nil {
return nil, err
}
rewardItems := s.loadRewardItemsMap(ctx, levels)
return &ConfigResponse{
Configured: true,
ID: configRow.ID,
SysOrigin: configRow.SysOrigin,
Enabled: configRow.Enabled,
UpdateTime: formatDateTime(configRow.UpdateTime),
LevelConfigs: buildLevelPayloads(levels, rewardItems, 0, 0, true),
}, nil
}
func (s *Service) SaveConfig(ctx context.Context, req SaveConfigRequest) (*ConfigResponse, error) {
requestID := req.ID.Int64()
sysOrigin := s.normalizeSysOrigin(req.SysOrigin)
levels, err := s.normalizeLevelInputs(req.LevelConfigs)
if err != nil {
return nil, err
}
if req.Enabled && !hasEnabledLevel(levels) {
return nil, NewAppError(http.StatusBadRequest, "invalid_level_configs", "enabled config must contain at least one enabled level")
}
var savedConfigID int64
if err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
now := time.Now()
var configRow model.FirstRechargeRewardConfig
if requestID > 0 {
err := tx.Where("id = ?", requestID).First(&configRow).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return NewAppError(http.StatusNotFound, "first_recharge_reward_config_not_found", "config not found")
}
if err != nil {
return err
}
if strings.TrimSpace(req.SysOrigin) == "" {
sysOrigin = configRow.SysOrigin
}
} else {
err := tx.Where("sys_origin = ?", sysOrigin).First(&configRow).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
configID, idErr := utils.NextID()
if idErr != nil {
return idErr
}
configRow = model.FirstRechargeRewardConfig{
ID: configID,
SysOrigin: sysOrigin,
CreateTime: now,
}
} else if err != nil {
return err
}
}
configRow.SysOrigin = sysOrigin
configRow.Enabled = req.Enabled
configRow.UpdateTime = now
if configRow.CreateTime.IsZero() {
configRow.CreateTime = now
}
if err := tx.Save(&configRow).Error; err != nil {
return err
}
if err := tx.Where("config_id = ?", configRow.ID).Delete(&model.FirstRechargeRewardLevel{}).Error; err != nil {
return err
}
for index := range levels {
levels[index].ConfigID = configRow.ID
levels[index].CreateTime = now
levels[index].UpdateTime = now
}
if len(levels) > 0 {
if err := tx.Create(&levels).Error; err != nil {
return err
}
}
savedConfigID = configRow.ID
return nil
}); err != nil {
return nil, err
}
return s.GetConfig(ctx, savedSysOrigin(ctx, s.db, savedConfigID, sysOrigin))
}
func (s *Service) normalizeLevelInputs(inputs []LevelInput) ([]model.FirstRechargeRewardLevel, error) {
if len(inputs) == 0 {
return nil, NewAppError(http.StatusBadRequest, "invalid_level_configs", "levelConfigs is required")
}
seenLevels := make(map[int]struct{}, len(inputs))
seenAmounts := make(map[int64]struct{}, len(inputs))
rows := make([]model.FirstRechargeRewardLevel, 0, len(inputs))
for _, item := range inputs {
if item.Level <= 0 {
return nil, NewAppError(http.StatusBadRequest, "invalid_level", "level must be greater than 0")
}
amountCents := item.RechargeAmount.Cents()
if amountCents == 0 && item.RechargeAmountCents > 0 {
amountCents = item.RechargeAmountCents
}
if amountCents <= 0 {
return nil, NewAppError(http.StatusBadRequest, "invalid_recharge_amount", "rechargeAmount must be greater than 0")
}
rewardGroupID := item.RewardGroupID.Int64()
if rewardGroupID <= 0 {
return nil, NewAppError(http.StatusBadRequest, "invalid_reward_group", "rewardGroupId is required")
}
if _, exists := seenLevels[item.Level]; exists {
return nil, NewAppError(http.StatusBadRequest, "duplicate_level", "levelConfigs must not contain duplicate levels")
}
if _, exists := seenAmounts[amountCents]; exists {
return nil, NewAppError(http.StatusBadRequest, "duplicate_recharge_amount", "levelConfigs must not contain duplicate rechargeAmount")
}
seenLevels[item.Level] = struct{}{}
seenAmounts[amountCents] = struct{}{}
id, err := utils.NextID()
if err != nil {
return nil, err
}
rows = append(rows, model.FirstRechargeRewardLevel{
ID: id,
Level: item.Level,
RechargeAmountCents: amountCents,
RewardGroupID: rewardGroupID,
RewardGroupName: strings.TrimSpace(item.RewardGroupName),
Enabled: item.Enabled,
})
}
sort.Slice(rows, func(i, j int) bool {
if rows[i].RechargeAmountCents == rows[j].RechargeAmountCents {
return rows[i].Level < rows[j].Level
}
return rows[i].RechargeAmountCents < rows[j].RechargeAmountCents
})
return rows, nil
}
func (s *Service) loadConfigBundle(ctx context.Context, sysOrigin string) (*configBundle, error) {
sysOrigin = s.normalizeSysOrigin(sysOrigin)
var configRow model.FirstRechargeRewardConfig
err := s.db.WithContext(ctx).
Where("sys_origin = ?", sysOrigin).
First(&configRow).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return &configBundle{}, nil
}
if err != nil {
return nil, err
}
levels, err := s.loadLevels(ctx, configRow.ID)
if err != nil {
return nil, err
}
return &configBundle{
Config: refConfigSnapshot(configSnapshotFromModel(configRow)),
Levels: levels,
}, nil
}
func (s *Service) loadLevels(ctx context.Context, configID int64) ([]levelSnapshot, error) {
if configID <= 0 {
return []levelSnapshot{}, nil
}
var rows []model.FirstRechargeRewardLevel
if err := s.db.WithContext(ctx).
Where("config_id = ?", configID).
Order("recharge_amount_cents asc").
Order("level asc").
Find(&rows).Error; err != nil {
return nil, err
}
levels := make([]levelSnapshot, 0, len(rows))
for _, row := range rows {
levels = append(levels, levelSnapshotFromModel(row))
}
sortLevels(levels)
return levels, nil
}
func hasEnabledLevel(levels []model.FirstRechargeRewardLevel) bool {
for _, level := range levels {
if level.Enabled {
return true
}
}
return false
}
func savedSysOrigin(ctx context.Context, db dbHandle, id int64, fallback string) string {
var row model.FirstRechargeRewardConfig
if id > 0 && db != nil {
if err := db.WithContext(ctx).Where("id = ?", id).First(&row).Error; err == nil {
return row.SysOrigin
}
}
return fallback
}