239 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			239 lines
		
	
	
		
			6.7 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package validator
 | ||
| 
 | ||
| import (
 | ||
| 	"fmt"
 | ||
| 	"net/url"
 | ||
| 	"regexp"
 | ||
| 	"strconv"
 | ||
| 	"strings"
 | ||
| 	"time"
 | ||
| 
 | ||
| 	"github.com/go-playground/validator/v10"
 | ||
| )
 | ||
| 
 | ||
| // RegisterCustomValidators 注册所有自定义验证器
 | ||
| func RegisterCustomValidators(validate *validator.Validate) {
 | ||
| 	// 手机号验证器
 | ||
| 	validate.RegisterValidation("phone", validatePhone)
 | ||
| 	
 | ||
| 	// 用户名验证器(字母开头,允许字母数字下划线,3-20位)
 | ||
| 	validate.RegisterValidation("username", validateUsername)
 | ||
| 	
 | ||
| 	// 强密码验证器(至少8位,包含大小写字母和数字)
 | ||
| 	validate.RegisterValidation("strong_password", validateStrongPassword)
 | ||
| 	
 | ||
| 	// 统一社会信用代码验证器
 | ||
| 	validate.RegisterValidation("social_credit_code", validateSocialCreditCode)
 | ||
| 	
 | ||
| 	// 身份证号验证器
 | ||
| 	validate.RegisterValidation("id_card", validateIDCard)
 | ||
| 	
 | ||
| 	// 价格验证器(非负数)
 | ||
| 	validate.RegisterValidation("price", validatePrice)
 | ||
| 	
 | ||
| 	// 排序方向验证器
 | ||
| 	validate.RegisterValidation("sort_order", validateSortOrder)
 | ||
| 	
 | ||
| 	// 产品代码验证器(字母数字下划线连字符,3-50位)
 | ||
| 	validate.RegisterValidation("product_code", validateProductCode)
 | ||
| 	
 | ||
| 	// UUID验证器
 | ||
| 	validate.RegisterValidation("uuid", validateUUID)
 | ||
| 	
 | ||
| 	// URL验证器
 | ||
| 	validate.RegisterValidation("url", validateURL)
 | ||
| 	
 | ||
| 	// 企业邮箱验证器
 | ||
| 	validate.RegisterValidation("enterprise_email", validateEnterpriseEmail)
 | ||
| 	
 | ||
| 	// 企业地址验证器
 | ||
| 	validate.RegisterValidation("enterprise_address", validateEnterpriseAddress)
 | ||
| 	
 | ||
| 	// IP地址验证器
 | ||
| 	validate.RegisterValidation("ip", validateIP)
 | ||
| 	
 | ||
| 	// 授权日期验证器
 | ||
| 	validate.RegisterValidation("auth_date", validateAuthDate)
 | ||
| }
 | ||
| 
 | ||
| // validatePhone 手机号验证
 | ||
| func validatePhone(fl validator.FieldLevel) bool {
 | ||
| 	phone := fl.Field().String()
 | ||
| 	matched, _ := regexp.MatchString(`^1[3-9]\d{9}$`, phone)
 | ||
| 	return matched
 | ||
| }
 | ||
| 
 | ||
| // validateUsername 用户名验证
 | ||
| func validateUsername(fl validator.FieldLevel) bool {
 | ||
| 	username := fl.Field().String()
 | ||
| 	matched, _ := regexp.MatchString(`^[a-zA-Z][a-zA-Z0-9_]{2,19}$`, username)
 | ||
| 	return matched
 | ||
| }
 | ||
| 
 | ||
| // validateStrongPassword 强密码验证
 | ||
| func validateStrongPassword(fl validator.FieldLevel) bool {
 | ||
| 	password := fl.Field().String()
 | ||
| 	if len(password) < 8 {
 | ||
| 		return false
 | ||
| 	}
 | ||
| 	hasUpper := regexp.MustCompile(`[A-Z]`).MatchString(password)
 | ||
| 	hasLower := regexp.MustCompile(`[a-z]`).MatchString(password)
 | ||
| 	hasDigit := regexp.MustCompile(`\d`).MatchString(password)
 | ||
| 	return hasUpper && hasLower && hasDigit
 | ||
| }
 | ||
