f
This commit is contained in:
246
internal/shared/validator/AUTH_DATE_VALIDATOR.md
Normal file
246
internal/shared/validator/AUTH_DATE_VALIDATOR.md
Normal file
@@ -0,0 +1,246 @@
|
||||
# AuthDate 授权日期验证器
|
||||
|
||||
## 概述
|
||||
|
||||
`authDate` 是一个自定义验证器,用于验证授权日期格式和有效性。该验证器确保日期格式正确,并且日期范围必须包括今天。
|
||||
|
||||
## 验证规则
|
||||
|
||||
### 1. 格式要求
|
||||
- 必须为 `YYYYMMDD-YYYYMMDD` 格式
|
||||
- 两个日期之间用连字符 `-` 分隔
|
||||
- 每个日期必须是8位数字
|
||||
|
||||
### 2. 日期有效性
|
||||
- 开始日期不能晚于结束日期
|
||||
- 日期范围必须包括今天(如果两个日期都是今天也行)
|
||||
- 支持闰年验证
|
||||
- 验证月份和日期的有效性
|
||||
|
||||
### 3. 业务逻辑
|
||||
- 空值由 `required` 标签处理,本验证器返回 `true`
|
||||
- 日期范围必须覆盖今天,确保授权在有效期内
|
||||
|
||||
## 使用示例
|
||||
|
||||
### 在 DTO 中使用
|
||||
|
||||
```go
|
||||
type FLXG0V4BReq struct {
|
||||
Name string `json:"name" validate:"required,name"`
|
||||
IDCard string `json:"id_card" validate:"required,idCard"`
|
||||
AuthDate string `json:"auth_date" validate:"required,authDate"`
|
||||
}
|
||||
```
|
||||
|
||||
### 在结构体中使用
|
||||
|
||||
```go
|
||||
type AuthorizationRequest struct {
|
||||
UserID string `json:"user_id" validate:"required"`
|
||||
AuthDate string `json:"auth_date" validate:"required,authDate"`
|
||||
Scope string `json:"scope" validate:"required"`
|
||||
}
|
||||
```
|
||||
|
||||
## 有效示例
|
||||
|
||||
### ✅ 有效的日期范围
|
||||
|
||||
```json
|
||||
{
|
||||
"auth_date": "20240101-20240131" // 1月1日到1月31日(如果今天是1月15日)
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"auth_date": "20240115-20240115" // 今天到今天
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"auth_date": "20240110-20240120" // 昨天到明天(如果今天是1月15日)
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"auth_date": "20240101-20240201" // 上个月到下个月(如果今天是1月15日)
|
||||
}
|
||||
```
|
||||
|
||||
### ❌ 无效的日期范围
|
||||
|
||||
```json
|
||||
{
|
||||
"auth_date": "20240116-20240120" // 明天到后天(不包括今天)
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"auth_date": "20240101-20240114" // 上个月到昨天(不包括今天)
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"auth_date": "20240131-20240101" // 开始日期晚于结束日期
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"auth_date": "20240101-2024013A" // 非数字字符
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"auth_date": "202401-20240131" // 日期长度不对
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"auth_date": "2024010120240131" // 缺少连字符
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"auth_date": "20240230-20240301" // 无效日期(2月30日)
|
||||
}
|
||||
```
|
||||
|
||||
## 错误消息
|
||||
|
||||
当验证失败时,会返回中文错误消息:
|
||||
|
||||
```
|
||||
"授权日期格式不正确,必须是YYYYMMDD-YYYYMMDD格式,且日期范围必须包括今天"
|
||||
```
|
||||
|
||||
## 测试用例
|
||||
|
||||
验证器包含完整的测试用例,覆盖以下场景:
|
||||
|
||||
### 有效场景
|
||||
- 今天到今天
|
||||
- 昨天到今天
|
||||
- 今天到明天
|
||||
- 上周到今天
|
||||
- 今天到下周
|
||||
- 昨天到明天
|
||||
|
||||
### 无效场景
|
||||
- 明天到后天(不包括今天)
|
||||
- 上周到昨天(不包括今天)
|
||||
- 格式错误(缺少连字符、多个连字符、长度不对、非数字)
|
||||
- 无效日期(2月30日、13月等)
|
||||
- 开始日期晚于结束日期
|
||||
|
||||
## 实现细节
|
||||
|
||||
### 核心验证逻辑
|
||||
|
||||
```go
|
||||
func validateAuthDate(fl validator.FieldLevel) bool {
|
||||
authDate := fl.Field().String()
|
||||
if authDate == "" {
|
||||
return true // 空值由required标签处理
|
||||
}
|
||||
|
||||
// 1. 检查格式:YYYYMMDD-YYYYMMDD
|
||||
parts := strings.Split(authDate, "-")
|
||||
if len(parts) != 2 {
|
||||
return false
|
||||
}
|
||||
|
||||
// 2. 解析日期
|
||||
startDate, err := parseYYYYMMDD(parts[0])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
endDate, err := parseYYYYMMDD(parts[1])
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// 3. 检查日期顺序
|
||||
if startDate.After(endDate) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 4. 检查是否包括今天
|
||||
today := time.Now().Truncate(24 * time.Hour)
|
||||
return !startDate.After(today) && !endDate.Before(today)
|
||||
}
|
||||
```
|
||||
|
||||
### 日期解析
|
||||
|
||||
```go
|
||||
func parseYYYYMMDD(dateStr string) (time.Time, error) {
|
||||
if len(dateStr) != 8 {
|
||||
return time.Time{}, fmt.Errorf("日期格式错误")
|
||||
}
|
||||
|
||||
year, _ := strconv.Atoi(dateStr[:4])
|
||||
month, _ := strconv.Atoi(dateStr[4:6])
|
||||
day, _ := strconv.Atoi(dateStr[6:8])
|
||||
|
||||
date := time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC)
|
||||
|
||||
// 验证日期有效性
|
||||
expectedDateStr := date.Format("20060102")
|
||||
if expectedDateStr != dateStr {
|
||||
return time.Time{}, fmt.Errorf("无效日期")
|
||||
}
|
||||
|
||||
return date, nil
|
||||
}
|
||||
```
|
||||
|
||||
## 注册方式
|
||||
|
||||
验证器已在 `RegisterCustomValidators` 函数中自动注册:
|
||||
|
||||
```go
|
||||
func RegisterCustomValidators(validate *validator.Validate) {
|
||||
// ... 其他验证器
|
||||
validate.RegisterValidation("auth_date", validateAuthDate)
|
||||
}
|
||||
```
|
||||
|
||||
翻译也已自动注册:
|
||||
|
||||
```go
|
||||
validate.RegisterTranslation("auth_date", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("auth_date", "{0}格式不正确,必须是YYYYMMDD-YYYYMMDD格式,且日期范围必须包括今天", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("auth_date", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **时区处理**:验证器使用 UTC 时区进行日期比较
|
||||
2. **空值处理**:空字符串由 `required` 标签处理,本验证器返回 `true`
|
||||
3. **日期精度**:只比较日期部分,忽略时间部分
|
||||
4. **闰年支持**:自动处理闰年验证
|
||||
5. **错误消息**:提供中文错误消息,便于用户理解
|
||||
|
||||
## 运行测试
|
||||
|
||||
```bash
|
||||
# 运行所有 authDate 相关测试
|
||||
go test ./internal/shared/validator -v -run TestValidateAuthDate
|
||||
|
||||
# 运行所有验证器测试
|
||||
go test ./internal/shared/validator -v
|
||||
```
|
||||
256
internal/shared/validator/README.md
Normal file
256
internal/shared/validator/README.md
Normal file
@@ -0,0 +1,256 @@
|
||||
# Validator 验证器包
|
||||
|
||||
这是一个功能完整的验证器包,提供了HTTP请求验证和业务逻辑验证的完整解决方案。
|
||||
|
||||
## 📁 包结构
|
||||
|
||||
```
|
||||
internal/shared/validator/
|
||||
├── validator.go # 统一校验器主逻辑(包含所有功能)
|
||||
├── custom_validators.go # 自定义验证器实现
|
||||
├── translations.go # 中文翻译
|
||||
└── README.md # 使用说明
|
||||
```
|
||||
|
||||
## 🚀 特性
|
||||
|
||||
### 1. HTTP请求验证
|
||||
- 自动绑定和验证请求体、查询参数、路径参数
|
||||
- 中文错误消息
|
||||
- 集成到Gin框架
|
||||
- 统一的错误响应格式
|
||||
|
||||
### 2. 业务逻辑验证
|
||||
- 独立的业务验证方法,可在任何地方调用
|
||||
- 丰富的预定义验证规则
|
||||
- 与标签验证使用相同的校验逻辑
|
||||
|
||||
### 3. 自定义验证规则
|
||||
- 手机号验证 (`phone`)
|
||||
- 强密码验证 (`strong_password`)
|
||||
- 用户名验证 (`username`)
|
||||
- 统一社会信用代码验证 (`social_credit_code`)
|
||||
- 身份证号验证 (`id_card`)
|
||||
- UUID验证 (`uuid`)
|
||||
- URL验证 (`url`)
|
||||
- 产品代码验证 (`product_code`)
|
||||
- 价格验证 (`price`)
|
||||
- 排序方向验证 (`sort_order`)
|
||||
|
||||
## 📖 使用方法
|
||||
|
||||
### 1. HTTP请求验证
|
||||
|
||||
在Handler中使用:
|
||||
|
||||
```go
|
||||
type UserHandler struct {
|
||||
validator interfaces.RequestValidator
|
||||
// ... 其他依赖
|
||||
}
|
||||
|
||||
func (h *UserHandler) Register(c *gin.Context) {
|
||||
var cmd commands.RegisterUserCommand
|
||||
|
||||
// 自动绑定和验证请求体
|
||||
if err := h.validator.BindAndValidate(c, &cmd); err != nil {
|
||||
return // 验证失败会自动返回错误响应
|
||||
}
|
||||
|
||||
// 验证查询参数
|
||||
var query queries.UserListQuery
|
||||
if err := h.validator.ValidateQuery(c, &query); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 验证路径参数
|
||||
var param queries.UserIDParam
|
||||
if err := h.validator.ValidateParam(c, ¶m); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// 继续业务逻辑...
|
||||
}
|
||||
```
|
||||
|
||||
### 2. DTO定义
|
||||
|
||||
在DTO中使用验证标签:
|
||||
|
||||
```go
|
||||
type RegisterUserCommand struct {
|
||||
Phone string `json:"phone" binding:"required,phone"`
|
||||
Password string `json:"password" binding:"required,strong_password"`
|
||||
ConfirmPassword string `json:"confirm_password" binding:"required,eqfield=Password"`
|
||||
Email string `json:"email" binding:"omitempty,email"`
|
||||
Username string `json:"username" binding:"required,username"`
|
||||
}
|
||||
|
||||
type EnterpriseInfoCommand struct {
|
||||
CompanyName string `json:"company_name" binding:"required,min=2,max=100"`
|
||||
UnifiedSocialCode string `json:"unified_social_code" binding:"required,social_credit_code"`
|
||||
LegalPersonName string `json:"legal_person_name" binding:"required,min=2,max=20"`
|
||||
LegalPersonID string `json:"legal_person_id" binding:"required,id_card"`
|
||||
LegalPersonPhone string `json:"legal_person_phone" binding:"required,phone"`
|
||||
}
|
||||
|
||||
type ProductCommand struct {
|
||||
Name string `json:"name" binding:"required,min=2,max=100"`
|
||||
Code string `json:"code" binding:"required,product_code"`
|
||||
Price float64 `json:"price" binding:"price,min=0"`
|
||||
CategoryID string `json:"category_id" binding:"required,uuid"`
|
||||
WebsiteURL string `json:"website_url" binding:"omitempty,url"`
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 业务逻辑验证
|
||||
|
||||
在Service中使用统一的校验方法:
|
||||
|
||||
```go
|
||||
import "hyapi-server/internal/shared/validator"
|
||||
|
||||
type UserService struct {
|
||||
// 不再需要单独的businessValidator
|
||||
}
|
||||
|
||||
func (s *UserService) ValidateUserData(phone, password string) error {
|
||||
// 直接使用包级别的校验方法
|
||||
if err := validator.ValidatePhone(phone); err != nil {
|
||||
return fmt.Errorf("手机号验证失败: %w", err)
|
||||
}
|
||||
|
||||
if err := validator.ValidatePassword(password); err != nil {
|
||||
return fmt.Errorf("密码验证失败: %w", err)
|
||||
}
|
||||
|
||||
// 也可以使用结构体验证
|
||||
userData := struct {
|
||||
Phone string `validate:"phone"`
|
||||
Password string `validate:"strong_password"`
|
||||
}{Phone: phone, Password: password}
|
||||
|
||||
if err := validator.GetGlobalValidator().Struct(userData); err != nil {
|
||||
return fmt.Errorf("用户数据验证失败: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *UserService) ValidateEnterpriseInfo(code, idCard string) error {
|
||||
// 直接使用包级别的校验方法
|
||||
if err := validator.ValidateSocialCreditCode(code); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := validator.ValidateIDCard(idCard); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
### 4. 处理器中的验证
|
||||
|
||||
在API处理器中,可以直接使用结构体验证:
|
||||
|
||||
```go
|
||||
// 在 flxg5a3b_processor.go 中
|
||||
func ProcessFLXG5A3BRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||
var paramsDto dto.FLXG5A3BReq
|
||||
if err := json.Unmarshal(params, ¶msDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
// 使用统一的校验器验证
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
|
||||
// ... 继续业务逻辑
|
||||
}
|
||||
```
|
||||
|
||||
## 🔧 可用的验证规则
|
||||
|
||||
### 标准验证规则
|
||||
- `required` - 必填
|
||||
- `omitempty` - 可为空
|
||||
- `min=n` - 最小长度/值
|
||||
- `max=n` - 最大长度/值
|
||||
- `len=n` - 固定长度
|
||||
- `email` - 邮箱格式
|
||||
- `oneof=a b c` - 枚举值
|
||||
- `eqfield=Field` - 字段相等
|
||||
- `gt=n` - 大于某值
|
||||
|
||||
### 自定义验证规则
|
||||
- `phone` - 中国手机号 (1[3-9]xxxxxxxxx)
|
||||
- `strong_password` - 强密码 (8位以上,包含大小写字母和数字)
|
||||
- `username` - 用户名 (字母开头,3-20位字母数字下划线)
|
||||
- `social_credit_code` - 统一社会信用代码 (18位)
|
||||
- `id_card` - 身份证号 (18位)
|
||||
- `uuid` - UUID格式
|
||||
- `url` - URL格式
|
||||
- `product_code` - 产品代码 (3-50位字母数字下划线连字符)
|
||||
- `price` - 价格 (非负数)
|
||||
- `sort_order` - 排序方向 (asc/desc)
|
||||
|
||||
## 🌐 错误消息
|
||||
|
||||
所有错误消息都已本地化为中文:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 422,
|
||||
"message": "请求参数验证失败",
|
||||
"data": null,
|
||||
"errors": {
|
||||
"phone": ["手机号必须是有效的手机号"],
|
||||
"password": ["密码强度不足,必须包含大小写字母和数字,且不少于8位"],
|
||||
"confirm_password": ["确认密码必须与密码一致"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🔄 依赖注入
|
||||
|
||||
在 `container.go` 中已配置:
|
||||
|
||||
```go
|
||||
fx.Provide(
|
||||
validator.NewRequestValidator, // HTTP请求验证器
|
||||
),
|
||||
```
|
||||
|
||||
## 📝 最佳实践
|
||||
|
||||
1. **DTO验证**: 在DTO中使用binding标签进行声明式验证
|
||||
2. **业务验证**: 在业务逻辑中直接使用 `validator.ValidateXXX()` 方法
|
||||
3. **统一性**: 所有校验都使用同一个校验器实例,确保规则一致
|
||||
4. **错误处理**: 验证错误会自动返回统一格式的HTTP响应
|
||||
|
||||
## 🧪 测试示例
|
||||
|
||||
```go
|
||||
// 测试自定义校验规则
|
||||
func TestCustomValidators(t *testing.T) {
|
||||
validator.InitGlobalValidator()
|
||||
|
||||
// 测试手机号验证
|
||||
err := validator.ValidatePhone("13800138000")
|
||||
if err != nil {
|
||||
t.Errorf("有效手机号验证失败: %v", err)
|
||||
}
|
||||
|
||||
err = validator.ValidatePhone("12345")
|
||||
if err == nil {
|
||||
t.Error("无效手机号应该验证失败")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
这个验证器包现在提供了完整的统一解决方案,既可以用于HTTP请求的自动验证,也可以在业务逻辑中进行程序化验证,确保数据的完整性和正确性。
|
||||
1039
internal/shared/validator/custom_validators.go
Normal file
1039
internal/shared/validator/custom_validators.go
Normal file
File diff suppressed because it is too large
Load Diff
420
internal/shared/validator/translations.go
Normal file
420
internal/shared/validator/translations.go
Normal file
@@ -0,0 +1,420 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
ut "github.com/go-playground/universal-translator"
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
// RegisterCustomTranslations 注册所有自定义翻译
|
||||
func RegisterCustomTranslations(validate *validator.Validate, trans ut.Translator) {
|
||||
// 注册标准字段翻译
|
||||
registerStandardTranslations(validate, trans)
|
||||
|
||||
// 注册自定义字段翻译
|
||||
registerCustomFieldTranslations(validate, trans)
|
||||
}
|
||||
|
||||
// registerStandardTranslations 注册标准翻译
|
||||
func registerStandardTranslations(validate *validator.Validate, trans ut.Translator) {
|
||||
// 必填字段翻译
|
||||
validate.RegisterTranslation("required", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("required", "{0}不能为空", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("required", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// 字段相等翻译
|
||||
validate.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", getFieldDisplayName(fe.Field()), getFieldDisplayName(fe.Param()))
|
||||
return t
|
||||
})
|
||||
|
||||
// 最小长度翻译
|
||||
validate.RegisterTranslation("min", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("min", "{0}长度不能少于{1}位", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("min", getFieldDisplayName(fe.Field()), fe.Param())
|
||||
return t
|
||||
})
|
||||
|
||||
// 最大长度翻译
|
||||
validate.RegisterTranslation("max", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("max", "{0}长度不能超过{1}位", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("max", getFieldDisplayName(fe.Field()), fe.Param())
|
||||
return t
|
||||
})
|
||||
|
||||
// 固定长度翻译
|
||||
validate.RegisterTranslation("len", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("len", "{0}长度必须为{1}位", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("len", getFieldDisplayName(fe.Field()), fe.Param())
|
||||
return t
|
||||
})
|
||||
|
||||
// 邮箱翻译
|
||||
validate.RegisterTranslation("email", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("email", "{0}必须是有效的邮箱地址", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("email", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// 枚举值翻译
|
||||
validate.RegisterTranslation("oneof", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("oneof", "{0}必须是以下值之一: {1}", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("oneof", getFieldDisplayName(fe.Field()), fe.Param())
|
||||
return t
|
||||
})
|
||||
|
||||
// 大于翻译
|
||||
validate.RegisterTranslation("gt", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("gt", "{0}必须大于{1}", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("gt", getFieldDisplayName(fe.Field()), fe.Param())
|
||||
return t
|
||||
})
|
||||
}
|
||||
|
||||
// registerCustomFieldTranslations 注册自定义字段翻译
|
||||
func registerCustomFieldTranslations(validate *validator.Validate, trans ut.Translator) {
|
||||
// 手机号翻译
|
||||
validate.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", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// 用户名翻译
|
||||
validate.RegisterTranslation("username", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("username", "{0}格式不正确,只能包含字母、数字、下划线,且必须以字母开头,长度3-20位", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("username", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// 强密码翻译
|
||||
validate.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", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// 统一社会信用代码翻译
|
||||
validate.RegisterTranslation("social_credit_code", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("social_credit_code", "{0}格式不正确,必须是18位统一社会信用代码", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("social_credit_code", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// 身份证号翻译
|
||||
validate.RegisterTranslation("id_card", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("id_card", "{0}格式不正确,必须是18位身份证号", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("id_card", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// 价格翻译
|
||||
validate.RegisterTranslation("price", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("price", "{0}必须是非负数", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("price", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// 排序方向翻译
|
||||
validate.RegisterTranslation("sort_order", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("sort_order", "{0}必须是 asc 或 desc", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("sort_order", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// 产品代码翻译
|
||||
validate.RegisterTranslation("product_code", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("product_code", "{0}格式不正确,只能包含字母、数字、下划线、连字符、中英文括号,长度3-50位", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("product_code", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// UUID翻译
|
||||
validate.RegisterTranslation("uuid", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("uuid", "{0}必须是有效的UUID格式", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("uuid", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// URL翻译
|
||||
validate.RegisterTranslation("url", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("url", "{0}必须是有效的URL地址", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("url", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// 企业邮箱翻译
|
||||
validate.RegisterTranslation("enterprise_email", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("enterprise_email", "{0}必须是有效的企业邮箱地址", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("enterprise_email", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// 企业地址翻译
|
||||
validate.RegisterTranslation("enterprise_address", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("enterprise_address", "{0}长度必须在2-200字符之间,且不能只包含空格", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("enterprise_address", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// IP地址翻译
|
||||
validate.RegisterTranslation("ip", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("ip", "{0}必须是有效的IPv4地址格式", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("ip", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// 授权日期翻译(兼容两种标签)
|
||||
validate.RegisterTranslation("auth_date", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("auth_date", "{0}格式不正确,必须是YYYYMMDD-YYYYMMDD格式,且日期范围必须包括今天", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("auth_date", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
validate.RegisterTranslation("validAuthDate", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("validAuthDate", "{0}格式不正确,必须是YYYYMMDD-YYYYMMDD格式,且日期范围必须包括今天", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("validAuthDate", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
validate.RegisterTranslation("validDateRange", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("validDateRange", "{0}格式不正确,必须是YYYYMMDD-YYYYMMDD格式,且开始日期不能晚于结束日期", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("validDateRange", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// 时间范围翻译
|
||||
validate.RegisterTranslation("validTimeRange", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("validTimeRange", "{0}格式不正确,必须是HH:MM-HH:MM格式", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("validTimeRange", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// 日期翻译
|
||||
validate.RegisterTranslation("validDate", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("validDate", "{0}格式不正确,必须是YYYY-MM-DD格式", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("validDate", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// 统一社会信用代码翻译(validUSCI)
|
||||
validate.RegisterTranslation("validUSCI", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("validUSCI", "{0}格式不正确,必须是18位统一社会信用代码", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("validUSCI", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// 手机号翻译(validMobileNo)
|
||||
validate.RegisterTranslation("validMobileNo", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("validMobileNo", "{0}格式不正确,必须是11位手机号", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("validMobileNo", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// 银行卡号翻译
|
||||
validate.RegisterTranslation("validBankCard", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("validBankCard", "{0}格式不正确,必须是有效的银行卡号", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("validBankCard", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// 姓名翻译(validName)
|
||||
validate.RegisterTranslation("validName", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("validName", "{0}格式不正确,必须包含至少一个汉字或英文字母,长度1-50字符", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("validName", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// 授权书URL翻译
|
||||
validate.RegisterTranslation("authorization_url", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("authorization_url", "{0}必须是有效的URL地址,且文件类型必须是PDF、JPG、JPEG、PNG或BMP格式", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("authorization_url", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// 唯一标识翻译
|
||||
validate.RegisterTranslation("validUniqueID", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("validUniqueID", "{0}格式不正确,只能包含字母、数字、下划线和连字符,且长度不能超过32位", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("validUniqueID", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// 回调地址翻译
|
||||
validate.RegisterTranslation("validReturnURL", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("validReturnURL", "{0}必须是有效的URL地址,且长度不能超过500字符", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("validReturnURL", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// 身份证号翻译(validIDCard)
|
||||
validate.RegisterTranslation("validIDCard", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("validIDCard", "{0}格式不正确,必须是18位身份证号", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("validIDCard", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// 手机类型翻译
|
||||
validate.RegisterTranslation("validMobileType", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("validMobileType", "{0}格式不正确,必须是:移动、联通、电信、广电或虚拟运营商", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("validMobileType", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// 授权链接翻译
|
||||
validate.RegisterTranslation("validAuthorizationURL", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("validAuthorizationURL", "{0}必须是有效的URL地址,且文件类型必须是PDF、JPG、JPEG、PNG或BMP格式", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("validAuthorizationURL", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// 企业名称翻译
|
||||
validate.RegisterTranslation("validEnterpriseName", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("validEnterpriseName", "{0}格式不正确,必须包含至少一个汉字,长度2-100字符", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("validEnterpriseName", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
validate.RegisterTranslation("enterprise_name", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("enterprise_name", "{0}格式不正确,必须包含至少一个汉字,长度2-100字符", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("enterprise_name", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
}
|
||||
|
||||
// getFieldDisplayName 获取字段显示名称(中文)
|
||||
func getFieldDisplayName(field string) string {
|
||||
fieldNames := map[string]string{
|
||||
"phone": "手机号",
|
||||
"password": "密码",
|
||||
"confirm_password": "确认密码",
|
||||
"old_password": "原密码",
|
||||
"new_password": "新密码",
|
||||
"confirm_new_password": "确认新密码",
|
||||
"code": "验证码",
|
||||
"username": "用户名",
|
||||
"email": "邮箱",
|
||||
"enterprise_email": "企业邮箱",
|
||||
"enterprise_address": "企业地址",
|
||||
"ip": "IP地址",
|
||||
"auth_date": "授权日期",
|
||||
"unique_id": "唯一标识",
|
||||
"return_url": "回调地址",
|
||||
"display_name": "显示名称",
|
||||
"scene": "使用场景",
|
||||
"Password": "密码",
|
||||
"NewPassword": "新密码",
|
||||
"ConfirmPassword": "确认密码",
|
||||
"name": "名称",
|
||||
"Name": "名称",
|
||||
"description": "描述",
|
||||
"Description": "描述",
|
||||
"price": "价格",
|
||||
"Price": "价格",
|
||||
"category_id": "分类ID",
|
||||
"CategoryID": "分类ID",
|
||||
"product_id": "产品ID",
|
||||
"ProductID": "产品ID",
|
||||
"user_id": "用户ID",
|
||||
"UserID": "用户ID",
|
||||
"page": "页码",
|
||||
"Page": "页码",
|
||||
"page_size": "每页数量",
|
||||
"PageSize": "每页数量",
|
||||
"keyword": "关键词",
|
||||
"Keyword": "关键词",
|
||||
"sort_by": "排序字段",
|
||||
"SortBy": "排序字段",
|
||||
"sort_order": "排序方向",
|
||||
"SortOrder": "排序方向",
|
||||
"company_name": "企业名称",
|
||||
"CompanyName": "企业名称",
|
||||
"unified_social_code": "统一社会信用代码",
|
||||
"UnifiedSocialCode": "统一社会信用代码",
|
||||
"legal_person_name": "法定代表人姓名",
|
||||
"LegalPersonName": "法定代表人姓名",
|
||||
"legal_person_id": "法定代表人身份证号",
|
||||
"LegalPersonID": "法定代表人身份证号",
|
||||
"legal_person_phone": "法定代表人手机号",
|
||||
"LegalPersonPhone": "法定代表人手机号",
|
||||
"verification_code": "验证码",
|
||||
"VerificationCode": "验证码",
|
||||
"contract_url": "合同URL",
|
||||
"ContractURL": "合同URL",
|
||||
"authorization_url": "授权书地址",
|
||||
"AuthorizationURL": "授权书地址",
|
||||
"amount": "金额",
|
||||
"Amount": "金额",
|
||||
"balance": "余额",
|
||||
"Balance": "余额",
|
||||
"is_active": "是否激活",
|
||||
"IsActive": "是否激活",
|
||||
"is_enabled": "是否启用",
|
||||
"IsEnabled": "是否启用",
|
||||
"is_visible": "是否可见",
|
||||
"IsVisible": "是否可见",
|
||||
"is_package": "是否组合包",
|
||||
"IsPackage": "是否组合包",
|
||||
"Code": "编号",
|
||||
"content": "内容",
|
||||
"Content": "内容",
|
||||
"sort": "排序",
|
||||
"Sort": "排序",
|
||||
"seo_title": "SEO标题",
|
||||
"SEOTitle": "SEO标题",
|
||||
"seo_description": "SEO描述",
|
||||
"SEODescription": "SEO描述",
|
||||
"seo_keywords": "SEO关键词",
|
||||
"SEOKeywords": "SEO关键词",
|
||||
"id": "ID",
|
||||
"ID": "ID",
|
||||
"ids": "ID列表",
|
||||
"IDs": "ID列表",
|
||||
"id_card": "身份证号",
|
||||
"IDCard": "身份证号",
|
||||
}
|
||||
|
||||
if displayName, exists := fieldNames[field]; exists {
|
||||
return displayName
|
||||
}
|
||||
return field
|
||||
}
|
||||
253
internal/shared/validator/validator.go
Normal file
253
internal/shared/validator/validator.go
Normal file
@@ -0,0 +1,253 @@
|
||||
package validator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"hyapi-server/internal/shared/interfaces"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
"github.com/go-playground/locales/zh"
|
||||
ut "github.com/go-playground/universal-translator"
|
||||
"github.com/go-playground/validator/v10"
|
||||
zh_translations "github.com/go-playground/validator/v10/translations/zh"
|
||||
)
|
||||
|
||||
// 全局变量声明
|
||||
var (
|
||||
globalValidator *validator.Validate
|
||||
globalTranslator ut.Translator
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
// InitGlobalValidator 初始化全局校验器(线程安全)
|
||||
func InitGlobalValidator() {
|
||||
once.Do(func() {
|
||||
// 1. 创建新的校验器实例
|
||||
globalValidator = validator.New()
|
||||
|
||||
// 2. 创建中文翻译器
|
||||
zhLocale := zh.New()
|
||||
uni := ut.New(zhLocale, zhLocale)
|
||||
globalTranslator, _ = uni.GetTranslator("zh")
|
||||
|
||||
// 3. 注册官方中文翻译
|
||||
zh_translations.RegisterDefaultTranslations(globalValidator, globalTranslator)
|
||||
|
||||
// 4. 注册自定义校验规则
|
||||
RegisterCustomValidators(globalValidator)
|
||||
|
||||
// 5. 注册自定义中文翻译
|
||||
RegisterCustomTranslations(globalValidator, globalTranslator)
|
||||
|
||||
// 6. 设置到Gin全局校验器(确保Gin使用我们的校验器)
|
||||
if binding.Validator.Engine() != nil {
|
||||
// 如果Gin已经初始化,则替换其校验器
|
||||
ginValidator := binding.Validator.Engine().(*validator.Validate)
|
||||
*ginValidator = *globalValidator
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// GetGlobalValidator 获取全局校验器实例
|
||||
func GetGlobalValidator() *validator.Validate {
|
||||
if globalValidator == nil {
|
||||
InitGlobalValidator()
|
||||
}
|
||||
return globalValidator
|
||||
}
|
||||
|
||||
// GetGlobalTranslator 获取全局翻译器实例
|
||||
func GetGlobalTranslator() ut.Translator {
|
||||
if globalTranslator == nil {
|
||||
InitGlobalValidator()
|
||||
}
|
||||
return globalTranslator
|
||||
}
|
||||
|
||||
// RequestValidator HTTP请求验证器
|
||||
type RequestValidator struct {
|
||||
response interfaces.ResponseBuilder
|
||||
translator ut.Translator
|
||||
validator *validator.Validate
|
||||
}
|
||||
|
||||
// NewRequestValidator 创建HTTP请求验证器
|
||||
func NewRequestValidator(response interfaces.ResponseBuilder) interfaces.RequestValidator {
|
||||
// 确保全局校验器已初始化
|
||||
InitGlobalValidator()
|
||||
|
||||
return &RequestValidator{
|
||||
response: response,
|
||||
translator: globalTranslator, // 使用全局翻译器
|
||||
validator: globalValidator, // 使用全局校验器
|
||||
}
|
||||
}
|
||||
|
||||
// Validate 验证请求体
|
||||
func (v *RequestValidator) Validate(c *gin.Context, dto interface{}) error {
|
||||
return v.BindAndValidate(c, dto)
|
||||
}
|
||||
|
||||
// ValidateQuery 验证查询参数
|
||||
func (v *RequestValidator) ValidateQuery(c *gin.Context, dto interface{}) error {
|
||||
if err := c.ShouldBindQuery(dto); err != nil {
|
||||
if validationErrors, ok := err.(validator.ValidationErrors); ok {
|
||||
validationErrorsMap := v.formatValidationErrors(validationErrors)
|
||||
v.response.ValidationError(c, validationErrorsMap)
|
||||
} else {
|
||||
v.response.BadRequest(c, "查询参数格式错误")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateParam 验证路径参数
|
||||
func (v *RequestValidator) ValidateParam(c *gin.Context, dto interface{}) error {
|
||||
if err := c.ShouldBindUri(dto); err != nil {
|
||||
if validationErrors, ok := err.(validator.ValidationErrors); ok {
|
||||
validationErrorsMap := v.formatValidationErrors(validationErrors)
|
||||
v.response.ValidationError(c, validationErrorsMap)
|
||||
} else {
|
||||
v.response.BadRequest(c, "路径参数格式错误")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// BindAndValidate 绑定并验证请求
|
||||
func (v *RequestValidator) BindAndValidate(c *gin.Context, dto interface{}) error {
|
||||
if err := c.ShouldBindJSON(dto); err != nil {
|
||||
if validationErrors, ok := err.(validator.ValidationErrors); ok {
|
||||
validationErrorsMap := v.formatValidationErrors(validationErrors)
|
||||
v.response.ValidationError(c, validationErrorsMap)
|
||||
} else {
|
||||
v.response.BadRequest(c, "请求体格式错误")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// formatValidationErrors 格式化验证错误
|
||||
func (v *RequestValidator) formatValidationErrors(validationErrors validator.ValidationErrors) map[string][]string {
|
||||
errors := make(map[string][]string)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// getErrorMessage 获取错误消息
|
||||
func (v *RequestValidator) getErrorMessage(fieldError validator.FieldError) string {
|
||||
fieldDisplayName := getFieldDisplayName(fieldError.Field())
|
||||
|
||||
// 优先使用翻译器
|
||||
errorMessage := fieldError.Translate(v.translator)
|
||||
if errorMessage != fieldError.Error() {
|
||||
// 替换字段名为中文
|
||||
if fieldDisplayName != fieldError.Field() {
|
||||
errorMessage = strings.ReplaceAll(errorMessage, fieldError.Field(), fieldDisplayName)
|
||||
}
|
||||
return errorMessage
|
||||
}
|
||||
|
||||
// 回退到手动翻译
|
||||
return v.getFallbackErrorMessage(fieldError, fieldDisplayName)
|
||||
}
|
||||
|
||||
// getFallbackErrorMessage 获取回退错误消息
|
||||
func (v *RequestValidator) getFallbackErrorMessage(fieldError validator.FieldError, fieldDisplayName string) string {
|
||||
tag := fieldError.Tag()
|
||||
param := fieldError.Param()
|
||||
|
||||
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 := getFieldDisplayName(param)
|
||||
return fmt.Sprintf("%s必须与%s一致", fieldDisplayName, paramDisplayName)
|
||||
case "phone":
|
||||
return fmt.Sprintf("%s必须是有效的手机号", fieldDisplayName)
|
||||
case "username":
|
||||
return fmt.Sprintf("%s格式不正确,只能包含字母、数字、下划线,且必须以字母开头,长度3-20位", fieldDisplayName)
|
||||
case "strong_password":
|
||||
return fmt.Sprintf("%s强度不足,必须包含大小写字母和数字,且不少于8位", fieldDisplayName)
|
||||
case "social_credit_code":
|
||||
return fmt.Sprintf("%s格式不正确,必须是18位统一社会信用代码", fieldDisplayName)
|
||||
case "id_card":
|
||||
return fmt.Sprintf("%s格式不正确,必须是18位身份证号", fieldDisplayName)
|
||||
case "price":
|
||||
return fmt.Sprintf("%s必须是非负数", fieldDisplayName)
|
||||
case "sort_order":
|
||||
return fmt.Sprintf("%s必须是 asc 或 desc", fieldDisplayName)
|
||||
case "product_code":
|
||||
return fmt.Sprintf("%s格式不正确,只能包含字母、数字、下划线、连字符、中英文括号,长度3-50位", fieldDisplayName)
|
||||
case "uuid":
|
||||
return fmt.Sprintf("%s必须是有效的UUID格式", fieldDisplayName)
|
||||
case "url":
|
||||
return fmt.Sprintf("%s必须是有效的URL地址", fieldDisplayName)
|
||||
case "oneof":
|
||||
return fmt.Sprintf("%s必须是以下值之一: %s", fieldDisplayName, param)
|
||||
case "gt":
|
||||
return fmt.Sprintf("%s必须大于%s", fieldDisplayName, param)
|
||||
default:
|
||||
return fmt.Sprintf("%s格式不正确", fieldDisplayName)
|
||||
}
|
||||
}
|
||||
|
||||
// getFieldName 获取字段名
|
||||
func (v *RequestValidator) getFieldName(fieldError validator.FieldError) string {
|
||||
fieldName := fieldError.Field()
|
||||
return v.toSnakeCase(fieldName)
|
||||
}
|
||||
|
||||
// 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())
|
||||
}
|
||||
|
||||
// GetValidator 获取validator实例(用于业务逻辑)
|
||||
func (v *RequestValidator) GetValidator() *validator.Validate {
|
||||
return v.validator
|
||||
}
|
||||
|
||||
// ValidateValue 验证单个值(用于业务逻辑)
|
||||
func (v *RequestValidator) ValidateValue(field interface{}, tag string) error {
|
||||
return v.validator.Var(field, tag)
|
||||
}
|
||||
|
||||
// ValidateStruct 验证结构体(用于业务逻辑)
|
||||
func (v *RequestValidator) ValidateStruct(s interface{}) error {
|
||||
return v.validator.Struct(s)
|
||||
}
|
||||
Reference in New Issue
Block a user