package kit import ( "fmt" "strings" "time" ) const ( DateLayout = "2006-01-02" DateTimeLayout = time.DateTime // 2006-01-02 15:04:05 MonthLayout = "2006-01" ) // ArgError 参数错误:Handler 返回给模型的可读说明。 type ArgError struct{ Msg string } func (e *ArgError) Error() string { return e.Msg } func argErrorf(format string, a ...any) error { return &ArgError{Msg: fmt.Sprintf(format, a...)} } // Required 必填字符串参数(去掉首尾空白)。 func Required(name, v string) (string, error) { v = strings.TrimSpace(v) if v == "" { return "", argErrorf("参数 %s 必填", name) } return v, nil } // ParseDate 解析 yyyy-MM-dd;v 为空且 allowEmpty 时返回 nil。 func ParseDate(name, v string, allowEmpty bool) (*time.Time, error) { v = strings.TrimSpace(v) if v == "" { if allowEmpty { return nil, nil } return nil, argErrorf("参数 %s 必填,格式 yyyy-MM-dd", name) } t, err := time.Parse(DateLayout, v) if err != nil { return nil, argErrorf("参数 %s 格式错误,需要 yyyy-MM-dd:%s", name, v) } return &t, nil } // ParseDateTime 解析 yyyy-MM-dd HH:mm:ss;只给日期时按当天 00:00:00 处理。 func ParseDateTime(name, v string) (time.Time, error) { v = strings.TrimSpace(v) if v == "" { return time.Time{}, argErrorf("参数 %s 必填,格式 yyyy-MM-dd HH:mm:ss", name) } if t, err := time.Parse(DateTimeLayout, v); err == nil { return t, nil } if t, err := time.Parse(DateLayout, v); err == nil { return t, nil } return time.Time{}, argErrorf("参数 %s 格式错误,需要 yyyy-MM-dd HH:mm:ss:%s", name, v) } // ParseRange 解析时间范围并检查 from < to、跨度不超过 max。 func ParseRange(from, to string, max time.Duration) (time.Time, time.Time, error) { f, err := ParseDateTime("from", from) if err != nil { return f, f, err } t, err := ParseDateTime("to", to) if err != nil { return f, t, err } if !t.After(f) { return f, t, argErrorf("结束时间必须晚于开始时间") } if max > 0 && t.Sub(f) > max { return f, t, argErrorf("时间范围不能超过 %s,请缩短范围后分段查询", humanDuration(max)) } return f, t, nil } func humanDuration(d time.Duration) string { if d%(24*time.Hour) == 0 { return fmt.Sprintf("%d 天", int(d/(24*time.Hour))) } return d.String() } // Limit 归一化条数:<=0 取默认值,超过上限取上限。 func Limit(v, def, max int) int { if v <= 0 { return def } if v > max { return max } return v } // Page 归一化页码(从 1 开始)。 func Page(v int) int { if v <= 0 { return 1 } return v } // Enum 校验枚举值;v 为空返回默认值。 func Enum(name, v, def string, allowed ...string) (string, error) { v = strings.TrimSpace(v) if v == "" { return def, nil } for _, a := range allowed { if strings.EqualFold(v, a) { return a, nil } } return "", argErrorf("参数 %s 取值错误:%s,可选值:%s", name, v, strings.Join(allowed, " / ")) }