package appconfig import ( "context" "database/sql" "encoding/json" "errors" "fmt" "strings" ) const ( giftConfigGroup = "room-gift" giftQuantityPresetsKey = "quantity-presets" giftQuantityPresetMaxCount = 8 giftQuantityPresetMaxValue = 9_999 ) const getGiftQuantityPresetsSQL = ` SELECT COALESCE(value, '') FROM admin_app_configs WHERE app_code = ? AND ` + "`group`" + ` = ? AND ` + "`key`" + ` = ? AND is_deleted = FALSE LIMIT 1` // DefaultGiftQuantityPresets preserves the existing product behavior for Apps that have not saved // their own room-gift configuration. Callers receive a copy so request-local fallback cannot mutate it. func DefaultGiftQuantityPresets() []int32 { return []int32{1, 9, 99, 999} } // GiftQuantityPresets resolves only the selected App's row. The three-column unique index makes this // a bounded point lookup; a missing row is a normal compatibility fallback, not a cross-App baseline. func (r *MySQLReader) GiftQuantityPresets(ctx context.Context, appCode string) ([]int32, error) { if r == nil || r.db == nil { return nil, errors.New("app config reader is not configured") } appCode = normalizeAppCode(appCode) var raw string err := r.db.QueryRowContext(ctx, getGiftQuantityPresetsSQL, appCode, giftConfigGroup, giftQuantityPresetsKey).Scan(&raw) if errors.Is(err, sql.ErrNoRows) { return DefaultGiftQuantityPresets(), nil } if err != nil { return nil, err } var presets []int32 if err := json.Unmarshal([]byte(strings.TrimSpace(raw)), &presets); err != nil { return nil, fmt.Errorf("parse gift quantity presets for app %s: %w", appCode, err) } return normalizeGiftQuantityPresets(presets) } func normalizeGiftQuantityPresets(values []int32) ([]int32, error) { if len(values) == 0 || len(values) > giftQuantityPresetMaxCount { return nil, fmt.Errorf("gift quantity presets must contain between 1 and %d values", giftQuantityPresetMaxCount) } normalized := make([]int32, len(values)) var previous int32 for index, value := range values { if value < 1 || value > giftQuantityPresetMaxValue { return nil, fmt.Errorf("gift quantity preset must be between 1 and %d", giftQuantityPresetMaxValue) } if index > 0 && value <= previous { return nil, errors.New("gift quantity presets must be strictly increasing and unique") } normalized[index] = value previous = value } return normalized, nil } var _ interface { GiftQuantityPresets(context.Context, string) ([]int32, error) } = (*MySQLReader)(nil)