Files
2026-01-25 21:59:00 +08:00

159 lines
4.6 KiB
Go

// Package validator provides validation utilities for API parameters
package validator
import (
"errors"
"fmt"
"math"
"time"
)
// Pagination constants
const (
DefaultPageSize = 20
MaxPageSize = 100
MinPageSize = 1
)
// Amount constants
const (
MinAmount = 0.01
MaxAmount = 999999999999.99
)
// Date constants
const (
MaxDateRangeDays = 366
MaxFutureDays = 365
)
// String length constants
const (
MaxNameLength = 100
MaxNoteLength = 500
)
// Validation errors
var (
ErrPaginationLimitExceeded = errors.New("pagination limit exceeded maximum allowed value")
ErrInvalidOffset = errors.New("pagination offset cannot be negative")
ErrAmountTooSmall = errors.New("amount is below minimum allowed value")
ErrAmountTooLarge = errors.New("amount exceeds maximum allowed value")
ErrDateRangeExceeded = errors.New("date range exceeds maximum allowed days")
ErrFutureDateExceeded = errors.New("date exceeds maximum allowed future date")
ErrEndDateBeforeStartDate = errors.New("end date must be after start date")
ErrNameTooLong = errors.New("name exceeds maximum allowed length")
ErrNoteTooLong = errors.New("note exceeds maximum allowed length")
)
// ValidatePagination validates and normalizes pagination parameters
// Returns normalized offset and limit values
// Feature: api-interface-optimization
// Validates: Requirements 7.1, 7.2
func ValidatePagination(offset, limit int) (int, int) {
// Normalize offset
if offset < 0 {
offset = 0
}
// Normalize limit
if limit <= 0 {
limit = DefaultPageSize
}
if limit > MaxPageSize {
limit = MaxPageSize
}
return offset, limit
}
// ValidatePaginationStrict validates pagination parameters and returns error if invalid
// Feature: api-interface-optimization
// Validates: Requirements 7.1, 7.2, 7.3
func ValidatePaginationStrict(offset, limit int) error {
if offset < 0 {
return fmt.Errorf("%w: offset=%d", ErrInvalidOffset, offset)
}
if limit > MaxPageSize {
return fmt.Errorf("%w: limit=%d, max=%d", ErrPaginationLimitExceeded, limit, MaxPageSize)
}
return nil
}
// ValidateAmount validates that an amount is within acceptable bounds
// Feature: api-interface-optimization
// Validates: Requirements 8.1, 8.2, 8.3
func ValidateAmount(amount float64) error {
if amount < MinAmount {
return fmt.Errorf("%w: amount=%.2f, min=%.2f", ErrAmountTooSmall, amount, MinAmount)
}
if amount > MaxAmount {
return fmt.Errorf("%w: amount=%.2f, max=%.2f", ErrAmountTooLarge, amount, MaxAmount)
}
return nil
}
// RoundAmount rounds an amount to 2 decimal places
// Feature: api-interface-optimization
// Validates: Requirements 8.2
func RoundAmount(amount float64) float64 {
return math.Round(amount*100) / 100
}
// ValidateDateRange validates that a date range is within acceptable bounds
// Feature: api-interface-optimization
// Validates: Requirements 9.1
func ValidateDateRange(startDate, endDate time.Time) error {
if endDate.Before(startDate) {
return ErrEndDateBeforeStartDate
}
days := int(endDate.Sub(startDate).Hours() / 24)
if days > MaxDateRangeDays {
return fmt.Errorf("%w: days=%d, max=%d", ErrDateRangeExceeded, days, MaxDateRangeDays)
}
return nil
}
// ValidateFutureDate validates that a date is not too far in the future
// Feature: api-interface-optimization
// Validates: Requirements 9.2
func ValidateFutureDate(date time.Time) error {
maxFutureDate := time.Now().AddDate(0, 0, MaxFutureDays)
if date.After(maxFutureDate) {
return fmt.Errorf("%w: date=%s, max=%s", ErrFutureDateExceeded, date.Format("2006-01-02"), maxFutureDate.Format("2006-01-02"))
}
return nil
}
// ValidateStringLength validates that a string does not exceed the maximum length
// Feature: api-interface-optimization
// Validates: Requirements 10.1, 10.2
func ValidateStringLength(s string, maxLen int, fieldName string) error {
if len(s) > maxLen {
if fieldName == "name" {
return fmt.Errorf("%w: length=%d, max=%d", ErrNameTooLong, len(s), maxLen)
}
if fieldName == "note" {
return fmt.Errorf("%w: length=%d, max=%d", ErrNoteTooLong, len(s), maxLen)
}
return fmt.Errorf("%s exceeds maximum length: length=%d, max=%d", fieldName, len(s), maxLen)
}
return nil
}
// ValidateName validates a name field
// Feature: api-interface-optimization
// Validates: Requirements 10.1
func ValidateName(name string) error {
return ValidateStringLength(name, MaxNameLength, "name")
}
// ValidateNote validates a note field
// Feature: api-interface-optimization
// Validates: Requirements 10.2
func ValidateNote(note string) error {
return ValidateStringLength(note, MaxNoteLength, "note")
}