2026-05-08 18:55:47 +08:00

195 lines
5.7 KiB
Go

package voiceroomredpacket
import (
"context"
"errors"
"net/http"
"time"
"chatapp3-golang/internal/model"
"chatapp3-golang/internal/utils"
"gorm.io/gorm"
)
// GetConfig 返回 App 当前红包配置和用户今日发送次数。
func (s *Service) GetConfig(ctx context.Context, user AuthUser) (*ConfigResponse, error) {
snapshot, err := s.loadConfig(ctx, user.SysOrigin)
if err != nil {
return nil, err
}
todayCount := 0
if user.UserID > 0 {
location := resolveLocation(snapshot.Timezone)
count, err := s.loadDailySendCount(ctx, snapshot.SysOrigin, user.UserID, dayKey(time.Now().In(location)))
if err != nil {
return nil, err
}
todayCount = count
}
resp := configResponse(snapshot)
resp.TodaySendCount = todayCount
return resp, nil
}
// GetAdminConfig 返回后台配置。
func (s *Service) GetAdminConfig(ctx context.Context, sysOrigin string) (*ConfigResponse, error) {
snapshot, err := s.loadConfig(ctx, sysOrigin)
if err != nil {
return nil, err
}
return configResponse(snapshot), nil
}
// SaveAdminConfig 保存后台配置。
func (s *Service) SaveAdminConfig(ctx context.Context, req SaveConfigRequest) (*ConfigResponse, error) {
sysOrigin := normalizeSysOrigin(req.SysOrigin)
timezone := normalizeTimezone(req.Timezone)
if _, err := time.LoadLocation(timezone); err != nil {
return nil, NewAppError(http.StatusBadRequest, "invalid_timezone", err.Error())
}
amountOptions := req.AmountOptions
if len(amountOptions) == 0 {
amountOptions = defaultAmountOptions()
}
countOptions := req.CountOptions
if len(countOptions) == 0 {
countOptions = defaultCountOptions()
}
dailyLimit := req.DailySendLimit
if dailyLimit <= 0 {
dailyLimit = defaultDailySendLimit
}
delaySeconds := req.DelaySeconds
if delaySeconds <= 0 {
delaySeconds = defaultDelaySeconds
}
refundAfterSeconds := req.RefundAfterSeconds
if refundAfterSeconds <= 0 {
refundAfterSeconds = defaultRefundAfterSeconds
}
var configID int64
if err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
now := time.Now()
var row model.VoiceRoomRedPacketConfig
err := tx.Where("sys_origin = ?", sysOrigin).First(&row).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
id, idErr := utils.NextID()
if idErr != nil {
return idErr
}
row = model.VoiceRoomRedPacketConfig{
ID: id,
SysOrigin: sysOrigin,
CreateTime: now,
}
} else if err != nil {
return err
}
row.Enabled = req.Enabled
row.DailySendLimit = dailyLimit
row.DelaySeconds = delaySeconds
row.RefundAfterSeconds = refundAfterSeconds
row.Timezone = timezone
row.AmountOptions = joinInt64CSV(amountOptions)
row.CountOptions = joinIntCSV(countOptions)
row.UpdateTime = now
if row.CreateTime.IsZero() {
row.CreateTime = now
}
if err := tx.Save(&row).Error; err != nil {
return err
}
configID = row.ID
return nil
}); err != nil {
return nil, err
}
_ = configID
return s.GetAdminConfig(ctx, sysOrigin)
}
func (s *Service) loadConfig(ctx context.Context, sysOrigin string) (configSnapshot, error) {
sysOrigin = normalizeSysOrigin(sysOrigin)
var row model.VoiceRoomRedPacketConfig
err := s.db.WithContext(ctx).Where("sys_origin = ?", sysOrigin).First(&row).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return configSnapshot{
SysOrigin: sysOrigin,
Enabled: false,
DailySendLimit: defaultDailySendLimit,
DelaySeconds: defaultDelaySeconds,
RefundAfterSeconds: defaultRefundAfterSeconds,
Timezone: defaultTimezone,
AmountOptions: defaultAmountOptions(),
CountOptions: defaultCountOptions(),
}, nil
}
if err != nil {
return configSnapshot{}, err
}
return configSnapshot{
ID: row.ID,
SysOrigin: row.SysOrigin,
Enabled: row.Enabled,
DailySendLimit: positiveOrDefault(row.DailySendLimit, defaultDailySendLimit),
DelaySeconds: positiveOrDefault(row.DelaySeconds, defaultDelaySeconds),
RefundAfterSeconds: positiveOrDefault(row.RefundAfterSeconds, defaultRefundAfterSeconds),
Timezone: normalizeTimezone(row.Timezone),
AmountOptions: parseInt64CSV(row.AmountOptions, defaultAmountOptions()),
CountOptions: parseIntCSV(row.CountOptions, defaultCountOptions()),
Configured: true,
UpdateTime: row.UpdateTime,
}, nil
}
func configResponse(snapshot configSnapshot) *ConfigResponse {
return &ConfigResponse{
Configured: snapshot.Configured,
ID: snapshot.ID,
SysOrigin: snapshot.SysOrigin,
Enabled: snapshot.Enabled,
DailySendLimit: snapshot.DailySendLimit,
DelaySeconds: snapshot.DelaySeconds,
RefundAfterSeconds: snapshot.RefundAfterSeconds,
Timezone: snapshot.Timezone,
AmountOptions: append([]int64(nil), snapshot.AmountOptions...),
CountOptions: append([]int(nil), snapshot.CountOptions...),
UpdateTime: formatTime(snapshot.UpdateTime),
}
}
func positiveOrDefault(value, fallback int) int {
if value <= 0 {
return fallback
}
return value
}
func resolveLocation(timezone string) *time.Location {
location, err := time.LoadLocation(normalizeTimezone(timezone))
if err != nil {
return time.UTC
}
return location
}
func dayKey(t time.Time) string {
return t.Format("20060102")
}
func (s *Service) loadDailySendCount(ctx context.Context, sysOrigin string, userID int64, day string) (int, error) {
var row model.VoiceRoomRedPacketDailyCounter
err := s.db.WithContext(ctx).
Where("sys_origin = ? AND user_id = ? AND day_key = ?", sysOrigin, userID, day).
First(&row).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
return 0, nil
}
if err != nil {
return 0, err
}
return row.SendCount, nil
}