| 
 | ||
| // validateSocialCreditCode 统一社会信用代码验证
 | ||
| func validateSocialCreditCode(fl validator.FieldLevel) bool {
 | ||
| 	code := fl.Field().String()
 | ||
| 	matched, _ := regexp.MatchString(`^[0-9A-HJ-NPQRTUWXY]{2}\d{6}[0-9A-HJ-NPQRTUWXY]{10}$`, code)
 | ||
| 	return matched
 | ||
| }
 | ||
| 
 | ||
| // validateIDCard 身份证号验证
 | ||
| func validateIDCard(fl validator.FieldLevel) bool {
 | ||
| 	idCard := fl.Field().String()
 | ||
| 	matched, _ := regexp.MatchString(`^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[\dXx]$`, idCard)
 | ||
| 	return matched
 | ||
| }
 | ||
| 
 | ||
| // validatePrice 价格验证
 | ||
| func validatePrice(fl validator.FieldLevel) bool {
 | ||
| 	price := fl.Field().Float()
 | ||
| 	return price >= 0
 | ||
| }
 | ||
| 
 | ||
| // validateSortOrder 排序方向验证
 | ||
| func validateSortOrder(fl validator.FieldLevel) bool {
 | ||
| 	sortOrder := fl.Field().String()
 | ||
| 	return sortOrder == "" || sortOrder == "asc" || sortOrder == "desc"
 | ||
| }
 | ||
| 
 | ||
| // validateProductCode 产品代码验证
 | ||
| func validateProductCode(fl validator.FieldLevel) bool {
 | ||
| 	code := fl.Field().String()
 | ||
| 	matched, _ := regexp.MatchString(`^[a-zA-Z0-9_-]{3,50}$`, code)
 | ||
| 	return matched
 | ||
| }
 | ||
| 
 | ||
| // validateUUID UUID验证
 | ||
| func validateUUID(fl validator.FieldLevel) bool {
 | ||
| 	uuid := fl.Field().String()
 | ||
| 	matched, _ := regexp.MatchString(`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`, uuid)
 | ||
| 	return matched
 | ||
| }
 | ||
| 
 | ||
| // validateURL URL验证
 | ||
| func validateURL(fl validator.FieldLevel) bool {
 | ||
| 	urlStr := fl.Field().String()
 | ||
| 	_, err := url.ParseRequestURI(urlStr)
 | ||
| 	return err == nil
 | ||
| }
 | ||
| 
 | ||
| // validateEnterpriseEmail 企业邮箱验证
 | ||
