| 
									
										
										
										
											2025-07-20 20:53:26 +08:00
										 |  |  |  | # Validator 验证器包
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 这是一个功能完整的验证器包,提供了HTTP请求验证和业务逻辑验证的完整解决方案。 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ## 📁 包结构
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | internal/shared/validator/ | 
					
						
							| 
									
										
										
										
											2025-08-27 22:19:19 +08:00
										 |  |  |  | ├── validator.go           # 统一校验器主逻辑(包含所有功能) | 
					
						
							| 
									
										
										
										
											2025-07-20 20:53:26 +08:00
										 |  |  |  | ├── custom_validators.go   # 自定义验证器实现 | 
					
						
							|  |  |  |  | ├── translations.go        # 中文翻译 | 
					
						
							|  |  |  |  | └── README.md            # 使用说明 | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ## 🚀 特性
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ### 1. HTTP请求验证
 | 
					
						
							|  |  |  |  | - 自动绑定和验证请求体、查询参数、路径参数 | 
					
						
							|  |  |  |  | - 中文错误消息 | 
					
						
							|  |  |  |  | - 集成到Gin框架 | 
					
						
							|  |  |  |  | - 统一的错误响应格式 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ### 2. 业务逻辑验证
 | 
					
						
							| 
									
										
										
										
											2025-08-27 22:19:19 +08:00
										 |  |  |  | - 独立的业务验证方法,可在任何地方调用 | 
					
						
							| 
									
										
										
										
											2025-07-20 20:53:26 +08:00
										 |  |  |  | - 丰富的预定义验证规则 | 
					
						
							| 
									
										
										
										
											2025-08-27 22:19:19 +08:00
										 |  |  |  | - 与标签验证使用相同的校验逻辑 | 
					
						
							| 
									
										
										
										
											2025-07-20 20:53:26 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | ### 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. 业务逻辑验证
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-27 22:19:19 +08:00
										 |  |  |  | 在Service中使用统一的校验方法: | 
					
						
							| 
									
										
										
										
											2025-07-20 20:53:26 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | ```go | 
					
						
							|  |  |  |  | import "tyapi-server/internal/shared/validator" | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | type UserService struct { | 
					
						
							| 
									
										
										
										
											2025-08-27 22:19:19 +08:00
										 |  |  |  |     // 不再需要单独的businessValidator | 
					
						
							| 
									
										
										
										
											2025-07-20 20:53:26 +08:00
										 |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | func (s *UserService) ValidateUserData(phone, password string) error { | 
					
						
							| 
									
										
										
										
											2025-08-27 22:19:19 +08:00
										 |  |  |  |     // 直接使用包级别的校验方法 | 
					
						
							|  |  |  |  |     if err := validator.ValidatePhone(phone); err != nil { | 
					
						
							| 
									
										
										
										
											2025-07-20 20:53:26 +08:00
										 |  |  |  |         return fmt.Errorf("手机号验证失败: %w", err) | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |      | 
					
						
							| 
									
										
										
										
											2025-08-27 22:19:19 +08:00
										 |  |  |  |     if err := validator.ValidatePassword(password); err != nil { | 
					
						
							| 
									
										
										
										
											2025-07-20 20:53:26 +08:00
										 |  |  |  |         return fmt.Errorf("密码验证失败: %w", err) | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |      | 
					
						
							| 
									
										
										
										
											2025-08-27 22:19:19 +08:00
										 |  |  |  |     // 也可以使用结构体验证 | 
					
						
							|  |  |  |  |     userData := struct { | 
					
						
							|  |  |  |  |         Phone    string `validate:"phone"` | 
					
						
							|  |  |  |  |         Password string `validate:"strong_password"` | 
					
						
							|  |  |  |  |     }{Phone: phone, Password: password} | 
					
						
							|  |  |  |  |      | 
					
						
							|  |  |  |  |     if err := validator.GetGlobalValidator().Struct(userData); err != nil { | 
					
						
							| 
									
										
										
										
											2025-07-20 20:53:26 +08:00
										 |  |  |  |         return fmt.Errorf("用户数据验证失败: %w", err) | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |      | 
					
						
							|  |  |  |  |     return nil | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | func (s *UserService) ValidateEnterpriseInfo(code, idCard string) error { | 
					
						
							| 
									
										
										
										
											2025-08-27 22:19:19 +08:00
										 |  |  |  |     // 直接使用包级别的校验方法 | 
					
						
							|  |  |  |  |     if err := validator.ValidateSocialCreditCode(code); err != nil { | 
					
						
							| 
									
										
										
										
											2025-07-20 20:53:26 +08:00
										 |  |  |  |         return err | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |      | 
					
						
							| 
									
										
										
										
											2025-08-27 22:19:19 +08:00
										 |  |  |  |     if err := validator.ValidateIDCard(idCard); err != nil { | 
					
						
							| 
									
										
										
										
											2025-07-20 20:53:26 +08:00
										 |  |  |  |         return err | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |      | 
					
						
							|  |  |  |  |     return nil | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-27 22:19:19 +08:00
										 |  |  |  | ### 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) | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |      | 
					
						
							|  |  |  |  |     // ... 继续业务逻辑 | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | ``` | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-20 20:53:26 +08:00
										 |  |  |  | ## 🔧 可用的验证规则
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | ### 标准验证规则
 | 
					
						
							|  |  |  |  | - `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标签进行声明式验证 | 
					
						
							| 
									
										
										
										
											2025-08-27 22:19:19 +08:00
										 |  |  |  | 2. **业务验证**: 在业务逻辑中直接使用 `validator.ValidateXXX()` 方法 | 
					
						
							|  |  |  |  | 3. **统一性**: 所有校验都使用同一个校验器实例,确保规则一致 | 
					
						
							|  |  |  |  | 4. **错误处理**: 验证错误会自动返回统一格式的HTTP响应 | 
					
						
							| 
									
										
										
										
											2025-07-20 20:53:26 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | ## 🧪 测试示例
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-27 22:19:19 +08:00
										 |  |  |  | ```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("无效手机号应该验证失败") | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | ``` | 
					
						
							| 
									
										
										
										
											2025-07-20 20:53:26 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | --- | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-27 22:19:19 +08:00
										 |  |  |  | 这个验证器包现在提供了完整的统一解决方案,既可以用于HTTP请求的自动验证,也可以在业务逻辑中进行程序化验证,确保数据的完整性和正确性。  |