246 lines
5.4 KiB
Markdown
246 lines
5.4 KiB
Markdown
|
|
# 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
|
|||
|
|
```
|