fix
This commit is contained in:
@@ -329,31 +329,31 @@ func (s *FormConfigServiceImpl) getFieldType(fieldType reflect.Type, validation
|
|||||||
func (s *FormConfigServiceImpl) generateFieldLabel(jsonTag string) string {
|
func (s *FormConfigServiceImpl) generateFieldLabel(jsonTag string) string {
|
||||||
// 将下划线命名转换为中文标签
|
// 将下划线命名转换为中文标签
|
||||||
labelMap := map[string]string{
|
labelMap := map[string]string{
|
||||||
"mobile_no": "手机号码",
|
"mobile_no": "手机号码",
|
||||||
"id_card": "身份证号",
|
"id_card": "身份证号",
|
||||||
"name": "姓名",
|
"name": "姓名",
|
||||||
"man_name": "男方姓名",
|
"man_name": "男方姓名",
|
||||||
"woman_name": "女方姓名",
|
"woman_name": "女方姓名",
|
||||||
"man_id_card": "男方身份证",
|
"man_id_card": "男方身份证",
|
||||||
"woman_id_card": "女方身份证",
|
"woman_id_card": "女方身份证",
|
||||||
"ent_name": "企业名称",
|
"ent_name": "企业名称",
|
||||||
"legal_person": "法人姓名",
|
"legal_person": "法人姓名",
|
||||||
"ent_code": "企业代码",
|
"ent_code": "企业代码",
|
||||||
"auth_date": "授权日期",
|
"auth_date": "授权日期",
|
||||||
"time_range": "时间范围",
|
"time_range": "时间范围",
|
||||||
"authorized": "是否授权",
|
"authorized": "是否授权",
|
||||||
"authorization_url": "授权链接",
|
"authorization_url": "授权链接",
|
||||||
"unique_id": "唯一标识",
|
"unique_id": "唯一标识",
|
||||||
"return_url": "返回链接",
|
"return_url": "返回链接",
|
||||||
"mobile_type": "手机类型",
|
"mobile_type": "手机类型",
|
||||||
"start_date": "开始日期",
|
"start_date": "开始日期",
|
||||||
"years": "年数",
|
"years": "年数",
|
||||||
"bank_card": "银行卡号",
|
"bank_card": "银行卡号",
|
||||||
"user_type": "关系类型",
|
"user_type": "关系类型",
|
||||||
"vehicle_type": "车辆类型",
|
"vehicle_type": "车辆类型",
|
||||||
"page_num": "页码",
|
"page_num": "页码",
|
||||||
"page_size": "每页数量",
|
"page_size": "每页数量",
|
||||||
"use_scenario": "使用场景",
|
"use_scenario": "使用场景",
|
||||||
"auth_authorize_file_code": "授权文件编码",
|
"auth_authorize_file_code": "授权文件编码",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -368,29 +368,29 @@ func (s *FormConfigServiceImpl) generateFieldLabel(jsonTag string) string {
|
|||||||
// generateExampleValue 生成示例值
|
// generateExampleValue 生成示例值
|
||||||
func (s *FormConfigServiceImpl) generateExampleValue(fieldType reflect.Type, jsonTag string) string {
|
func (s *FormConfigServiceImpl) generateExampleValue(fieldType reflect.Type, jsonTag string) string {
|
||||||
exampleMap := map[string]string{
|
exampleMap := map[string]string{
|
||||||
"mobile_no": "13800138000",
|
"mobile_no": "13800138000",
|
||||||
"id_card": "110101199001011234",
|
"id_card": "110101199001011234",
|
||||||
"name": "张三",
|
"name": "张三",
|
||||||
"man_name": "张三",
|
"man_name": "张三",
|
||||||
"woman_name": "李四",
|
"woman_name": "李四",
|
||||||
"ent_name": "示例企业有限公司",
|
"ent_name": "示例企业有限公司",
|
||||||
"legal_person": "王五",
|
"legal_person": "王五",
|
||||||
"ent_code": "91110000123456789X",
|
"ent_code": "91110000123456789X",
|
||||||
"auth_date": "20240101-20241231",
|
"auth_date": "20240101-20241231",
|
||||||
"time_range": "09:00-18:00",
|
"time_range": "09:00-18:00",
|
||||||
"authorized": "1",
|
"authorized": "1",
|
||||||
"years": "5",
|
"years": "5",
|
||||||
"bank_card": "6222021234567890123",
|
"bank_card": "6222021234567890123",
|
||||||
"mobile_type": "移动",
|
"mobile_type": "移动",
|
||||||
"start_date": "2024-01-01",
|
"start_date": "2024-01-01",
|
||||||
"unique_id": "UNIQUE123456",
|
"unique_id": "UNIQUE123456",
|
||||||
"return_url": "https://example.com/return",
|
"return_url": "https://example.com/return",
|
||||||
"authorization_url": "https://example.com/auth20250101.pdf",
|
"authorization_url": "https://example.com/auth20250101.pdf 注意:请不要使用示例链接,示例链接仅作为参考格式。必须为实际的被查询人授权具有法律效益的授权书文件链接,如访问不到或为不实授权书将追究责任。协议必须为http https",
|
||||||
"user_type": "1",
|
"user_type": "1",
|
||||||
"vehicle_type": "0",
|
"vehicle_type": "0",
|
||||||
"page_num": "1",
|
"page_num": "1",
|
||||||
"page_size": "10",
|
"page_size": "10",
|
||||||
"use_scenario": "1",
|
"use_scenario": "1",
|
||||||
"auth_authorize_file_code": "AUTH123456",
|
"auth_authorize_file_code": "AUTH123456",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -414,29 +414,29 @@ func (s *FormConfigServiceImpl) generateExampleValue(fieldType reflect.Type, jso
|
|||||||
// generatePlaceholder 生成占位符
|
// generatePlaceholder 生成占位符
|
||||||
func (s *FormConfigServiceImpl) generatePlaceholder(jsonTag string, fieldType string) string {
|
func (s *FormConfigServiceImpl) generatePlaceholder(jsonTag string, fieldType string) string {
|
||||||
placeholderMap := map[string]string{
|
placeholderMap := map[string]string{
|
||||||
"mobile_no": "请输入11位手机号码",
|
"mobile_no": "请输入11位手机号码",
|
||||||
"id_card": "请输入18位身份证号码",
|
"id_card": "请输入18位身份证号码",
|
||||||
"name": "请输入真实姓名",
|
"name": "请输入真实姓名",
|
||||||
"man_name": "请输入男方真实姓名",
|
"man_name": "请输入男方真实姓名",
|
||||||
"woman_name": "请输入女方真实姓名",
|
"woman_name": "请输入女方真实姓名",
|
||||||
"ent_name": "请输入企业全称",
|
"ent_name": "请输入企业全称",
|
||||||
"legal_person": "请输入法人真实姓名",
|
"legal_person": "请输入法人真实姓名",
|
||||||
"ent_code": "请输入统一社会信用代码",
|
"ent_code": "请输入统一社会信用代码",
|
||||||
"auth_date": "请输入授权日期范围(YYYYMMDD-YYYYMMDD)",
|
"auth_date": "请输入授权日期范围(YYYYMMDD-YYYYMMDD)",
|
||||||
"time_range": "请输入时间范围(HH:MM-HH:MM)",
|
"time_range": "请输入时间范围(HH:MM-HH:MM)",
|
||||||
"authorized": "请选择是否授权",
|
"authorized": "请选择是否授权",
|
||||||
"years": "请输入查询年数(0-100)",
|
"years": "请输入查询年数(0-100)",
|
||||||
"bank_card": "请输入银行卡号",
|
"bank_card": "请输入银行卡号",
|
||||||
"mobile_type": "请选择手机类型",
|
"mobile_type": "请选择手机类型",
|
||||||
"start_date": "请选择开始日期",
|
"start_date": "请选择开始日期",
|
||||||
"unique_id": "请输入唯一标识",
|
"unique_id": "请输入唯一标识",
|
||||||
"return_url": "请输入返回链接",
|
"return_url": "请输入返回链接",
|
||||||
"authorization_url": "请输入授权链接",
|
"authorization_url": "请输入授权链接",
|
||||||
"user_type": "请选择关系类型",
|
"user_type": "请选择关系类型",
|
||||||
"vehicle_type": "请选择车辆类型",
|
"vehicle_type": "请选择车辆类型",
|
||||||
"page_num": "请输入页码",
|
"page_num": "请输入页码",
|
||||||
"page_size": "请输入每页数量(1-100)",
|
"page_size": "请输入每页数量(1-100)",
|
||||||
"use_scenario": "请选择使用场景",
|
"use_scenario": "请选择使用场景",
|
||||||
"auth_authorize_file_code": "请输入授权文件编码",
|
"auth_authorize_file_code": "请输入授权文件编码",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -462,29 +462,29 @@ func (s *FormConfigServiceImpl) generatePlaceholder(jsonTag string, fieldType st
|
|||||||
// generateDescription 生成字段描述
|
// generateDescription 生成字段描述
|
||||||
func (s *FormConfigServiceImpl) generateDescription(jsonTag string, validation string) string {
|
func (s *FormConfigServiceImpl) generateDescription(jsonTag string, validation string) string {
|
||||||
descMap := map[string]string{
|
descMap := map[string]string{
|
||||||
"mobile_no": "请输入11位手机号码",
|
"mobile_no": "请输入11位手机号码",
|
||||||
"id_card": "请输入18位身份证号码",
|
"id_card": "请输入18位身份证号码",
|
||||||
"name": "请输入真实姓名",
|
"name": "请输入真实姓名",
|
||||||
"man_name": "请输入男方真实姓名",
|
"man_name": "请输入男方真实姓名",
|
||||||
"woman_name": "请输入女方真实姓名",
|
"woman_name": "请输入女方真实姓名",
|
||||||
"ent_name": "请输入企业全称",
|
"ent_name": "请输入企业全称",
|
||||||
"legal_person": "请输入法人真实姓名",
|
"legal_person": "请输入法人真实姓名",
|
||||||
"ent_code": "请输入统一社会信用代码",
|
"ent_code": "请输入统一社会信用代码",
|
||||||
"auth_date": "请输入授权日期范围,格式:YYYYMMDD-YYYYMMDD,且日期范围必须包括今天",
|
"auth_date": "请输入授权日期范围,格式:YYYYMMDD-YYYYMMDD,且日期范围必须包括今天",
|
||||||
"time_range": "请输入时间范围,格式:HH:MM-HH:MM",
|
"time_range": "请输入时间范围,格式:HH:MM-HH:MM",
|
||||||
"authorized": "请输入是否授权:0-未授权,1-已授权",
|
"authorized": "请输入是否授权:0-未授权,1-已授权",
|
||||||
"years": "请输入查询年数(0-100)",
|
"years": "请输入查询年数(0-100)",
|
||||||
"bank_card": "请输入银行卡号",
|
"bank_card": "请输入银行卡号",
|
||||||
"mobile_type": "请选择手机类型",
|
"mobile_type": "请选择手机类型",
|
||||||
"start_date": "请选择开始日期",
|
"start_date": "请选择开始日期",
|
||||||
"unique_id": "请输入唯一标识",
|
"unique_id": "请输入唯一标识",
|
||||||
"return_url": "请输入返回链接",
|
"return_url": "请输入返回链接",
|
||||||
"authorization_url": "请输入授权链接",
|
"authorization_url": "请输入授权链接",
|
||||||
"user_type": "关系类型:1-ETC开户人;2-车辆所有人;3-ETC经办人(默认1-ETC开户人)",
|
"user_type": "关系类型:1-ETC开户人;2-车辆所有人;3-ETC经办人(默认1-ETC开户人)",
|
||||||
"vehicle_type": "车辆类型:0-客车;1-货车;2-全部(默认查全部)",
|
"vehicle_type": "车辆类型:0-客车;1-货车;2-全部(默认查全部)",
|
||||||
"page_num": "请输入页码,从1开始",
|
"page_num": "请输入页码,从1开始",
|
||||||
"page_size": "请输入每页数量,范围1-100",
|
"page_size": "请输入每页数量,范围1-100",
|
||||||
"use_scenario": "使用场景:1-信贷审核;2-保险评估;3-招聘背景调查;4-其他业务场景;99-其他",
|
"use_scenario": "使用场景:1-信贷审核;2-保险评估;3-招聘背景调查;4-其他业务场景;99-其他",
|
||||||
"auth_authorize_file_code": "请输入授权文件编码",
|
"auth_authorize_file_code": "请输入授权文件编码",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"tyapi-server/internal/domains/api/services/processors"
|
"tyapi-server/internal/domains/api/services/processors"
|
||||||
@@ -136,11 +137,37 @@ func (cs *CombService) processSingleSubProduct(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// combineResults 组合所有子产品的结果
|
// combineResults 组合所有子产品的结果
|
||||||
|
// 只要至少有一个子产品成功,就返回成功结果(部分成功也算成功)
|
||||||
|
// 只有当所有子产品都失败时,才返回错误
|
||||||
func (cs *CombService) combineResults(results []*processors.SubProductResult) (*processors.CombinedResult, error) {
|
func (cs *CombService) combineResults(results []*processors.SubProductResult) (*processors.CombinedResult, error) {
|
||||||
|
// 检查是否至少有一个成功的子产品
|
||||||
|
hasSuccess := false
|
||||||
|
for _, result := range results {
|
||||||
|
if result.Success {
|
||||||
|
hasSuccess = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 构建组合结果
|
// 构建组合结果
|
||||||
combinedResult := &processors.CombinedResult{
|
combinedResult := &processors.CombinedResult{
|
||||||
Responses: results,
|
Responses: results,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果所有子产品都失败,返回错误
|
||||||
|
if !hasSuccess && len(results) > 0 {
|
||||||
|
// 构建错误信息,包含所有失败的原因
|
||||||
|
errorMessages := make([]string, 0, len(results))
|
||||||
|
for _, result := range results {
|
||||||
|
if result.Error != "" {
|
||||||
|
errorMessages = append(errorMessages, fmt.Sprintf("%s: %s", result.ApiCode, result.Error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
errorMsg := fmt.Sprintf("组合包所有子产品调用失败: %s", strings.Join(errorMessages, "; "))
|
||||||
|
return nil, fmt.Errorf(errorMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 至少有一个成功,返回成功结果
|
||||||
return combinedResult, nil
|
return combinedResult, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -58,10 +59,27 @@ func (a *AlicloudService) CallAPI(path string, params map[string]interface{}) (r
|
|||||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
|
||||||
req.Header.Set("Authorization", "APPCODE "+a.config.AppCode)
|
req.Header.Set("Authorization", "APPCODE "+a.config.AppCode)
|
||||||
|
|
||||||
// 发送请求
|
// 发送请求,超时时间设置为60秒
|
||||||
client := &http.Client{}
|
client := &http.Client{
|
||||||
|
Timeout: 60 * time.Second,
|
||||||
|
}
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// 检查是否是超时错误
|
||||||
|
isTimeout := false
|
||||||
|
if netErr, ok := err.(interface{ Timeout() bool }); ok && netErr.Timeout() {
|
||||||
|
isTimeout = true
|
||||||
|
} else if errStr := err.Error();
|
||||||
|
errStr == "context deadline exceeded" ||
|
||||||
|
errStr == "timeout" ||
|
||||||
|
errStr == "Client.Timeout exceeded" ||
|
||||||
|
errStr == "net/http: request canceled" {
|
||||||
|
isTimeout = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if isTimeout {
|
||||||
|
return nil, fmt.Errorf("%w: API请求超时: %s", ErrDatasource, err.Error())
|
||||||
|
}
|
||||||
return nil, fmt.Errorf("%w: %s", ErrSystem, err.Error())
|
return nil, fmt.Errorf("%w: %s", ErrSystem, err.Error())
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ func NewWeChatWorkService(webhookURL, secret string, logger *zap.Logger) *WeChat
|
|||||||
return &WeChatWorkService{
|
return &WeChatWorkService{
|
||||||
webhookURL: webhookURL,
|
webhookURL: webhookURL,
|
||||||
secret: secret,
|
secret: secret,
|
||||||
timeout: 30 * time.Second,
|
timeout: 60 * time.Second,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -385,7 +385,25 @@ func (s *WeChatWorkService) sendMessage(ctx context.Context, message map[string]
|
|||||||
// 发送请求
|
// 发送请求
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("发送请求失败: %w", err)
|
// 检查是否是超时错误
|
||||||
|
isTimeout := false
|
||||||
|
if ctx.Err() == context.DeadlineExceeded {
|
||||||
|
isTimeout = true
|
||||||
|
} else if netErr, ok := err.(interface{ Timeout() bool }); ok && netErr.Timeout() {
|
||||||
|
isTimeout = true
|
||||||
|
} else if errStr := err.Error();
|
||||||
|
errStr == "context deadline exceeded" ||
|
||||||
|
errStr == "timeout" ||
|
||||||
|
errStr == "Client.Timeout exceeded" ||
|
||||||
|
errStr == "net/http: request canceled" {
|
||||||
|
isTimeout = true
|
||||||
|
}
|
||||||
|
|
||||||
|
errorMsg := "发送请求失败"
|
||||||
|
if isTimeout {
|
||||||
|
errorMsg = "发送请求超时"
|
||||||
|
}
|
||||||
|
return fmt.Errorf("%s: %w", errorMsg, err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ func NewBaiduOCRService(apiKey, secretKey string, logger *zap.Logger) *BaiduOCRS
|
|||||||
apiKey: apiKey,
|
apiKey: apiKey,
|
||||||
secretKey: secretKey,
|
secretKey: secretKey,
|
||||||
endpoint: "https://aip.baidubce.com",
|
endpoint: "https://aip.baidubce.com",
|
||||||
timeout: 30 * time.Second,
|
timeout: 60 * time.Second,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -258,6 +258,23 @@ func (s *BaiduOCRService) sendRequest(ctx context.Context, method, url string, b
|
|||||||
// 发送请求
|
// 发送请求
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// 检查是否是超时错误
|
||||||
|
isTimeout := false
|
||||||
|
if ctx.Err() == context.DeadlineExceeded {
|
||||||
|
isTimeout = true
|
||||||
|
} else if netErr, ok := err.(interface{ Timeout() bool }); ok && netErr.Timeout() {
|
||||||
|
isTimeout = true
|
||||||
|
} else if errStr := err.Error();
|
||||||
|
errStr == "context deadline exceeded" ||
|
||||||
|
errStr == "timeout" ||
|
||||||
|
errStr == "Client.Timeout exceeded" ||
|
||||||
|
errStr == "net/http: request canceled" {
|
||||||
|
isTimeout = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if isTimeout {
|
||||||
|
return nil, fmt.Errorf("API请求超时: %w", err)
|
||||||
|
}
|
||||||
return nil, fmt.Errorf("发送请求失败: %w", err)
|
return nil, fmt.Errorf("发送请求失败: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
@@ -443,7 +460,7 @@ func (s *BaiduOCRService) extractWords(result map[string]interface{}) []string {
|
|||||||
func (s *BaiduOCRService) downloadImage(ctx context.Context, imageURL string) ([]byte, error) {
|
func (s *BaiduOCRService) downloadImage(ctx context.Context, imageURL string) ([]byte, error) {
|
||||||
// 创建HTTP客户端
|
// 创建HTTP客户端
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
Timeout: 30 * time.Second,
|
Timeout: 60 * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建请求
|
// 创建请求
|
||||||
|
|||||||
@@ -285,9 +285,9 @@ func (s *QiNiuStorageService) UploadFromReader(ctx context.Context, reader io.Re
|
|||||||
func (s *QiNiuStorageService) DownloadFile(ctx context.Context, fileURL string) ([]byte, error) {
|
func (s *QiNiuStorageService) DownloadFile(ctx context.Context, fileURL string) ([]byte, error) {
|
||||||
s.logger.Info("开始从七牛云下载文件", zap.String("file_url", fileURL))
|
s.logger.Info("开始从七牛云下载文件", zap.String("file_url", fileURL))
|
||||||
|
|
||||||
// 创建HTTP客户端
|
// 创建HTTP客户端,超时时间设置为60秒
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
Timeout: 30 * time.Second,
|
Timeout: 60 * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建请求
|
// 创建请求
|
||||||
@@ -299,11 +299,29 @@ func (s *QiNiuStorageService) DownloadFile(ctx context.Context, fileURL string)
|
|||||||
// 发送请求
|
// 发送请求
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.logger.Error("下载文件失败",
|
// 检查是否是超时错误
|
||||||
|
isTimeout := false
|
||||||
|
if ctx.Err() == context.DeadlineExceeded {
|
||||||
|
isTimeout = true
|
||||||
|
} else if netErr, ok := err.(interface{ Timeout() bool }); ok && netErr.Timeout() {
|
||||||
|
isTimeout = true
|
||||||
|
} else if errStr := err.Error();
|
||||||
|
errStr == "context deadline exceeded" ||
|
||||||
|
errStr == "timeout" ||
|
||||||
|
errStr == "Client.Timeout exceeded" ||
|
||||||
|
errStr == "net/http: request canceled" {
|
||||||
|
isTimeout = true
|
||||||
|
}
|
||||||
|
|
||||||
|
errorMsg := "下载文件失败"
|
||||||
|
if isTimeout {
|
||||||
|
errorMsg = "下载文件超时"
|
||||||
|
}
|
||||||
|
s.logger.Error(errorMsg,
|
||||||
zap.String("file_url", fileURL),
|
zap.String("file_url", fileURL),
|
||||||
zap.Error(err),
|
zap.Error(err),
|
||||||
)
|
)
|
||||||
return nil, fmt.Errorf("下载文件失败: %w", err)
|
return nil, fmt.Errorf("%s: %w", errorMsg, err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ type TianYanChaResponse struct {
|
|||||||
// NewTianYanChaService 创建天眼查服务实例
|
// NewTianYanChaService 创建天眼查服务实例
|
||||||
func NewTianYanChaService(baseURL, token string, timeout time.Duration) *TianYanChaService {
|
func NewTianYanChaService(baseURL, token string, timeout time.Duration) *TianYanChaService {
|
||||||
if timeout == 0 {
|
if timeout == 0 {
|
||||||
timeout = 30 * time.Second
|
timeout = 60 * time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
return &TianYanChaService{
|
return &TianYanChaService{
|
||||||
@@ -112,6 +112,23 @@ func (t *TianYanChaService) CallAPI(ctx context.Context, apiCode string, params
|
|||||||
client := &http.Client{Timeout: t.config.Timeout}
|
client := &http.Client{Timeout: t.config.Timeout}
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// 检查是否是超时错误
|
||||||
|
isTimeout := false
|
||||||
|
if ctx.Err() == context.DeadlineExceeded {
|
||||||
|
isTimeout = true
|
||||||
|
} else if netErr, ok := err.(interface{ Timeout() bool }); ok && netErr.Timeout() {
|
||||||
|
isTimeout = true
|
||||||
|
} else if errStr := err.Error();
|
||||||
|
errStr == "context deadline exceeded" ||
|
||||||
|
errStr == "timeout" ||
|
||||||
|
errStr == "Client.Timeout exceeded" ||
|
||||||
|
errStr == "net/http: request canceled" {
|
||||||
|
isTimeout = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if isTimeout {
|
||||||
|
return nil, errors.Join(ErrDatasource, fmt.Errorf("API请求超时: %v", err))
|
||||||
|
}
|
||||||
return nil, errors.Join(ErrDatasource, fmt.Errorf("API 请求异常: %v", err))
|
return nil, errors.Join(ErrDatasource, fmt.Errorf("API 请求异常: %v", err))
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|||||||
@@ -120,11 +120,31 @@ func (w *WestDexService) CallAPI(ctx context.Context, code string, reqData map[s
|
|||||||
// 设置请求头
|
// 设置请求头
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
// 发送请求
|
// 发送请求,超时时间设置为60秒
|
||||||
client := &http.Client{}
|
client := &http.Client{
|
||||||
|
Timeout: 60 * time.Second,
|
||||||
|
}
|
||||||
httpResp, clientDoErr := client.Do(req)
|
httpResp, clientDoErr := client.Do(req)
|
||||||
if clientDoErr != nil {
|
if clientDoErr != nil {
|
||||||
err = errors.Join(ErrSystem, clientDoErr)
|
// 检查是否是超时错误
|
||||||
|
isTimeout := false
|
||||||
|
if ctx.Err() == context.DeadlineExceeded {
|
||||||
|
isTimeout = true
|
||||||
|
} else if netErr, ok := clientDoErr.(interface{ Timeout() bool }); ok && netErr.Timeout() {
|
||||||
|
isTimeout = true
|
||||||
|
} else if errStr := clientDoErr.Error();
|
||||||
|
errStr == "context deadline exceeded" ||
|
||||||
|
errStr == "timeout" ||
|
||||||
|
errStr == "Client.Timeout exceeded" ||
|
||||||
|
errStr == "net/http: request canceled" {
|
||||||
|
isTimeout = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if isTimeout {
|
||||||
|
err = errors.Join(ErrDatasource, fmt.Errorf("API请求超时: %v", clientDoErr))
|
||||||
|
} else {
|
||||||
|
err = errors.Join(ErrSystem, clientDoErr)
|
||||||
|
}
|
||||||
if w.logger != nil {
|
if w.logger != nil {
|
||||||
w.logger.LogError(requestID, transactionID, code, err, reqData)
|
w.logger.LogError(requestID, transactionID, code, err, reqData)
|
||||||
}
|
}
|
||||||
@@ -273,11 +293,31 @@ func (w *WestDexService) G05HZ01CallAPI(ctx context.Context, code string, reqDat
|
|||||||
// 设置请求头
|
// 设置请求头
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
// 发送请求
|
// 发送请求,超时时间设置为60秒
|
||||||
client := &http.Client{}
|
client := &http.Client{
|
||||||
|
Timeout: 60 * time.Second,
|
||||||
|
}
|
||||||
httpResp, clientDoErr := client.Do(req)
|
httpResp, clientDoErr := client.Do(req)
|
||||||
if clientDoErr != nil {
|
if clientDoErr != nil {
|
||||||
err = errors.Join(ErrSystem, clientDoErr)
|
// 检查是否是超时错误
|
||||||
|
isTimeout := false
|
||||||
|
if ctx.Err() == context.DeadlineExceeded {
|
||||||
|
isTimeout = true
|
||||||
|
} else if netErr, ok := clientDoErr.(interface{ Timeout() bool }); ok && netErr.Timeout() {
|
||||||
|
isTimeout = true
|
||||||
|
} else if errStr := clientDoErr.Error();
|
||||||
|
errStr == "context deadline exceeded" ||
|
||||||
|
errStr == "timeout" ||
|
||||||
|
errStr == "Client.Timeout exceeded" ||
|
||||||
|
errStr == "net/http: request canceled" {
|
||||||
|
isTimeout = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if isTimeout {
|
||||||
|
err = errors.Join(ErrDatasource, fmt.Errorf("API请求超时: %v", clientDoErr))
|
||||||
|
} else {
|
||||||
|
err = errors.Join(ErrSystem, clientDoErr)
|
||||||
|
}
|
||||||
if w.logger != nil {
|
if w.logger != nil {
|
||||||
w.logger.LogError(requestID, transactionID, code, err, reqData)
|
w.logger.LogError(requestID, transactionID, code, err, reqData)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,15 +158,33 @@ func (x *XingweiService) CallAPI(ctx context.Context, projectID string, params m
|
|||||||
req.Header.Set("API-ID", x.config.ApiID)
|
req.Header.Set("API-ID", x.config.ApiID)
|
||||||
req.Header.Set("project_id", projectID)
|
req.Header.Set("project_id", projectID)
|
||||||
|
|
||||||
// 创建HTTP客户端
|
// 创建HTTP客户端,超时时间设置为60秒
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
Timeout: 20 * time.Second,
|
Timeout: 60 * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送请求
|
// 发送请求
|
||||||
httpResp, clientDoErr := client.Do(req)
|
httpResp, clientDoErr := client.Do(req)
|
||||||
if clientDoErr != nil {
|
if clientDoErr != nil {
|
||||||
err = errors.Join(ErrSystem, clientDoErr)
|
// 检查是否是超时错误
|
||||||
|
isTimeout := false
|
||||||
|
if ctx.Err() == context.DeadlineExceeded {
|
||||||
|
isTimeout = true
|
||||||
|
} else if netErr, ok := clientDoErr.(interface{ Timeout() bool }); ok && netErr.Timeout() {
|
||||||
|
isTimeout = true
|
||||||
|
} else if errStr := clientDoErr.Error();
|
||||||
|
errStr == "context deadline exceeded" ||
|
||||||
|
errStr == "timeout" ||
|
||||||
|
errStr == "Client.Timeout exceeded" ||
|
||||||
|
errStr == "net/http: request canceled" {
|
||||||
|
isTimeout = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if isTimeout {
|
||||||
|
err = errors.Join(ErrDatasource, fmt.Errorf("API请求超时: %v", clientDoErr))
|
||||||
|
} else {
|
||||||
|
err = errors.Join(ErrSystem, clientDoErr)
|
||||||
|
}
|
||||||
if x.logger != nil {
|
if x.logger != nil {
|
||||||
x.logger.LogError(requestID, transactionID, "xingwei_api", err, params)
|
x.logger.LogError(requestID, transactionID, "xingwei_api", err, params)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,9 +107,9 @@ func (y *YushanService) CallAPI(ctx context.Context, code string, params map[str
|
|||||||
// 将加密后的数据编码为 Base64 字符串
|
// 将加密后的数据编码为 Base64 字符串
|
||||||
content := base64.StdEncoding.EncodeToString(cipherText)
|
content := base64.StdEncoding.EncodeToString(cipherText)
|
||||||
|
|
||||||
// 发起 HTTP 请求
|
// 发起 HTTP 请求,超时时间设置为60秒
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
Timeout: 20 * time.Second,
|
Timeout: 60 * time.Second,
|
||||||
}
|
}
|
||||||
req, err := http.NewRequestWithContext(ctx, "POST", y.config.URL, strings.NewReader(content))
|
req, err := http.NewRequestWithContext(ctx, "POST", y.config.URL, strings.NewReader(content))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -125,7 +125,25 @@ func (y *YushanService) CallAPI(ctx context.Context, code string, params map[str
|
|||||||
// 执行请求
|
// 执行请求
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = errors.Join(ErrSystem, err)
|
// 检查是否是超时错误
|
||||||
|
isTimeout := false
|
||||||
|
if ctx.Err() == context.DeadlineExceeded {
|
||||||
|
isTimeout = true
|
||||||
|
} else if netErr, ok := err.(interface{ Timeout() bool }); ok && netErr.Timeout() {
|
||||||
|
isTimeout = true
|
||||||
|
} else if errStr := err.Error();
|
||||||
|
errStr == "context deadline exceeded" ||
|
||||||
|
errStr == "timeout" ||
|
||||||
|
errStr == "Client.Timeout exceeded" ||
|
||||||
|
errStr == "net/http: request canceled" {
|
||||||
|
isTimeout = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if isTimeout {
|
||||||
|
err = errors.Join(ErrDatasource, fmt.Errorf("API请求超时: %v", err))
|
||||||
|
} else {
|
||||||
|
err = errors.Join(ErrSystem, err)
|
||||||
|
}
|
||||||
if y.logger != nil {
|
if y.logger != nil {
|
||||||
y.logger.LogError(requestID, transactionID, code, err, params)
|
y.logger.LogError(requestID, transactionID, code, err, params)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,15 +118,35 @@ func (z *ZhichaService) CallAPI(ctx context.Context, proID string, params map[st
|
|||||||
req.Header.Set("timestamp", strconv.FormatInt(timestamp, 10))
|
req.Header.Set("timestamp", strconv.FormatInt(timestamp, 10))
|
||||||
req.Header.Set("sign", z.generateSign(timestamp))
|
req.Header.Set("sign", z.generateSign(timestamp))
|
||||||
|
|
||||||
// 创建HTTP客户端
|
// 创建HTTP客户端,超时时间设置为60秒
|
||||||
client := &http.Client{
|
client := &http.Client{
|
||||||
Timeout: 20 * time.Second,
|
Timeout: 60 * time.Second,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送请求
|
// 发送请求
|
||||||
response, err := client.Do(req)
|
response, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = errors.Join(ErrSystem, err)
|
// 检查是否是超时错误
|
||||||
|
isTimeout := false
|
||||||
|
if ctx.Err() == context.DeadlineExceeded {
|
||||||
|
isTimeout = true
|
||||||
|
} else if netErr, ok := err.(interface{ Timeout() bool }); ok && netErr.Timeout() {
|
||||||
|
// 检查是否是网络超时错误
|
||||||
|
isTimeout = true
|
||||||
|
} else if errStr := err.Error();
|
||||||
|
errStr == "context deadline exceeded" ||
|
||||||
|
errStr == "timeout" ||
|
||||||
|
errStr == "Client.Timeout exceeded" ||
|
||||||
|
errStr == "net/http: request canceled" {
|
||||||
|
isTimeout = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if isTimeout {
|
||||||
|
// 超时错误应该返回数据源异常,而不是系统异常
|
||||||
|
err = errors.Join(ErrDatasource, fmt.Errorf("API请求超时: %v", err))
|
||||||
|
} else {
|
||||||
|
err = errors.Join(ErrSystem, err)
|
||||||
|
}
|
||||||
if z.logger != nil {
|
if z.logger != nil {
|
||||||
z.logger.LogError(requestID, transactionID, proID, err, params)
|
z.logger.LogError(requestID, transactionID, proID, err, params)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
package validator
|
package validator
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -211,6 +215,8 @@ func validateUUID(fl validator.FieldLevel) bool {
|
|||||||
// validateURL URL验证
|
// validateURL URL验证
|
||||||
func validateURL(fl validator.FieldLevel) bool {
|
func validateURL(fl validator.FieldLevel) bool {
|
||||||
urlStr := fl.Field().String()
|
urlStr := fl.Field().String()
|
||||||
|
// 去除首尾空白字符
|
||||||
|
urlStr = strings.TrimSpace(urlStr)
|
||||||
_, err := url.ParseRequestURI(urlStr)
|
_, err := url.ParseRequestURI(urlStr)
|
||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
@@ -462,19 +468,31 @@ func validateLuhn(cardNumber string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// validateAuthorizationURL 授权书URL验证器
|
// validateAuthorizationURL 授权书URL验证器
|
||||||
|
// 验证URL格式、可访问性和文件类型
|
||||||
|
// 安全措施:
|
||||||
|
// 1. 仅允许http/https协议
|
||||||
|
// 2. 设置超时时间防止阻塞
|
||||||
|
// 3. 限制重定向次数
|
||||||
|
// 4. 检查Content-Type和文件签名
|
||||||
func validateAuthorizationURL(fl validator.FieldLevel) bool {
|
func validateAuthorizationURL(fl validator.FieldLevel) bool {
|
||||||
urlStr := fl.Field().String()
|
urlStr := fl.Field().String()
|
||||||
if urlStr == "" {
|
if urlStr == "" {
|
||||||
return true // 空值由required标签处理
|
return true // 空值由required标签处理
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 去除首尾空白字符
|
||||||
|
urlStr = strings.TrimSpace(urlStr)
|
||||||
|
if urlStr == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// 解析URL
|
// 解析URL
|
||||||
parsedURL, err := url.Parse(urlStr)
|
parsedURL, err := url.Parse(urlStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查协议
|
// 检查协议(仅允许http和https)
|
||||||
if parsedURL.Scheme != "http" && parsedURL.Scheme != "https" {
|
if parsedURL.Scheme != "http" && parsedURL.Scheme != "https" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -489,8 +507,165 @@ func validateAuthorizationURL(fl validator.FieldLevel) bool {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if !hasValidExtension {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
return hasValidExtension
|
// 验证URL可访问性和文件类型
|
||||||
|
return validateURLAccessibility(parsedURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateURLAccessibility 验证URL可访问性和文件类型
|
||||||
|
func validateURLAccessibility(parsedURL *url.URL) bool {
|
||||||
|
// 创建带超时的context(5秒超时,避免阻塞验证流程)
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
// 创建HTTP客户端,设置安全参数
|
||||||
|
client := &http.Client{
|
||||||
|
Timeout: 5 * time.Second,
|
||||||
|
// 限制重定向次数,防止重定向攻击
|
||||||
|
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||||
|
// 最多允许3次重定向
|
||||||
|
if len(via) >= 3 {
|
||||||
|
return fmt.Errorf("重定向次数过多")
|
||||||
|
}
|
||||||
|
// 检查重定向后的URL是否仍然是http/https
|
||||||
|
if req.URL.Scheme != "http" && req.URL.Scheme != "https" {
|
||||||
|
return fmt.Errorf("不允许重定向到非HTTP协议")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先尝试HEAD请求(更高效,不下载文件内容)
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "HEAD", parsedURL.String(), nil)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置User-Agent,避免某些服务器拒绝请求
|
||||||
|
req.Header.Set("User-Agent", "TYAPI-Validator/1.0")
|
||||||
|
|
||||||
|
// 发送HEAD请求
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
// HEAD请求失败,尝试GET请求(某些服务器不支持HEAD)
|
||||||
|
return validateWithGETRequest(ctx, client, parsedURL)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// 检查HTTP状态码
|
||||||
|
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证Content-Type
|
||||||
|
if !isValidContentType(resp.Header.Get("Content-Type")) {
|
||||||
|
// Content-Type无效,尝试读取文件签名验证
|
||||||
|
return validateWithGETRequest(ctx, client, parsedURL)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateWithGETRequest 使用GET请求验证文件(仅在HEAD失败时使用)
|
||||||
|
func validateWithGETRequest(ctx context.Context, client *http.Client, parsedURL *url.URL) bool {
|
||||||
|
req, err := http.NewRequestWithContext(ctx, "GET", parsedURL.String(), nil)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Set("User-Agent", "TYAPI-Validator/1.0")
|
||||||
|
// 只读取部分内容,不下载整个文件
|
||||||
|
req.Header.Set("Range", "bytes=0-1023") // 只读取前1024字节
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// 检查HTTP状态码(206是部分内容,200是完整内容)
|
||||||
|
if resp.StatusCode != 200 && resp.StatusCode != 206 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证Content-Type
|
||||||
|
contentType := resp.Header.Get("Content-Type")
|
||||||
|
if isValidContentType(contentType) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取文件签名(magic bytes)验证文件类型
|
||||||
|
return validateFileSignature(resp.Body)
|
||||||
|
}
|
||||||
|
|
||||||
|
// isValidContentType 检查Content-Type是否有效
|
||||||
|
func isValidContentType(contentType string) bool {
|
||||||
|
contentType = strings.ToLower(strings.TrimSpace(contentType))
|
||||||
|
if contentType == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除charset等参数,只检查主类型
|
||||||
|
if idx := strings.Index(contentType, ";"); idx != -1 {
|
||||||
|
contentType = contentType[:idx]
|
||||||
|
}
|
||||||
|
contentType = strings.TrimSpace(contentType)
|
||||||
|
|
||||||
|
// 允许的Content-Type列表
|
||||||
|
validContentTypes := []string{
|
||||||
|
"application/pdf", // PDF
|
||||||
|
"image/jpeg", // JPEG
|
||||||
|
"image/jpg", // JPG
|
||||||
|
"image/png", // PNG
|
||||||
|
"image/bmp", // BMP
|
||||||
|
"image/x-ms-bmp", // BMP (另一种MIME类型)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, validType := range validContentTypes {
|
||||||
|
if contentType == validType {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// validateFileSignature 通过文件签名(magic bytes)验证文件类型
|
||||||
|
func validateFileSignature(body io.Reader) bool {
|
||||||
|
// 读取文件前16字节(足够识别所有支持的文件类型)
|
||||||
|
buffer := make([]byte, 16)
|
||||||
|
n, err := body.Read(buffer)
|
||||||
|
if err != nil && err != io.EOF {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if n < 4 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// PDF签名: %PDF (前4字节)
|
||||||
|
if n >= 4 && bytes.Equal(buffer[0:4], []byte("%PDF")) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// JPEG签名: FF D8 FF (前3字节)
|
||||||
|
if n >= 3 && buffer[0] == 0xFF && buffer[1] == 0xD8 && buffer[2] == 0xFF {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// PNG签名: 89 50 4E 47 0D 0A 1A 0A (前8字节)
|
||||||
|
if n >= 8 && bytes.Equal(buffer[0:8], []byte{0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// BMP签名: BM (前2字节: 42 4D)
|
||||||
|
if n >= 2 && bytes.Equal(buffer[0:2], []byte("BM")) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// validateUniqueID 唯一标识验证器(小于等于32位字符串)
|
// validateUniqueID 唯一标识验证器(小于等于32位字符串)
|
||||||
@@ -517,6 +692,9 @@ func validateReturnURL(fl validator.FieldLevel) bool {
|
|||||||
return true // 空值由required标签处理
|
return true // 空值由required标签处理
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 去除首尾空白字符
|
||||||
|
returnURL = strings.TrimSpace(returnURL)
|
||||||
|
|
||||||
// 检查长度:不能超过500字符
|
// 检查长度:不能超过500字符
|
||||||
if len(returnURL) > 500 {
|
if len(returnURL) > 500 {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -81,8 +81,8 @@ func NewRequestValidator(response interfaces.ResponseBuilder) interfaces.Request
|
|||||||
|
|
||||||
return &RequestValidator{
|
return &RequestValidator{
|
||||||
response: response,
|
response: response,
|
||||||
translator: globalTranslator, // 使用全局翻译器
|
translator: globalTranslator, // 使用全局翻译器
|
||||||
validator: globalValidator, // 使用全局校验器
|
validator: globalValidator, // 使用全局校验器
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user