ff
This commit is contained in:
@@ -1052,7 +1052,7 @@ type IVYZFIC1Req struct {
|
|||||||
IDCard string `json:"id_card" validate:"required,validIDCard"`
|
IDCard string `json:"id_card" validate:"required,validIDCard"`
|
||||||
Name string `json:"name" validate:"required,min=1,validName"`
|
Name string `json:"name" validate:"required,min=1,validName"`
|
||||||
PhotoData string `json:"photo_data" validate:"omitempty,validBase64Image"`
|
PhotoData string `json:"photo_data" validate:"omitempty,validBase64Image"`
|
||||||
ImageUrl string `json:"image_url" validate:"omitempty,url"`
|
ImageUrl string `json:"+" validate:"omitempty,url"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type IVYZC4R9Req struct {
|
type IVYZC4R9Req struct {
|
||||||
|
|||||||
@@ -4,10 +4,15 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"tyapi-server/internal/domains/api/dto"
|
"tyapi-server/internal/domains/api/dto"
|
||||||
"tyapi-server/internal/domains/api/services/processors"
|
"tyapi-server/internal/domains/api/services/processors"
|
||||||
"tyapi-server/internal/infrastructure/external/xingwei"
|
"tyapi-server/internal/infrastructure/external/shumai"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ProcessIVYZZQT3Request IVYZZQT3 人脸比对V3API处理方法
|
// ProcessIVYZZQT3Request IVYZZQT3 人脸比对V3API处理方法
|
||||||
@@ -21,31 +26,187 @@ func ProcessIVYZZQT3Request(ctx context.Context, params []byte, deps *processors
|
|||||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 构建请求数据,使用xingwei服务的正确字段名
|
// 使用数脉接口进行人脸身份证比对
|
||||||
reqData := map[string]interface{}{
|
reqFormData := map[string]interface{}{
|
||||||
"name": paramsDto.Name,
|
"idcard": paramsDto.IDCard,
|
||||||
"idCardNum": paramsDto.IDCard,
|
"name": paramsDto.Name,
|
||||||
"image": paramsDto.PhotoData,
|
"image": paramsDto.PhotoData,
|
||||||
}
|
}
|
||||||
|
|
||||||
// 调用行为数据API,使用指定的project_id
|
apiPath := "/v4/face_id_card/compare"
|
||||||
projectID := "CDJ-1104321430396268544"
|
|
||||||
respBytes, err := deps.XingweiService.CallAPI(ctx, projectID, reqData)
|
// 先尝试政务接口,再回退实时接口
|
||||||
|
respBytes, err := deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, xingwei.ErrNotFound) {
|
respBytes, err = deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData, false)
|
||||||
// 查空情况,返回特定的查空错误
|
if err != nil {
|
||||||
return nil, errors.Join(processors.ErrNotFound, err)
|
if errors.Is(err, shumai.ErrNotFound) {
|
||||||
} else if errors.Is(err, xingwei.ErrDatasource) {
|
return nil, errors.Join(processors.ErrNotFound, err)
|
||||||
// 数据源错误
|
} else if errors.Is(err, shumai.ErrDatasource) {
|
||||||
return nil, errors.Join(processors.ErrDatasource, err)
|
return nil, errors.Join(processors.ErrDatasource, err)
|
||||||
} else if errors.Is(err, xingwei.ErrSystem) {
|
} else if errors.Is(err, shumai.ErrSystem) {
|
||||||
// 系统错误
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
return nil, errors.Join(processors.ErrSystem, err)
|
}
|
||||||
} else {
|
|
||||||
// 其他未知错误
|
|
||||||
return nil, errors.Join(processors.ErrSystem, err)
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return respBytes, nil
|
outBytes, err := mapShumaiFaceCompareToIVYZZQT3(respBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Join(processors.ErrSystem, err)
|
||||||
|
}
|
||||||
|
return outBytes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type shumaiFaceCompareResp struct {
|
||||||
|
OrderNo string `json:"order_no"`
|
||||||
|
Score interface{} `json:"score"`
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
Incorrect interface{} `json:"incorrect"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ivyzzqt3Out struct {
|
||||||
|
HandleTime string `json:"handleTime"`
|
||||||
|
ResultData ivyzzqt3OutResultData `json:"resultData"`
|
||||||
|
OrderNo string `json:"orderNo"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ivyzzqt3OutResultData struct {
|
||||||
|
VerificationCode string `json:"verification_code"`
|
||||||
|
VerificationResult string `json:"verification_result"`
|
||||||
|
VerificationMessage string `json:"verification_message"`
|
||||||
|
Similarity string `json:"similarity"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapShumaiFaceCompareToIVYZZQT3(respBytes []byte) ([]byte, error) {
|
||||||
|
var r shumaiFaceCompareResp
|
||||||
|
if err := json.Unmarshal(respBytes, &r); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
score := parseScoreToFloat64(r.Score)
|
||||||
|
similarity := strconv.Itoa(int(math.Round(mapScoreToSimilarity(score))))
|
||||||
|
verificationResult := mapScoreToVerificationResult(score)
|
||||||
|
verificationMessage := strings.TrimSpace(r.Msg)
|
||||||
|
if verificationMessage == "" {
|
||||||
|
verificationMessage = mapScoreToVerificationMessage(score)
|
||||||
|
}
|
||||||
|
|
||||||
|
out := ivyzzqt3Out{
|
||||||
|
HandleTime: time.Now().Format("2006-01-02 15:04:05"),
|
||||||
|
OrderNo: strings.TrimSpace(r.OrderNo),
|
||||||
|
ResultData: ivyzzqt3OutResultData{
|
||||||
|
VerificationCode: mapVerificationCode(verificationResult, r.Incorrect),
|
||||||
|
VerificationResult: verificationResult,
|
||||||
|
VerificationMessage: verificationMessage,
|
||||||
|
Similarity: similarity,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return json.Marshal(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapScoreToVerificationResult(score float64) string {
|
||||||
|
if score >= 0.45 {
|
||||||
|
return "valid"
|
||||||
|
}
|
||||||
|
// 旧结构仅支持 valid/invalid,不能确定场景按 invalid 返回
|
||||||
|
return "invalid"
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapScoreToVerificationMessage(score float64) string {
|
||||||
|
if score < 0.40 {
|
||||||
|
return "系统判断为不同人"
|
||||||
|
}
|
||||||
|
if score < 0.45 {
|
||||||
|
return "不能确定是否为同一人"
|
||||||
|
}
|
||||||
|
return "系统判断为同一人"
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapScoreToSimilarity(score float64) float64 {
|
||||||
|
// 将 score(0~1) 分段映射到 similarity(0~1000),并对齐业务阈值:
|
||||||
|
// 0.40 -> 600,0.45 -> 700
|
||||||
|
if score <= 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if score >= 1 {
|
||||||
|
return 1000
|
||||||
|
}
|
||||||
|
if score < 0.40 {
|
||||||
|
// [0, 0.40) -> [0, 600)
|
||||||
|
return (score / 0.40) * 600
|
||||||
|
}
|
||||||
|
if score < 0.45 {
|
||||||
|
// [0.40, 0.45) -> [600, 700)
|
||||||
|
return 600 + ((score-0.40)/0.05)*100
|
||||||
|
}
|
||||||
|
// [0.45, 1] -> [700, 1000]
|
||||||
|
return 700 + ((score-0.45)/0.55)*300
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseScoreToFloat64(v interface{}) float64 {
|
||||||
|
switch t := v.(type) {
|
||||||
|
case float64:
|
||||||
|
return t
|
||||||
|
case float32:
|
||||||
|
return float64(t)
|
||||||
|
case int:
|
||||||
|
return float64(t)
|
||||||
|
case int32:
|
||||||
|
return float64(t)
|
||||||
|
case int64:
|
||||||
|
return float64(t)
|
||||||
|
case json.Number:
|
||||||
|
if f, err := t.Float64(); err == nil {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
case string:
|
||||||
|
s := strings.TrimSpace(t)
|
||||||
|
if s == "" {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if f, err := strconv.ParseFloat(s, 64); err == nil {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func valueToString(v interface{}) string {
|
||||||
|
switch t := v.(type) {
|
||||||
|
case string:
|
||||||
|
return strings.TrimSpace(t)
|
||||||
|
case json.Number:
|
||||||
|
return t.String()
|
||||||
|
case float64:
|
||||||
|
return strconv.FormatFloat(t, 'f', -1, 64)
|
||||||
|
case float32:
|
||||||
|
return strconv.FormatFloat(float64(t), 'f', -1, 64)
|
||||||
|
case int:
|
||||||
|
return strconv.Itoa(t)
|
||||||
|
case int32:
|
||||||
|
return strconv.FormatInt(int64(t), 10)
|
||||||
|
case int64:
|
||||||
|
return strconv.FormatInt(t, 10)
|
||||||
|
default:
|
||||||
|
if v == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strings.TrimSpace(fmt.Sprint(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func mapVerificationCode(verificationResult string, upstreamIncorrect interface{}) string {
|
||||||
|
if verificationResult == "valid" {
|
||||||
|
return "1000"
|
||||||
|
}
|
||||||
|
if verificationResult == "invalid" {
|
||||||
|
return "2006"
|
||||||
|
}
|
||||||
|
// 兜底:若后续扩展出其它结果,保持可追溯
|
||||||
|
if s := valueToString(upstreamIncorrect); s != "" {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return "2006"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user