2026-04-27 23:27:37 +08:00

212 lines
6.3 KiB
Go

package roomturnoverreward
import (
"context"
"errors"
"net/http"
"sort"
"strings"
"time"
"chatapp3-golang/internal/model"
"chatapp3-golang/internal/utils"
"gorm.io/gorm"
)
// GetConfig 返回系统当前房间流水奖励配置;未配置时返回默认档位草稿。
func (s *Service) GetConfig(ctx context.Context, sysOrigin string) (*RoomTurnoverRewardConfigResponse, error) {
sysOrigin = s.normalizeSysOrigin(sysOrigin)
var configRow model.RoomTurnoverRewardConfig
err := s.repo.DB.WithContext(ctx).
Where("sys_origin = ?", sysOrigin).
First(&configRow).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return &RoomTurnoverRewardConfigResponse{
Configured: false,
SysOrigin: sysOrigin,
Enabled: false,
Timezone: s.defaultTimezone(),
LevelConfigs: defaultLevelPayloads(),
}, nil
}
if err != nil {
return nil, err
}
levels, err := s.loadLevels(ctx, configRow.ID)
if err != nil {
return nil, err
}
return &RoomTurnoverRewardConfigResponse{
Configured: true,
ID: configRow.ID,
SysOrigin: configRow.SysOrigin,
Enabled: configRow.Enabled,
Timezone: configRow.Timezone,
UpdateTime: formatDateTime(configRow.UpdateTime),
LevelConfigs: buildLevelPayloads(levels),
}, nil
}
// SaveConfig 保存系统级房间流水奖励配置;同一系统只保留一份当前配置。
func (s *Service) SaveConfig(ctx context.Context, req SaveRoomTurnoverRewardConfigRequest) (*RoomTurnoverRewardConfigResponse, error) {
requestID := req.ID.Int64()
sysOrigin := s.normalizeSysOrigin(req.SysOrigin)
timezone := strings.TrimSpace(req.Timezone)
if timezone == "" {
timezone = s.defaultTimezone()
}
if _, err := time.LoadLocation(timezone); err != nil {
return nil, NewAppError(http.StatusBadRequest, "invalid_timezone", err.Error())
}
levels, err := s.normalizeLevelInputs(req.LevelConfigs)
if err != nil {
return nil, err
}
if req.Enabled {
hasEnabledLevel := false
for _, level := range levels {
if level.Enabled {
hasEnabledLevel = true
break
}
}
if !hasEnabledLevel {
return nil, NewAppError(http.StatusBadRequest, "invalid_level_configs", "enabled config must contain at least one enabled level")
}
}
var savedConfigID int64
if err := s.repo.DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
now := time.Now()
var configRow model.RoomTurnoverRewardConfig
if requestID > 0 {
err := tx.Where("id = ?", requestID).First(&configRow).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return NewAppError(http.StatusNotFound, "room_turnover_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.RoomTurnoverRewardConfig{
ID: configID,
SysOrigin: sysOrigin,
CreateTime: now,
}
} else if err != nil {
return err
}
}
configRow.SysOrigin = sysOrigin
configRow.Enabled = req.Enabled
configRow.Timezone = timezone
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.RoomTurnoverRewardLevel{}).Error; err != nil {
return err
}
for index := range levels {
levels[index].ConfigID = configRow.ID
levels[index].CreateTime = now
levels[index].UpdateTime = now
}
if err := tx.Create(&levels).Error; err != nil {
return err
}
savedConfigID = configRow.ID
return nil
}); err != nil {
return nil, err
}
return s.GetConfig(ctx, sysOriginBySavedID(ctx, s.repo.DB, savedConfigID, sysOrigin))
}
func (s *Service) normalizeLevelInputs(inputs []RoomTurnoverRewardLevelInput) ([]model.RoomTurnoverRewardLevel, error) {
if len(inputs) == 0 {
return nil, NewAppError(http.StatusBadRequest, "invalid_level_configs", "levelConfigs is required")
}
seenLevels := make(map[int]struct{}, len(inputs))
seenThresholds := make(map[int64]struct{}, len(inputs))
rows := make([]model.RoomTurnoverRewardLevel, 0, len(inputs))
for _, item := range inputs {
if item.Level <= 0 {
return nil, NewAppError(http.StatusBadRequest, "invalid_level", "level must be greater than 0")
}
if item.TurnoverThreshold <= 0 {
return nil, NewAppError(http.StatusBadRequest, "invalid_turnover_threshold", "turnoverThreshold must be greater than 0")
}
if item.RewardGold <= 0 {
return nil, NewAppError(http.StatusBadRequest, "invalid_reward_gold", "rewardGold must be greater than 0")
}
if _, exists := seenLevels[item.Level]; exists {
return nil, NewAppError(http.StatusBadRequest, "duplicate_level", "levelConfigs must not contain duplicate levels")
}
if _, exists := seenThresholds[item.TurnoverThreshold]; exists {
return nil, NewAppError(http.StatusBadRequest, "duplicate_turnover_threshold", "levelConfigs must not contain duplicate thresholds")
}
seenLevels[item.Level] = struct{}{}
seenThresholds[item.TurnoverThreshold] = struct{}{}
id, err := utils.NextID()
if err != nil {
return nil, err
}
rows = append(rows, model.RoomTurnoverRewardLevel{
ID: id,
Level: item.Level,
TurnoverThreshold: item.TurnoverThreshold,
RewardGold: item.RewardGold,
Enabled: item.Enabled,
})
}
sort.Slice(rows, func(i, j int) bool {
return rows[i].TurnoverThreshold < rows[j].TurnoverThreshold
})
return rows, nil
}
func (s *Service) loadLevels(ctx context.Context, configID int64) ([]model.RoomTurnoverRewardLevel, error) {
var levels []model.RoomTurnoverRewardLevel
if configID <= 0 {
return levels, nil
}
err := s.repo.DB.WithContext(ctx).
Where("config_id = ?", configID).
Order("level asc").
Find(&levels).Error
return levels, err
}
func sysOriginBySavedID(ctx context.Context, db roomTurnoverRewardDB, configID int64, fallback string) string {
if configID <= 0 {
return fallback
}
var row model.RoomTurnoverRewardConfig
if err := db.WithContext(ctx).Select("sys_origin").Where("id = ?", configID).First(&row).Error; err != nil {
return fallback
}
return row.SysOrigin
}