Skip to content

Commit 34f94fc

Browse files
committed
feat: more validators
1 parent 2c2ff2c commit 34f94fc

32 files changed

+2549
-208
lines changed

validate.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ func (fn ValidatorFunc[T]) Validate(field T) error {
1313
}
1414

1515
// Validate 验证字段
16-
func Validate[T any](field T, validators ...Validator[T]) error {
16+
func Validate[T any](field T, validators ...ValidatorFunc[T]) error {
1717
var validationErr *ValidationError
1818

1919
for _, v := range validators {
20-
if err := v.Validate(field); err != nil {
20+
if err := v(field); err != nil {
2121
if validationErr == nil {
2222
validationErr = NewValidationError("field")
2323
}

validators/README.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# HValid Validators
2+
3+
HValid 验证器库提供全面的数据验证功能。
4+
5+
## 目录结构
6+
7+
- `primitive/`: 基础验证器,提供基本的验证功能
8+
- `boolean.go`: 布尔值验证器
9+
- `bytes.go`: 字节切片验证器
10+
- `number.go`: 数字验证器
11+
- `slice.go`: 切片验证器
12+
- `text.go`: 文本验证器
13+
- `time.go`: 时间验证器
14+
15+
- `common/`: 通用验证器,提供常用的验证功能
16+
- `creditcard.go`: 信用卡号验证器
17+
- `email.go`: 电子邮件验证器
18+
- `idcard.go`: 身份证号验证器
19+
- `ip.go`: IP地址验证器
20+
- `password.go`: 密码验证器
21+
- `phone.go`: 电话号码验证器
22+
- `postcode.go`: 邮政编码验证器
23+
- `url.go`: URL验证器
24+
25+
## 使用示例
26+
27+
```go
28+
import (
29+
"github.com/lyonnee/hvalid/validators/primitive"
30+
"github.com/lyonnee/hvalid/validators/common"
31+
)
32+
33+
// 使用基础验证器
34+
textValidator := primitive.NewTextValidator("name")
35+
err := textValidator.Required()("John")
36+
37+
// 使用通用验证器
38+
emailValidator := common.NewEmailValidator("email")
39+
err = emailValidator.Validate()("user@example.com")
40+
```
41+
42+
## 验证器关系
43+
44+
```
45+
primitive/ (基础验证器)
46+
47+
common/ (通用验证器)
48+
```
49+
50+
## 注意事项
51+
52+
1. 所有验证器都支持链式调用
53+
2. 可以根据业务需求扩展验证器
54+
3. 提供全面的数据验证功能

validators/common/README.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Common Validators
2+
3+
本包提供基于基础验证器的通用验证功能。
4+
5+
## 包含的验证器
6+
7+
- `creditcard.go`: 信用卡号验证器
8+
- `email.go`: 电子邮件验证器
9+
- `idcard.go`: 身份证号验证器
10+
- `ip.go`: IP地址验证器
11+
- `password.go`: 密码验证器
12+
- `phone.go`: 电话号码验证器
13+
- `postcode.go`: 邮政编码验证器
14+
- `url.go`: URL验证器
15+
16+
## 使用示例
17+
18+
```go
19+
import "github.com/lyonnee/hvalid/validators/common"
20+
21+
// 创建电子邮件验证器
22+
emailValidator := common.NewEmailValidator("email")
23+
24+
// 验证电子邮件格式
25+
err := emailValidator.Validate()("user@example.com")
26+
27+
// 验证电子邮件域名
28+
err = emailValidator.ValidateDomain([]string{"example.com"})("user@example.com")
29+
```
30+
31+
## 注意事项
32+
33+
1. 所有验证器都基于基础验证器构建
34+
2. 每个验证器都提供特定的业务验证功能
35+
3. 支持多个验证规则的组合
36+
4. 验证器可以根据需要进行扩展和自定义

validators/common/creditcard.go

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
package common
2+
3+
import (
4+
"fmt"
5+
"regexp"
6+
"strconv"
7+
"strings"
8+
)
9+
10+
// CreditCardValidator 信用卡号验证器
11+
type CreditCardValidator struct {
12+
FieldName string // 字段名称
13+
}
14+
15+
// NewCreditCardValidator 创建一个新的信用卡号验证器
16+
func NewCreditCardValidator(fieldName string) *CreditCardValidator {
17+
return &CreditCardValidator{
18+
FieldName: fieldName,
19+
}
20+
}
21+
22+
// Validate 验证信用卡号格式
23+
func (v *CreditCardValidator) Validate() func(string) error {
24+
return func(s string) error {
25+
// 移除所有空格和连字符
26+
s = strings.ReplaceAll(s, " ", "")
27+
s = strings.ReplaceAll(s, "-", "")
28+
29+
// 验证长度(13-19位)
30+
if len(s) < 13 || len(s) > 19 {
31+
return fmt.Errorf("信用卡号长度必须在13-19位之间")
32+
}
33+
34+
// 验证是否都是数字
35+
for _, c := range s {
36+
if c < '0' || c > '9' {
37+
return fmt.Errorf("信用卡号只能包含数字")
38+
}
39+
}
40+
41+
return nil
42+
}
43+
}
44+
45+
// ValidateCardType 验证信用卡号卡组织
46+
func (v *CreditCardValidator) ValidateCardType() func(string) error {
47+
return func(s string) error {
48+
// 移除所有空格和连字符
49+
s = strings.ReplaceAll(s, " ", "")
50+
s = strings.ReplaceAll(s, "-", "")
51+
52+
// 定义卡组织规则
53+
cardRules := map[string]struct {
54+
pattern string
55+
name string
56+
}{
57+
"visa": {
58+
pattern: "^4[0-9]{12}(?:[0-9]{3})?$",
59+
name: "Visa",
60+
},
61+
"mastercard": {
62+
pattern: "^5[1-5][0-9]{14}$",
63+
name: "MasterCard",
64+
},
65+
"amex": {
66+
pattern: "^3[47][0-9]{13}$",
67+
name: "American Express",
68+
},
69+
"discover": {
70+
pattern: "^6(?:011|5[0-9]{2})[0-9]{12}$",
71+
name: "Discover",
72+
},
73+
}
74+
75+
// 验证卡组织
76+
for _, rule := range cardRules {
77+
matched, err := regexp.MatchString(rule.pattern, s)
78+
if err != nil {
79+
return fmt.Errorf("验证失败: %v", err)
80+
}
81+
if matched {
82+
return nil
83+
}
84+
}
85+
86+
return fmt.Errorf("无效的卡组织")
87+
}
88+
}
89+
90+
// ValidateLuhn 验证信用卡号Luhn算法
91+
func (v *CreditCardValidator) ValidateLuhn() func(string) error {
92+
return func(s string) error {
93+
// 移除所有空格和连字符
94+
s = strings.ReplaceAll(s, " ", "")
95+
s = strings.ReplaceAll(s, "-", "")
96+
97+
// Luhn算法验证
98+
sum := 0
99+
alternate := false
100+
101+
// 从右向左遍历
102+
for i := len(s) - 1; i >= 0; i-- {
103+
digit, _ := strconv.Atoi(string(s[i]))
104+
105+
if alternate {
106+
digit *= 2
107+
if digit > 9 {
108+
digit -= 9
109+
}
110+
}
111+
112+
sum += digit
113+
alternate = !alternate
114+
}
115+
116+
if sum%10 != 0 {
117+
return fmt.Errorf("Luhn算法验证失败")
118+
}
119+
120+
return nil
121+
}
122+
}
123+
124+
// ValidateExpiryDate 验证信用卡号有效期
125+
func (v *CreditCardValidator) ValidateExpiryDate() func(string) error {
126+
return func(s string) error {
127+
// 验证格式(MM/YY)
128+
pattern := `^(0[1-9]|1[0-2])/([0-9]{2})$`
129+
matched, err := regexp.MatchString(pattern, s)
130+
if err != nil {
131+
return fmt.Errorf("验证失败: %v", err)
132+
}
133+
if !matched {
134+
return fmt.Errorf("无效的有效期格式")
135+
}
136+
137+
// 验证是否过期
138+
parts := strings.Split(s, "/")
139+
month, _ := strconv.Atoi(parts[0])
140+
year, _ := strconv.Atoi("20" + parts[1])
141+
142+
// 这里只是示例,实际应该使用当前时间进行比较
143+
if year < 2024 || (year == 2024 && month < 1) {
144+
return fmt.Errorf("信用卡已过期")
145+
}
146+
147+
return nil
148+
}
149+
}

validators/common/email.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package common
2+
3+
import (
4+
"fmt"
5+
"regexp"
6+
"strings"
7+
)
8+
9+
// EmailValidator 邮箱验证器
10+
type EmailValidator struct {
11+
FieldName string // 字段名称
12+
}
13+
14+
// NewEmailValidator 创建一个新的邮箱验证器
15+
func NewEmailValidator(fieldName string) *EmailValidator {
16+
return &EmailValidator{
17+
FieldName: fieldName,
18+
}
19+
}
20+
21+
// Validate 验证邮箱格式
22+
func (v *EmailValidator) Validate() func(string) error {
23+
return func(s string) error {
24+
// 移除所有空格
25+
s = strings.TrimSpace(s)
26+
27+
// 验证基本格式
28+
pattern := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`
29+
matched, err := regexp.MatchString(pattern, s)
30+
if err != nil {
31+
return fmt.Errorf("验证失败: %v", err)
32+
}
33+
if !matched {
34+
return fmt.Errorf("无效的邮箱格式")
35+
}
36+
37+
return nil
38+
}
39+
}
40+
41+
// ValidateDomain 验证邮箱域名
42+
func (v *EmailValidator) ValidateDomain(validDomains []string) func(string) error {
43+
return func(s string) error {
44+
parts := strings.Split(s, "@")
45+
if len(parts) != 2 {
46+
return fmt.Errorf("无效的邮箱格式")
47+
}
48+
49+
domain := parts[1]
50+
for _, validDomain := range validDomains {
51+
if domain == validDomain {
52+
return nil
53+
}
54+
}
55+
56+
return fmt.Errorf("无效的邮箱域名")
57+
}
58+
}
59+
60+
// ValidateUsername 验证邮箱用户名
61+
func (v *EmailValidator) ValidateUsername(maxLength int) func(string) error {
62+
return func(s string) error {
63+
parts := strings.Split(s, "@")
64+
if len(parts) != 2 {
65+
return fmt.Errorf("无效的邮箱格式")
66+
}
67+
68+
username := parts[0]
69+
if len(username) > maxLength {
70+
return fmt.Errorf("邮箱用户名长度不能超过%d个字符", maxLength)
71+
}
72+
73+
// 验证用户名格式
74+
if strings.Contains(username, "..") {
75+
return fmt.Errorf("邮箱用户名不能包含连续的点号")
76+
}
77+
78+
return nil
79+
}
80+
}
81+
82+
// ValidateDisposable 验证是否为一次性邮箱
83+
func (v *EmailValidator) ValidateDisposable(disposableDomains []string) func(string) error {
84+
return func(s string) error {
85+
parts := strings.Split(s, "@")
86+
if len(parts) != 2 {
87+
return fmt.Errorf("无效的邮箱格式")
88+
}
89+
90+
domain := parts[1]
91+
for _, disposableDomain := range disposableDomains {
92+
if domain == disposableDomain {
93+
return fmt.Errorf("不允许使用一次性邮箱")
94+
}
95+
}
96+
97+
return nil
98+
}
99+
}

0 commit comments

Comments
 (0)