package invite import ( "strings" "time" ) // defaultIfBlank 在字符串为空时返回默认值。 func defaultIfBlank(value, fallback string) string { if strings.TrimSpace(value) == "" { return fallback } return value } // normalizeSysOrigin 统一系统标识格式。 func normalizeSysOrigin(value string) string { return strings.TrimSpace(value) } // normalizeTimezone 统一时区配置,缺省回退到默认时区。 func normalizeTimezone(value string) string { value = strings.TrimSpace(value) if value == "" { return defaultTimezone } return value } func inviteLocation(timezone string) *time.Location { location, err := time.LoadLocation(normalizeTimezone(timezone)) if err != nil { location = time.FixedZone(defaultTimezone, 3*3600) } return location } // thirtyDayPeriodInfoAt 返回指定时间所在 30 天任务周期的 key、结束时间和剩余秒数。 func thirtyDayPeriodInfoAt(t time.Time, timezone string, anchor time.Time) (string, time.Time, int64) { location := inviteLocation(timezone) localTime := t.In(location) anchorLocal := anchor.In(location) if anchor.IsZero() { anchorLocal = time.Date(1970, 1, 1, 0, 0, 0, 0, location) } anchorStart := time.Date(anchorLocal.Year(), anchorLocal.Month(), anchorLocal.Day(), 0, 0, 0, 0, location) elapsedDays := int(localTime.Sub(anchorStart) / (24 * time.Hour)) if elapsedDays < 0 { elapsedDays = 0 } periodStart := anchorStart.AddDate(0, 0, elapsedDays/inviteTaskPeriodDays*inviteTaskPeriodDays) periodEnd := periodStart.AddDate(0, 0, inviteTaskPeriodDays) seconds := int64(periodEnd.Sub(localTime).Seconds()) if seconds < 0 { seconds = 0 } return periodStart.Format("20060102"), periodEnd, seconds } // digitsOnly 判断字符串是否只包含数字。 func digitsOnly(value string) bool { for _, ch := range value { if ch < '0' || ch > '9' { return false } } return true } // truncate 截断字符串,避免超出数据库字段长度。 func truncate(value string, size int) string { if len(value) <= size { return value } return value[:size] }