| func validateEnterpriseEmail(fl validator.FieldLevel) bool {
 | ||
| 	email := fl.Field().String()
 | ||
| 	// 邮箱格式验证:用户名@域名.顶级域名
 | ||
| 	matched, _ := regexp.MatchString(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`, email)
 | ||
| 	return matched
 | ||
| }
 | ||
| 
 | ||
| // validateEnterpriseAddress 企业地址验证
 | ||
| func validateEnterpriseAddress(fl validator.FieldLevel) bool {
 | ||
| 	address := fl.Field().String()
 | ||
| 	// 地址长度验证:2-200字符,不能只包含空格
 | ||
| 	if len(strings.TrimSpace(address)) < 2 || len(address) > 200 {
 | ||
| 		return false
 | ||
| 	}
 | ||
| 	// 地址不能只包含特殊字符
 | ||
| 	matched, _ := regexp.MatchString(`^[^\s]+.*[^\s]+$`, strings.TrimSpace(address))
 | ||
| 	return matched
 | ||
| }
 | ||
| 
 | ||
| // validateIP IP地址验证(支持IPv4)
 | ||
| func validateIP(fl validator.FieldLevel) bool {
 | ||
| 	ip := fl.Field().String()
 | ||
| 	// 使用正则表达式验证IPv4格式
 | ||
| 	pattern := `^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$`
 | ||
| 	matched, _ := regexp.MatchString(pattern, ip)
 | ||
| 	return matched
 | ||
| } 
 | ||
| 
 | ||
| // validateAuthDate 授权日期验证器
 | ||
| // 格式:YYYYMMDD-YYYYMMDD,之前的日期范围必须包括今天
 | ||
| func validateAuthDate(fl validator.FieldLevel) bool {
 | ||
| 	authDate := fl.Field().String()
 | ||
| 	if authDate == "" {
 | ||
| 		return true // 空值由required标签处理
 | ||
| 	}
 | ||
| 
 | ||
| 	// 检查格式:YYYYMMDD-YYYYMMDD
 | ||
| 	parts := strings.Split(authDate, "-")
 | ||
| 	if len(parts) != 2 {
 | ||
| 		return false
 | ||
| 	}
 | ||
| 
 | ||
| 	startDateStr := parts[0]
 | ||
| 	endDateStr := parts[1]
 | ||
| 
 | ||
| 	// 检查日期格式是否为8位数字
 | ||
| 	if len(startDateStr) != 8 || len(endDateStr) != 8 {
 | ||
| 		return false
 | ||
| 	}
 | ||
| 
 | ||
| 	// 解析开始日期
 | ||
| 	startDate, err := parseYYYYMMDD(startDateStr)
 | ||
| 	if err != nil {
 | ||
| 		return false
 | ||
| 	}
 | ||
| 
 | ||
| 	// 解析结束日期
 | ||
| 	endDate, err := parseYYYYMMDD(endDateStr)
 | ||
| 	if err != nil {
 | ||
| 		return false
 | ||
| 	}
 | ||
| 
 | ||
| 	// 检查开始日期不能晚于结束日期
 | ||
| 	if startDate.After(endDate) {
 | ||
| 		return false
 | ||
| 	}
 | ||
| 
 | ||
| 	// 获取今天的日期(去掉时间部分)
 | ||
| 	today := time.Now().Truncate(24 * time.Hour)
 | ||
| 
 | ||
| 	// 检查日期范围是否包括今天
 | ||
| 	// 如果两个日期都是今天也行
 | ||
| 	return !startDate.After(today) && !endDate.Before(today)
 | ||
| }
 | ||
| 
 | ||
| // parseYYYYMMDD 解析YYYYMMDD格式的日期字符串
 | ||
| func parseYYYYMMDD(dateStr string) (time.Time, error) {
 | ||
| 	if len(dateStr) != 8 {
 | ||
| 		return time.Time{}, fmt.Errorf("日期格式错误")
 | ||
| 	}
 | ||
| 
 | ||
| 	year, err := strconv.Atoi(dateStr[:4])
 | ||
| 	if err != nil {
 | ||
| 		return time.Time{}, err
 | ||
| 	}
 | ||
| 
 | ||
| 	month, err := strconv.Atoi(dateStr[4:6])
 | ||
| 	if err != nil {
 | ||
| 		return time.Time{}, err
 | ||
| 	}
 | ||
| 
 | ||
| 	day, err := strconv.Atoi(dateStr[6:8])
 | ||
| 	if err != nil {
 | ||
| 		return time.Time{}, err
 | ||
| 	}
 | ||
| 
 | ||
| 	// 验证日期有效性
 | ||
| 	date := time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC)
 | ||
| 	
 | ||
| 	// 检查解析后的日期是否与输入一致(防止无效日期如20230230)
 | ||
| 	expectedDateStr := date.Format("20060102")
 | ||
| 	if expectedDateStr != dateStr {
 | ||
| 		return time.Time{}, fmt.Errorf("无效日期")
 | ||
| 	}
 | ||
| 
 | ||
| 	return date, nil
 | ||
| }  |