package user import ( "net/url" "strings" "time" "unicode/utf8" ) const ( // ProfileBirthDateLayout 是 users.birth_date 的唯一外部日期格式,不携带时区。 ProfileBirthDateLayout = "2006-01-02" // ProfileUsernameMaxRunes 必须和 users.username VARCHAR(64) 保持一致。 ProfileUsernameMaxRunes = 64 // ProfileAvatarMaxRunes 必须和 users.avatar VARCHAR(512) 保持一致。 ProfileAvatarMaxRunes = 512 // ProfileGenderMaxRunes 必须和 users.gender VARCHAR(32) 保持一致。 ProfileGenderMaxRunes = 32 ) // ProfileStringTooLong 按字符数校验 VARCHAR 边界,避免中文昵称按 UTF-8 字节数误拒。 func ProfileStringTooLong(value string, maxRunes int) bool { return utf8.RuneCountInString(value) > maxRunes } // ValidProfileBirthDate 校验生日只使用 yyyy-mm-dd,避免客户端时区解析造成日期偏移。 func ValidProfileBirthDate(value string) bool { parsed, err := time.Parse(ProfileBirthDateLayout, value) if err != nil { return false } // MySQL DATE 在严格模式下不能写入 Go 零值年份;注册入口必须先拦截,避免写库阶段退化为 INTERNAL_ERROR。 return parsed.Year() >= 1000 } // ValidProfileAvatarURL 限制头像为绝对 HTTP(S) URL,防止展示资料写入不可渲染或危险 scheme。 func ValidProfileAvatarURL(raw string) bool { parsed, err := url.Parse(raw) if err != nil || parsed.Host == "" { return false } if parsed.Scheme != "http" && parsed.Scheme != "https" { return false } return !strings.ContainsAny(raw, " \t\r\n") }