temp
This commit is contained in:
@@ -1,303 +0,0 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"tyapi-server/internal/shared/interfaces"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
// RequestValidator 请求验证器实现
|
||||
type RequestValidator struct {
|
||||
validator *validator.Validate
|
||||
response interfaces.ResponseBuilder
|
||||
}
|
||||
|
||||
// NewRequestValidator 创建请求验证器
|
||||
func NewRequestValidator(response interfaces.ResponseBuilder) interfaces.RequestValidator {
|
||||
v := validator.New()
|
||||
|
||||
// 注册自定义验证器
|
||||
registerCustomValidators(v)
|
||||
|
||||
return &RequestValidator{
|
||||
validator: v,
|
||||
response: response,
|
||||
}
|
||||
}
|
||||
|
||||
// Validate 验证请求体
|
||||
func (v *RequestValidator) Validate(c *gin.Context, dto interface{}) error {
|
||||
if err := v.validator.Struct(dto); err != nil {
|
||||
validationErrors := v.formatValidationErrors(err)
|
||||
v.response.BadRequest(c, "Validation failed", validationErrors)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateQuery 验证查询参数
|
||||
func (v *RequestValidator) ValidateQuery(c *gin.Context, dto interface{}) error {
|
||||
if err := c.ShouldBindQuery(dto); err != nil {
|
||||
v.response.BadRequest(c, "查询参数格式错误", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
if err := v.validator.Struct(dto); err != nil {
|
||||
validationErrors := v.formatValidationErrors(err)
|
||||
v.response.ValidationError(c, validationErrors)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateParam 验证路径参数
|
||||
func (v *RequestValidator) ValidateParam(c *gin.Context, dto interface{}) error {
|
||||
if err := c.ShouldBindUri(dto); err != nil {
|
||||
v.response.BadRequest(c, "路径参数格式错误", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
if err := v.validator.Struct(dto); err != nil {
|
||||
validationErrors := v.formatValidationErrors(err)
|
||||
v.response.ValidationError(c, validationErrors)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BindAndValidate 绑定并验证请求
|
||||
func (v *RequestValidator) BindAndValidate(c *gin.Context, dto interface{}) error {
|
||||
// 绑定请求体
|
||||
if err := c.ShouldBindJSON(dto); err != nil {
|
||||
v.response.BadRequest(c, "请求体格式错误", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
// 验证数据
|
||||
return v.Validate(c, dto)
|
||||
}
|
||||
|
||||
// formatValidationErrors 格式化验证错误
|
||||
func (v *RequestValidator) formatValidationErrors(err error) map[string][]string {
|
||||
errors := make(map[string][]string)
|
||||
|
||||
if validationErrors, ok := err.(validator.ValidationErrors); ok {
|
||||
for _, fieldError := range validationErrors {
|
||||
fieldName := v.getFieldName(fieldError)
|
||||
errorMessage := v.getErrorMessage(fieldError)
|
||||
|
||||
if _, exists := errors[fieldName]; !exists {
|
||||
errors[fieldName] = []string{}
|
||||
}
|
||||
errors[fieldName] = append(errors[fieldName], errorMessage)
|
||||
}
|
||||
}
|
||||
|
||||
return errors
|
||||
}
|
||||
|
||||
// getFieldName 获取字段名(JSON标签优先)
|
||||
func (v *RequestValidator) getFieldName(fieldError validator.FieldError) string {
|
||||
// 可以通过反射获取JSON标签,这里简化处理
|
||||
fieldName := fieldError.Field()
|
||||
|
||||
// 转换为snake_case(可选)
|
||||
return v.toSnakeCase(fieldName)
|
||||
}
|
||||
|
||||
// getErrorMessage 获取错误消息
|
||||
func (v *RequestValidator) getErrorMessage(fieldError validator.FieldError) string {
|
||||
field := fieldError.Field()
|
||||
tag := fieldError.Tag()
|
||||
param := fieldError.Param()
|
||||
|
||||
fieldDisplayName := v.getFieldDisplayName(field)
|
||||
|
||||
switch tag {
|
||||
case "required":
|
||||
return fmt.Sprintf("%s 不能为空", fieldDisplayName)
|
||||
case "email":
|
||||
return fmt.Sprintf("%s 必须是有效的邮箱地址", fieldDisplayName)
|
||||
case "min":
|
||||
return fmt.Sprintf("%s 长度不能少于 %s 位", fieldDisplayName, param)
|
||||
case "max":
|
||||
return fmt.Sprintf("%s 长度不能超过 %s 位", fieldDisplayName, param)
|
||||
case "len":
|
||||
return fmt.Sprintf("%s 长度必须为 %s 位", fieldDisplayName, param)
|
||||
case "gt":
|
||||
return fmt.Sprintf("%s 必须大于 %s", fieldDisplayName, param)
|
||||
case "gte":
|
||||
return fmt.Sprintf("%s 必须大于等于 %s", fieldDisplayName, param)
|
||||
case "lt":
|
||||
return fmt.Sprintf("%s 必须小于 %s", fieldDisplayName, param)
|
||||
case "lte":
|
||||
return fmt.Sprintf("%s 必须小于等于 %s", fieldDisplayName, param)
|
||||
case "oneof":
|
||||
return fmt.Sprintf("%s 必须是以下值之一:[%s]", fieldDisplayName, param)
|
||||
case "url":
|
||||
return fmt.Sprintf("%s 必须是有效的URL地址", fieldDisplayName)
|
||||
case "alpha":
|
||||
return fmt.Sprintf("%s 只能包含字母", fieldDisplayName)
|
||||
case "alphanum":
|
||||
return fmt.Sprintf("%s 只能包含字母和数字", fieldDisplayName)
|
||||
case "numeric":
|
||||
return fmt.Sprintf("%s 必须是数字", fieldDisplayName)
|
||||
case "phone":
|
||||
return fmt.Sprintf("%s 必须是有效的手机号", fieldDisplayName)
|
||||
case "username":
|
||||
return fmt.Sprintf("%s 格式不正确,只能包含字母、数字、下划线,且不能以数字开头", fieldDisplayName)
|
||||
case "strong_password":
|
||||
return fmt.Sprintf("%s 强度不足,必须包含大小写字母和数字,且不少于8位", fieldDisplayName)
|
||||
case "eqfield":
|
||||
return fmt.Sprintf("%s 必须与 %s 一致", fieldDisplayName, v.getFieldDisplayName(param))
|
||||
default:
|
||||
return fmt.Sprintf("%s 格式不正确", fieldDisplayName)
|
||||
}
|
||||
}
|
||||
|
||||
// getFieldDisplayName 获取字段显示名称(中文)
|
||||
func (v *RequestValidator) getFieldDisplayName(field string) string {
|
||||
fieldNames := map[string]string{
|
||||
"phone": "手机号",
|
||||
"password": "密码",
|
||||
"confirm_password": "确认密码",
|
||||
"old_password": "原密码",
|
||||
"new_password": "新密码",
|
||||
"confirm_new_password": "确认新密码",
|
||||
"code": "验证码",
|
||||
"username": "用户名",
|
||||
"email": "邮箱",
|
||||
"display_name": "显示名称",
|
||||
"scene": "使用场景",
|
||||
"Password": "密码",
|
||||
"NewPassword": "新密码",
|
||||
}
|
||||
|
||||
if displayName, exists := fieldNames[field]; exists {
|
||||
return displayName
|
||||
}
|
||||
return field
|
||||
}
|
||||
|
||||
// toSnakeCase 转换为snake_case
|
||||
func (v *RequestValidator) toSnakeCase(str string) string {
|
||||
var result strings.Builder
|
||||
for i, r := range str {
|
||||
if i > 0 && (r >= 'A' && r <= 'Z') {
|
||||
result.WriteRune('_')
|
||||
}
|
||||
result.WriteRune(r)
|
||||
}
|
||||
return strings.ToLower(result.String())
|
||||
}
|
||||
|
||||
// registerCustomValidators 注册自定义验证器
|
||||
func registerCustomValidators(v *validator.Validate) {
|
||||
// 注册手机号验证器
|
||||
v.RegisterValidation("phone", validatePhone)
|
||||
|
||||
// 注册用户名验证器
|
||||
v.RegisterValidation("username", validateUsername)
|
||||
|
||||
// 注册密码强度验证器
|
||||
v.RegisterValidation("strong_password", validateStrongPassword)
|
||||
}
|
||||
|
||||
// validatePhone 验证手机号
|
||||
func validatePhone(fl validator.FieldLevel) bool {
|
||||
phone := fl.Field().String()
|
||||
if phone == "" {
|
||||
return true // 空值由required标签处理
|
||||
}
|
||||
|
||||
// 简单的手机号验证(可根据需要完善)
|
||||
if len(phone) < 10 || len(phone) > 15 {
|
||||
return false
|
||||
}
|
||||
|
||||
// 检查是否以+开头或全是数字
|
||||
if strings.HasPrefix(phone, "+") {
|
||||
phone = phone[1:]
|
||||
}
|
||||
|
||||
for _, r := range phone {
|
||||
if r < '0' || r > '9' {
|
||||
if r != '-' && r != ' ' && r != '(' && r != ')' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// validateUsername 验证用户名
|
||||
func validateUsername(fl validator.FieldLevel) bool {
|
||||
username := fl.Field().String()
|
||||
if username == "" {
|
||||
return true // 空值由required标签处理
|
||||
}
|
||||
|
||||
// 用户名规则:3-30个字符,只能包含字母、数字、下划线,不能以数字开头
|
||||
if len(username) < 3 || len(username) > 30 {
|
||||
return false
|
||||
}
|
||||
|
||||
// 不能以数字开头
|
||||
if username[0] >= '0' && username[0] <= '9' {
|
||||
return false
|
||||
}
|
||||
|
||||
// 只能包含字母、数字、下划线
|
||||
for _, r := range username {
|
||||
if !((r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// validateStrongPassword 验证密码强度
|
||||
func validateStrongPassword(fl validator.FieldLevel) bool {
|
||||
password := fl.Field().String()
|
||||
if password == "" {
|
||||
return true // 空值由required标签处理
|
||||
}
|
||||
|
||||
// 密码强度规则:至少8个字符,包含大小写字母、数字
|
||||
if len(password) < 8 {
|
||||
return false
|
||||
}
|
||||
|
||||
hasUpper := false
|
||||
hasLower := false
|
||||
hasDigit := false
|
||||
|
||||
for _, r := range password {
|
||||
switch {
|
||||
case r >= 'A' && r <= 'Z':
|
||||
hasUpper = true
|
||||
case r >= 'a' && r <= 'z':
|
||||
hasLower = true
|
||||
case r >= '0' && r <= '9':
|
||||
hasDigit = true
|
||||
}
|
||||
}
|
||||
|
||||
return hasUpper && hasLower && hasDigit
|
||||
}
|
||||
|
||||
// ValidateStruct 直接验证结构体(不通过HTTP上下文)
|
||||
func (v *RequestValidator) ValidateStruct(dto interface{}) error {
|
||||
return v.validator.Struct(dto)
|
||||
}
|
||||
|
||||
// GetValidator 获取原始验证器(用于特殊情况)
|
||||
func (v *RequestValidator) GetValidator() *validator.Validate {
|
||||
return v.validator
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"tyapi-server/internal/shared/interfaces"
|
||||
@@ -14,16 +15,12 @@ import (
|
||||
|
||||
// RequestValidatorZh 中文验证器实现
|
||||
type RequestValidatorZh struct {
|
||||
validator *validator.Validate
|
||||
translator ut.Translator
|
||||
response interfaces.ResponseBuilder
|
||||
translator ut.Translator
|
||||
}
|
||||
|
||||
// NewRequestValidatorZh 创建支持中文翻译的请求验证器
|
||||
func NewRequestValidatorZh(response interfaces.ResponseBuilder) interfaces.RequestValidator {
|
||||
// 创建验证器实例
|
||||
validate := validator.New()
|
||||
|
||||
// 创建中文locale
|
||||
zhLocale := zh.New()
|
||||
uni := ut.New(zhLocale, zhLocale)
|
||||
@@ -31,39 +28,64 @@ func NewRequestValidatorZh(response interfaces.ResponseBuilder) interfaces.Reque
|
||||
// 获取中文翻译器
|
||||
trans, _ := uni.GetTranslator("zh")
|
||||
|
||||
// 注册中文翻译
|
||||
zh_translations.RegisterDefaultTranslations(validate, trans)
|
||||
// 注册官方中文翻译
|
||||
zh_translations.RegisterDefaultTranslations(validator.New(), trans)
|
||||
|
||||
// 注册自定义验证器
|
||||
registerCustomValidatorsZh(validate, trans)
|
||||
// 注册自定义翻译
|
||||
registerCustomTranslations(trans)
|
||||
|
||||
return &RequestValidatorZh{
|
||||
validator: validate,
|
||||
translator: trans,
|
||||
response: response,
|
||||
translator: trans,
|
||||
}
|
||||
}
|
||||
|
||||
// registerCustomTranslations 注册自定义翻译
|
||||
func registerCustomTranslations(trans ut.Translator) {
|
||||
// 自定义 eqfield 翻译(更友好的提示)
|
||||
_ = trans.Add("eqfield", "{0}必须与{1}一致", true)
|
||||
|
||||
// 自定义 required 翻译
|
||||
_ = trans.Add("required", "{0}不能为空", true)
|
||||
|
||||
// 自定义 min 翻译
|
||||
_ = trans.Add("min", "{0}长度不能少于{1}位", true)
|
||||
|
||||
// 自定义 max 翻译
|
||||
_ = trans.Add("max", "{0}长度不能超过{1}位", true)
|
||||
|
||||
// 自定义 len 翻译
|
||||
_ = trans.Add("len", "{0}长度必须为{1}位", true)
|
||||
|
||||
// 自定义 email 翻译
|
||||
_ = trans.Add("email", "{0}必须是有效的邮箱地址", true)
|
||||
|
||||
// 自定义手机号翻译
|
||||
_ = trans.Add("phone", "{0}必须是有效的手机号", true)
|
||||
|
||||
// 自定义用户名翻译
|
||||
_ = trans.Add("username", "{0}格式不正确,只能包含字母、数字、下划线,且不能以数字开头", true)
|
||||
|
||||
// 自定义强密码翻译
|
||||
_ = trans.Add("strong_password", "{0}强度不足,必须包含大小写字母和数字,且不少于8位", true)
|
||||
}
|
||||
|
||||
// Validate 验证请求体
|
||||
func (v *RequestValidatorZh) Validate(c *gin.Context, dto interface{}) error {
|
||||
if err := v.validator.Struct(dto); err != nil {
|
||||
validationErrors := v.formatValidationErrorsZh(err)
|
||||
v.response.ValidationError(c, validationErrors)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
// 直接使用 Gin 的绑定和验证
|
||||
return v.BindAndValidate(c, dto)
|
||||
}
|
||||
|
||||
// ValidateQuery 验证查询参数
|
||||
func (v *RequestValidatorZh) ValidateQuery(c *gin.Context, dto interface{}) error {
|
||||
if err := c.ShouldBindQuery(dto); err != nil {
|
||||
v.response.BadRequest(c, "查询参数格式错误", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
if err := v.validator.Struct(dto); err != nil {
|
||||
validationErrors := v.formatValidationErrorsZh(err)
|
||||
v.response.ValidationError(c, validationErrors)
|
||||
// 处理查询参数验证错误
|
||||
if _, ok := err.(validator.ValidationErrors); ok {
|
||||
validationErrors := v.formatValidationErrorsZh(err)
|
||||
v.response.ValidationError(c, validationErrors)
|
||||
} else {
|
||||
v.response.BadRequest(c, "查询参数格式错误", err.Error())
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -72,13 +94,13 @@ func (v *RequestValidatorZh) ValidateQuery(c *gin.Context, dto interface{}) erro
|
||||
// ValidateParam 验证路径参数
|
||||
func (v *RequestValidatorZh) ValidateParam(c *gin.Context, dto interface{}) error {
|
||||
if err := c.ShouldBindUri(dto); err != nil {
|
||||
v.response.BadRequest(c, "路径参数格式错误", err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
if err := v.validator.Struct(dto); err != nil {
|
||||
validationErrors := v.formatValidationErrorsZh(err)
|
||||
v.response.ValidationError(c, validationErrors)
|
||||
// 处理路径参数验证错误
|
||||
if _, ok := err.(validator.ValidationErrors); ok {
|
||||
validationErrors := v.formatValidationErrorsZh(err)
|
||||
v.response.ValidationError(c, validationErrors)
|
||||
} else {
|
||||
v.response.BadRequest(c, "路径参数格式错误", err.Error())
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -86,14 +108,20 @@ func (v *RequestValidatorZh) ValidateParam(c *gin.Context, dto interface{}) erro
|
||||
|
||||
// BindAndValidate 绑定并验证请求
|
||||
func (v *RequestValidatorZh) BindAndValidate(c *gin.Context, dto interface{}) error {
|
||||
// 绑定请求体
|
||||
// 绑定请求体(Gin 会自动进行 binding 标签验证)
|
||||
if err := c.ShouldBindJSON(dto); err != nil {
|
||||
v.response.BadRequest(c, "请求体格式错误", err.Error())
|
||||
// 处理 Gin binding 验证错误
|
||||
if _, ok := err.(validator.ValidationErrors); ok {
|
||||
// 所有验证错误都使用 422 状态码
|
||||
validationErrors := v.formatValidationErrorsZh(err)
|
||||
v.response.ValidationError(c, validationErrors)
|
||||
} else {
|
||||
v.response.BadRequest(c, "请求体格式错误", err.Error())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// 验证数据
|
||||
return v.Validate(c, dto)
|
||||
return nil
|
||||
}
|
||||
|
||||
// formatValidationErrorsZh 格式化验证错误(中文翻译版)
|
||||
@@ -104,15 +132,8 @@ func (v *RequestValidatorZh) formatValidationErrorsZh(err error) map[string][]st
|
||||
for _, fieldError := range validationErrors {
|
||||
fieldName := v.getFieldNameZh(fieldError)
|
||||
|
||||
// 首先尝试使用翻译器获取翻译后的错误消息
|
||||
errorMessage := fieldError.Translate(v.translator)
|
||||
|
||||
// 如果翻译后的消息包含英文字段名,则替换为中文字段名
|
||||
fieldDisplayName := v.getFieldDisplayName(fieldError.Field())
|
||||
if fieldDisplayName != fieldError.Field() {
|
||||
// 替换字段名为中文
|
||||
errorMessage = strings.ReplaceAll(errorMessage, fieldError.Field(), fieldDisplayName)
|
||||
}
|
||||
// 获取友好的中文错误消息
|
||||
errorMessage := v.getFriendlyErrorMessage(fieldError)
|
||||
|
||||
if _, exists := errors[fieldName]; !exists {
|
||||
errors[fieldName] = []string{}
|
||||
@@ -124,6 +145,53 @@ func (v *RequestValidatorZh) formatValidationErrorsZh(err error) map[string][]st
|
||||
return errors
|
||||
}
|
||||
|
||||
// getFriendlyErrorMessage 获取友好的中文错误消息
|
||||
func (v *RequestValidatorZh) getFriendlyErrorMessage(fieldError validator.FieldError) string {
|
||||
field := fieldError.Field()
|
||||
tag := fieldError.Tag()
|
||||
param := fieldError.Param()
|
||||
|
||||
fieldDisplayName := v.getFieldDisplayName(field)
|
||||
|
||||
// 优先使用官方翻译器
|
||||
errorMessage := fieldError.Translate(v.translator)
|
||||
|
||||
// 如果官方翻译成功且不是英文,使用官方翻译
|
||||
if errorMessage != fieldError.Error() {
|
||||
// 替换字段名为中文
|
||||
if fieldDisplayName != fieldError.Field() {
|
||||
errorMessage = strings.ReplaceAll(errorMessage, fieldError.Field(), fieldDisplayName)
|
||||
}
|
||||
return errorMessage
|
||||
}
|
||||
|
||||
// 回退到自定义翻译
|
||||
switch tag {
|
||||
case "required":
|
||||
return fmt.Sprintf("%s不能为空", fieldDisplayName)
|
||||
case "email":
|
||||
return fmt.Sprintf("%s必须是有效的邮箱地址", fieldDisplayName)
|
||||
case "min":
|
||||
return fmt.Sprintf("%s长度不能少于%s位", fieldDisplayName, param)
|
||||
case "max":
|
||||
return fmt.Sprintf("%s长度不能超过%s位", fieldDisplayName, param)
|
||||
case "len":
|
||||
return fmt.Sprintf("%s长度必须为%s位", fieldDisplayName, param)
|
||||
case "eqfield":
|
||||
paramDisplayName := v.getFieldDisplayName(param)
|
||||
return fmt.Sprintf("%s必须与%s一致", fieldDisplayName, paramDisplayName)
|
||||
case "phone":
|
||||
return fmt.Sprintf("%s必须是有效的手机号", fieldDisplayName)
|
||||
case "username":
|
||||
return fmt.Sprintf("%s格式不正确,只能包含字母、数字、下划线,且不能以数字开头", fieldDisplayName)
|
||||
case "strong_password":
|
||||
return fmt.Sprintf("%s强度不足,必须包含大小写字母和数字,且不少于8位", fieldDisplayName)
|
||||
default:
|
||||
// 默认错误消息
|
||||
return fmt.Sprintf("%s格式不正确", fieldDisplayName)
|
||||
}
|
||||
}
|
||||
|
||||
// getFieldNameZh 获取字段名(JSON标签优先)
|
||||
func (v *RequestValidatorZh) getFieldNameZh(fieldError validator.FieldError) string {
|
||||
fieldName := fieldError.Field()
|
||||
@@ -166,129 +234,3 @@ func (v *RequestValidatorZh) toSnakeCase(str string) string {
|
||||
}
|
||||
return strings.ToLower(result.String())
|
||||
}
|
||||
|
||||
// registerCustomValidatorsZh 注册自定义验证器和中文翻译
|
||||
func registerCustomValidatorsZh(v *validator.Validate, trans ut.Translator) {
|
||||
// 注册手机号验证器
|
||||
v.RegisterValidation("phone", validatePhoneZh)
|
||||
v.RegisterTranslation("phone", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("phone", "{0}必须是有效的手机号", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("phone", fe.Field())
|
||||
return t
|
||||
})
|
||||
|
||||
// 注册用户名验证器
|
||||
v.RegisterValidation("username", validateUsernameZh)
|
||||
v.RegisterTranslation("username", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("username", "{0}格式不正确,只能包含字母、数字、下划线,且不能以数字开头", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("username", fe.Field())
|
||||
return t
|
||||
})
|
||||
|
||||
// 注册密码强度验证器
|
||||
v.RegisterValidation("strong_password", validateStrongPasswordZh)
|
||||
v.RegisterTranslation("strong_password", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("strong_password", "{0}强度不足,必须包含大小写字母和数字,且不少于8位", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("strong_password", fe.Field())
|
||||
return t
|
||||
})
|
||||
|
||||
// 自定义eqfield翻译
|
||||
v.RegisterTranslation("eqfield", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("eqfield", "{0}必须等于{1}", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("eqfield", fe.Field(), fe.Param())
|
||||
return t
|
||||
})
|
||||
}
|
||||
|
||||
// validatePhoneZh 验证手机号
|
||||
func validatePhoneZh(fl validator.FieldLevel) bool {
|
||||
phone := fl.Field().String()
|
||||
if phone == "" {
|
||||
return true // 空值由required标签处理
|
||||
}
|
||||
|
||||
// 中国手机号验证:11位,以1开头
|
||||
if len(phone) != 11 {
|
||||
return false
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(phone, "1") {
|
||||
return false
|
||||
}
|
||||
|
||||
// 检查是否全是数字
|
||||
for _, r := range phone {
|
||||
if r < '0' || r > '9' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// validateUsernameZh 验证用户名
|
||||
func validateUsernameZh(fl validator.FieldLevel) bool {
|
||||
username := fl.Field().String()
|
||||
if username == "" {
|
||||
return true // 空值由required标签处理
|
||||
}
|
||||
|
||||
// 用户名规则:3-30个字符,只能包含字母、数字、下划线,不能以数字开头
|
||||
if len(username) < 3 || len(username) > 30 {
|
||||
return false
|
||||
}
|
||||
|
||||
// 不能以数字开头
|
||||
if username[0] >= '0' && username[0] <= '9' {
|
||||
return false
|
||||
}
|
||||
|
||||
// 只能包含字母、数字、下划线
|
||||
for _, r := range username {
|
||||
if !((r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// validateStrongPasswordZh 验证密码强度
|
||||
func validateStrongPasswordZh(fl validator.FieldLevel) bool {
|
||||
password := fl.Field().String()
|
||||
if password == "" {
|
||||
return true // 空值由required标签处理
|
||||
}
|
||||
|
||||
// 密码强度规则:至少8个字符,包含大小写字母、数字
|
||||
if len(password) < 8 {
|
||||
return false
|
||||
}
|
||||
|
||||
hasUpper := false
|
||||
hasLower := false
|
||||
hasDigit := false
|
||||
|
||||
for _, r := range password {
|
||||
switch {
|
||||
case r >= 'A' && r <= 'Z':
|
||||
hasUpper = true
|
||||
case r >= 'a' && r <= 'z':
|
||||
hasLower = true
|
||||
case r >= '0' && r <= '9':
|
||||
hasDigit = true
|
||||
}
|
||||
}
|
||||
|
||||
return hasUpper && hasLower && hasDigit
|
||||
}
|
||||
|
||||
// ValidateStruct 直接验证结构体
|
||||
func (v *RequestValidatorZh) ValidateStruct(dto interface{}) error {
|
||||
return v.validator.Struct(dto)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user