254 lines
7.6 KiB
Go
254 lines
7.6 KiB
Go
package signinreward
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"net/http"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"chatapp3-golang/internal/model"
|
|
"chatapp3-golang/internal/utils"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// GetConfig 返回后台配置页使用的签到奖励配置。
|
|
func (s *SignInRewardService) GetConfig(ctx context.Context, sysOrigin string) (*SignInRewardConfigResponse, error) {
|
|
bundle, err := s.loadConfigBundle(ctx, sysOrigin)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if bundle.Config == nil {
|
|
return &SignInRewardConfigResponse{
|
|
Configured: false,
|
|
SysOrigin: normalizeSignInRewardSysOrigin(sysOrigin),
|
|
Timezone: signInRewardDefaultTimezone,
|
|
CycleDays: signInRewardCycleLength,
|
|
Days: buildDefaultSignInRewardDays(),
|
|
}, nil
|
|
}
|
|
return &SignInRewardConfigResponse{
|
|
Configured: true,
|
|
ID: bundle.Config.ID,
|
|
SysOrigin: bundle.Config.SysOrigin,
|
|
Enabled: bundle.Config.Enabled,
|
|
Timezone: bundle.Config.Timezone,
|
|
CycleDays: signInRewardCycleLength,
|
|
Days: buildAdminDayPayloads(bundle.Days),
|
|
UpdateTime: formatSignInRewardDateTime(bundle.Config.UpdateTime),
|
|
}, nil
|
|
}
|
|
|
|
// SaveConfig 保存签到奖励后台配置。
|
|
func (s *SignInRewardService) SaveConfig(ctx context.Context, req SaveSignInRewardConfigRequest) (*SignInRewardConfigResponse, error) {
|
|
sysOrigin := normalizeSignInRewardSysOrigin(req.SysOrigin)
|
|
if sysOrigin == "" {
|
|
return nil, NewAppError(http.StatusBadRequest, "bad_request", "sysOrigin is required")
|
|
}
|
|
|
|
timezone := normalizeSignInRewardTimezone(req.Timezone)
|
|
if _, err := time.LoadLocation(timezone); err != nil {
|
|
return nil, NewAppError(http.StatusBadRequest, "invalid_timezone", err.Error())
|
|
}
|
|
|
|
normalizedDays, err := normalizeSaveDays(req.Days, req.Enabled)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
now := time.Now()
|
|
var configID int64
|
|
if err := s.repo.DB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
|
var configRow model.SignInRewardConfig
|
|
if err := tx.Where("sys_origin = ?", sysOrigin).First(&configRow).Error; errors.Is(err, gorm.ErrRecordNotFound) {
|
|
nextID, idErr := utils.NextID()
|
|
if idErr != nil {
|
|
return idErr
|
|
}
|
|
configRow = model.SignInRewardConfig{
|
|
ID: nextID,
|
|
SysOrigin: sysOrigin,
|
|
CreateTime: now,
|
|
}
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
|
|
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.SignInRewardDayConfig{}).Error; err != nil {
|
|
return err
|
|
}
|
|
|
|
rows := make([]model.SignInRewardDayConfig, 0, len(normalizedDays))
|
|
for _, item := range normalizedDays {
|
|
dayID, idErr := utils.NextID()
|
|
if idErr != nil {
|
|
return idErr
|
|
}
|
|
rows = append(rows, model.SignInRewardDayConfig{
|
|
ID: dayID,
|
|
ConfigID: configRow.ID,
|
|
DayIndex: item.DayIndex,
|
|
GoldAmount: item.GoldAmount,
|
|
RewardGroupID: item.RewardGroupID,
|
|
RewardGroupName: strings.TrimSpace(item.RewardGroupName),
|
|
CreateTime: now,
|
|
UpdateTime: now,
|
|
})
|
|
}
|
|
if len(rows) > 0 {
|
|
if err := tx.Create(&rows).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
configID = configRow.ID
|
|
return nil
|
|
}); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
_ = configID
|
|
return s.GetConfig(ctx, sysOrigin)
|
|
}
|
|
|
|
func normalizeSaveDays(days []SignInRewardDayConfigInput, enabled bool) ([]SignInRewardDayConfigInput, error) {
|
|
if len(days) != signInRewardCycleLength {
|
|
return nil, NewAppError(http.StatusBadRequest, "invalid_days", "days must contain exactly 7 items")
|
|
}
|
|
|
|
seen := make(map[int]struct{}, len(days))
|
|
normalized := make([]SignInRewardDayConfigInput, 0, len(days))
|
|
for _, item := range days {
|
|
if item.DayIndex < 1 || item.DayIndex > signInRewardCycleLength {
|
|
return nil, NewAppError(http.StatusBadRequest, "invalid_day_index", "dayIndex must be between 1 and 7")
|
|
}
|
|
if _, exists := seen[item.DayIndex]; exists {
|
|
return nil, NewAppError(http.StatusBadRequest, "duplicate_day_index", "dayIndex must be unique")
|
|
}
|
|
seen[item.DayIndex] = struct{}{}
|
|
if item.GoldAmount < 0 {
|
|
return nil, NewAppError(http.StatusBadRequest, "invalid_gold_amount", "goldAmount must be greater than or equal to 0")
|
|
}
|
|
if enabled && item.GoldAmount <= 0 && item.RewardGroupID == nil {
|
|
return nil, NewAppError(http.StatusBadRequest, "empty_day_reward", "each enabled day must configure goldAmount or rewardGroupId")
|
|
}
|
|
normalized = append(normalized, SignInRewardDayConfigInput{
|
|
DayIndex: item.DayIndex,
|
|
GoldAmount: item.GoldAmount,
|
|
RewardGroupID: item.RewardGroupID,
|
|
RewardGroupName: strings.TrimSpace(item.RewardGroupName),
|
|
})
|
|
}
|
|
sort.Slice(normalized, func(i, j int) bool {
|
|
return normalized[i].DayIndex < normalized[j].DayIndex
|
|
})
|
|
return normalized, nil
|
|
}
|
|
|
|
func (s *SignInRewardService) loadConfigBundle(ctx context.Context, sysOrigin string) (*signInRewardConfigBundle, error) {
|
|
sysOrigin = normalizeSignInRewardSysOrigin(sysOrigin)
|
|
if sysOrigin == "" {
|
|
return nil, NewAppError(http.StatusBadRequest, "bad_request", "sysOrigin is required")
|
|
}
|
|
|
|
var configRow model.SignInRewardConfig
|
|
err := s.repo.DB.WithContext(ctx).Where("sys_origin = ?", sysOrigin).First(&configRow).Error
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return &signInRewardConfigBundle{}, nil
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var dayRows []model.SignInRewardDayConfig
|
|
if err := s.repo.DB.WithContext(ctx).
|
|
Where("config_id = ?", configRow.ID).
|
|
Order("day_index asc").
|
|
Find(&dayRows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := &signInRewardConfigBundle{
|
|
Config: &signInRewardConfigSnapshot{
|
|
ID: configRow.ID,
|
|
SysOrigin: configRow.SysOrigin,
|
|
Enabled: configRow.Enabled,
|
|
Timezone: normalizeSignInRewardTimezone(configRow.Timezone),
|
|
UpdateTime: configRow.UpdateTime,
|
|
},
|
|
Days: make([]signInRewardDaySnapshot, 0, len(dayRows)),
|
|
}
|
|
for _, row := range dayRows {
|
|
result.Days = append(result.Days, signInRewardDaySnapshot{
|
|
ID: row.ID,
|
|
DayIndex: row.DayIndex,
|
|
GoldAmount: row.GoldAmount,
|
|
RewardGroupID: row.RewardGroupID,
|
|
RewardGroupName: strings.TrimSpace(row.RewardGroupName),
|
|
})
|
|
}
|
|
fillMissingSignInRewardDays(&result.Days)
|
|
return result, nil
|
|
}
|
|
|
|
func buildDefaultSignInRewardDays() []SignInRewardDayPayload {
|
|
days := make([]SignInRewardDayPayload, 0, signInRewardCycleLength)
|
|
for dayIndex := 1; dayIndex <= signInRewardCycleLength; dayIndex++ {
|
|
days = append(days, SignInRewardDayPayload{
|
|
DayIndex: dayIndex,
|
|
RewardItems: []SignInRewardRewardItem{},
|
|
})
|
|
}
|
|
return days
|
|
}
|
|
|
|
func buildAdminDayPayloads(days []signInRewardDaySnapshot) []SignInRewardDayPayload {
|
|
payloads := make([]SignInRewardDayPayload, 0, len(days))
|
|
for _, item := range days {
|
|
payloads = append(payloads, SignInRewardDayPayload{
|
|
DayIndex: item.DayIndex,
|
|
GoldAmount: item.GoldAmount,
|
|
RewardGroupID: item.RewardGroupID,
|
|
RewardGroupName: item.RewardGroupName,
|
|
RewardItems: []SignInRewardRewardItem{},
|
|
})
|
|
}
|
|
return payloads
|
|
}
|
|
|
|
func fillMissingSignInRewardDays(days *[]signInRewardDaySnapshot) {
|
|
dayMap := make(map[int]signInRewardDaySnapshot, len(*days))
|
|
for _, item := range *days {
|
|
dayMap[item.DayIndex] = item
|
|
}
|
|
filled := make([]signInRewardDaySnapshot, 0, signInRewardCycleLength)
|
|
for dayIndex := 1; dayIndex <= signInRewardCycleLength; dayIndex++ {
|
|
if item, ok := dayMap[dayIndex]; ok {
|
|
filled = append(filled, item)
|
|
continue
|
|
}
|
|
filled = append(filled, signInRewardDaySnapshot{DayIndex: dayIndex})
|
|
}
|
|
*days = filled
|
|
}
|
|
|
|
func formatSignInRewardDateTime(t time.Time) string {
|
|
if t.IsZero() {
|
|
return ""
|
|
}
|
|
return t.Format("2006-01-02 15:04:05")
|
|
}
|