Merge branch 'main' of http://1.117.67.95:3000/team/tyapi-server
This commit is contained in:
@@ -399,6 +399,7 @@ WechatH5:
|
||||
# ===========================================
|
||||
# 🔍 天眼查配置
|
||||
# ===========================================
|
||||
|
||||
tianyancha:
|
||||
base_url: http://open.api.tianyancha.com/services
|
||||
api_key: e6a43dc9-786e-4a16-bb12-392b8201d8e2
|
||||
|
||||
@@ -226,13 +226,19 @@ func (s *ApiApplicationServiceImpl) validateApiCall(ctx context.Context, cmd *co
|
||||
|
||||
// 4. 验证IP白名单(非开发环境)
|
||||
if !s.config.App.IsDevelopment() && !cmd.Options.IsDebug {
|
||||
whiteListIPs := make([]string, 0, len(apiUser.WhiteList))
|
||||
for _, item := range apiUser.WhiteList {
|
||||
whiteListIPs = append(whiteListIPs, item.IPAddress)
|
||||
}
|
||||
|
||||
// 添加调试日志
|
||||
s.logger.Info("开始验证白名单",
|
||||
zap.String("userId", apiUser.UserId),
|
||||
zap.String("clientIP", cmd.ClientIP),
|
||||
zap.Bool("isDevelopment", s.config.App.IsDevelopment()),
|
||||
zap.Bool("isDebug", cmd.Options.IsDebug),
|
||||
zap.Int("whiteListCount", len(apiUser.WhiteList)))
|
||||
zap.Int("whiteListCount", len(apiUser.WhiteList)),
|
||||
zap.Strings("whiteListIPs", whiteListIPs))
|
||||
|
||||
// 输出白名单详细信息(用于调试)
|
||||
for idx, item := range apiUser.WhiteList {
|
||||
@@ -246,10 +252,13 @@ func (s *ApiApplicationServiceImpl) validateApiCall(ctx context.Context, cmd *co
|
||||
s.logger.Error("IP不在白名单内",
|
||||
zap.String("userId", apiUser.UserId),
|
||||
zap.String("ip", cmd.ClientIP),
|
||||
zap.Int("whiteListSize", len(apiUser.WhiteList)))
|
||||
zap.Int("whiteListSize", len(apiUser.WhiteList)),
|
||||
zap.Strings("whiteListIPs", whiteListIPs))
|
||||
return nil, ErrInvalidIP
|
||||
}
|
||||
s.logger.Info("白名单验证通过", zap.String("ip", cmd.ClientIP))
|
||||
s.logger.Info("白名单验证通过",
|
||||
zap.String("ip", cmd.ClientIP),
|
||||
zap.Strings("whiteListIPs", whiteListIPs))
|
||||
}
|
||||
|
||||
// 5. 验证钱包状态
|
||||
|
||||
@@ -665,6 +665,11 @@ type QYGLDJ12Req struct {
|
||||
EntCode string `json:"ent_code" validate:"omitempty,validUSCI"`
|
||||
EntRegNo string `json:"ent_reg_no" validate:"omitempty"`
|
||||
}
|
||||
type QYGLDJ33Req struct {
|
||||
EntName string `json:"ent_name" validate:"omitempty,min=1,validEnterpriseName"`
|
||||
EntCode string `json:"ent_code" validate:"omitempty,validUSCI"`
|
||||
EntRegNo string `json:"ent_reg_no" validate:"omitempty"`
|
||||
}
|
||||
type YYSY6D9AReq struct {
|
||||
MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"`
|
||||
IDCard string `json:"id_card" validate:"required,validIDCard"`
|
||||
@@ -1048,6 +1053,13 @@ type IVYZA1B3Req struct {
|
||||
PhotoData string `json:"photo_data" validate:"required,validBase64Image"`
|
||||
}
|
||||
|
||||
type IVYZFIC1Req struct {
|
||||
IDCard string `json:"id_card" validate:"required,validIDCard"`
|
||||
Name string `json:"name" validate:"required,min=1,validName"`
|
||||
PhotoData string `json:"photo_data" validate:"omitempty,validBase64Image"`
|
||||
ImageUrl string `json:"+" validate:"omitempty,url"`
|
||||
}
|
||||
|
||||
type IVYZC4R9Req struct {
|
||||
IDCard string `json:"id_card" validate:"required,validIDCard"`
|
||||
Name string `json:"name" validate:"required,min=1,validName"`
|
||||
|
||||
@@ -248,6 +248,7 @@ func registerAllProcessors(combService *comb.CombService) {
|
||||
"QYGLUY3S": qygl.ProcessQYGLUY3SRequest, //企业经营状态全景查询
|
||||
"QYGLDJ12": qygl.ProcessQYGLDJ12Request, //企业年报信息核验
|
||||
"QYGL8848": qygl.ProcessQYGL8848Request, //企业税收违法核查
|
||||
"QYGLDJ33": qygl.ProcessQYGLDJ33Request, //企业年报信息核验
|
||||
|
||||
// YYSY系列处理器
|
||||
"YYSY35TA": yysy.ProcessYYSY35TARequest, //运营商归属地数卖
|
||||
@@ -318,6 +319,7 @@ func registerAllProcessors(combService *comb.CombService) {
|
||||
"IVYZ1J7H": ivyz.ProcessIVYZ1J7HRequest, //行驶证核查v2
|
||||
"IVYZ9K7F": ivyz.ProcessIVYZ9K7FRequest, //身份证实名认证即时版
|
||||
"IVYZA1B3": ivyz.ProcessIVYZA1B3Request, //公安三要素人脸识别
|
||||
"IVYZFIC1": ivyz.ProcessIVYZFIC1Request, //人脸身份证比对(数脉)
|
||||
"IVYZN2P8": ivyz.ProcessIVYZN2P8Request, //身份证实名认证政务版
|
||||
"IVYZX5QZ": ivyz.ProcessIVYZX5QZRequest, //活体检测
|
||||
"IVYZX5Q2": ivyz.ProcessIVYZX5Q2Request, //活体识别步骤二
|
||||
|
||||
@@ -241,6 +241,7 @@ func (s *FormConfigServiceImpl) getDTOStruct(ctx context.Context, apiCode string
|
||||
"YYSYK8R3": &dto.YYSYK8R3Req{}, //手机空号检测查询
|
||||
"YYSYF2T7": &dto.YYSYF2T7Req{}, //手机二次放号检测查询
|
||||
"IVYZA1B3": &dto.IVYZA1B3Req{}, //公安三要素人脸识别
|
||||
"IVYZFIC1": &dto.IVYZFIC1Req{}, //人脸身份证比对(数脉)
|
||||
"IVYZX5QZ": &dto.IVYZX5QZReq{}, //活体识别
|
||||
"IVYZN2P8": &dto.IVYZ9K7FReq{}, //身份证实名认证政务版
|
||||
"YYSYH6F3": &dto.YYSYH6F3Req{}, //运营商三要素简版即时版查询
|
||||
@@ -274,6 +275,7 @@ func (s *FormConfigServiceImpl) getDTOStruct(ctx context.Context, apiCode string
|
||||
"IVYZ48SR": &dto.IVYZ48SRReq{}, //婚姻状态核验V2(双人)
|
||||
"IVYZ5E22": &dto.IVYZ5E22Req{}, //双人婚姻评估查询zhicha版本
|
||||
"DWBG5SAM": &dto.DWBG5SAMReq{}, //天远指迷报告
|
||||
"QYGLDJ33": &dto.QYGLDJ33Req{}, //企业年报信息核验
|
||||
}
|
||||
|
||||
// 优先返回已配置的DTO
|
||||
@@ -635,7 +637,7 @@ func (s *FormConfigServiceImpl) generatePlaceholder(jsonTag string, fieldType st
|
||||
"notice_model": "请输入车辆型号",
|
||||
"vlphoto_data": "请输入行驶证图片",
|
||||
"carplate_type": "请选择车辆号牌类型(01-大型汽车 02-小型汽车 03-使馆汽车 04-领馆汽车 05-境外汽车 06-外籍汽车 07-普通摩托车 08-轻便摩托车 09-使馆摩托车 10-领馆摩托车 11-境外摩托车 12-外籍摩托车 13-低速车 14-拖拉机 15-挂车 16-教练汽车 17-教练摩托车 20-临时入境汽车 21-临时入境摩托车 22-临时行驶车 23-警用汽车 24-警用摩托 51-新能源大型车 52-新能源小型车)",
|
||||
"image_url": "请输入行驶证图片地址",
|
||||
"image_url": "请输入入参图片地址",
|
||||
"reg_url": "请输入车辆登记证图片地址",
|
||||
"token": "请输入token",
|
||||
"vehicle_name": "请输入车型名称",
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package ivyz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"tyapi-server/internal/domains/api/dto"
|
||||
"tyapi-server/internal/domains/api/services/processors"
|
||||
"tyapi-server/internal/infrastructure/external/muzi"
|
||||
)
|
||||
|
||||
// ProcessIVYZ3P9MRequest IVYZ3P9M API处理方法 - 学历查询实时版
|
||||
func ProcessIVYZ3P9MRequest_2(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||
var paramsDto dto.IVYZ3P9MReq
|
||||
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)
|
||||
}
|
||||
|
||||
encryptedName, err := deps.MuziService.Encrypt(paramsDto.Name)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
encryptedCertCode, err := deps.MuziService.Encrypt(paramsDto.IDCard)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
// 处理 returnType 参数,默认为 "1"
|
||||
returnType := paramsDto.ReturnType
|
||||
if returnType == "" {
|
||||
returnType = "1"
|
||||
}
|
||||
paramSign := map[string]interface{}{
|
||||
"returnType": returnType,
|
||||
"realName": encryptedName,
|
||||
"certCode": encryptedCertCode,
|
||||
}
|
||||
|
||||
reqData := map[string]interface{}{
|
||||
"realName": encryptedName,
|
||||
"certCode": encryptedCertCode,
|
||||
"returnType": returnType,
|
||||
}
|
||||
|
||||
respData, err := deps.MuziService.CallAPI(ctx, "PC0041", "/academic", reqData, paramSign)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, muzi.ErrDatasource):
|
||||
return nil, errors.Join(processors.ErrDatasource, err)
|
||||
case errors.Is(err, muzi.ErrSystem):
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
default:
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
}
|
||||
|
||||
return respData, nil
|
||||
}
|
||||
@@ -4,10 +4,11 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"tyapi-server/internal/domains/api/dto"
|
||||
"tyapi-server/internal/domains/api/services/processors"
|
||||
"tyapi-server/internal/infrastructure/external/muzi"
|
||||
"tyapi-server/internal/infrastructure/external/zhicha"
|
||||
)
|
||||
|
||||
// ProcessIVYZ3P9MRequest IVYZ3P9M API处理方法 - 学历查询实时版
|
||||
@@ -21,45 +22,147 @@ func ProcessIVYZ3P9MRequest(ctx context.Context, params []byte, deps *processors
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
|
||||
encryptedName, err := deps.MuziService.Encrypt(paramsDto.Name)
|
||||
encryptedName, err := deps.ZhichaService.Encrypt(paramsDto.Name)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
encryptedCertCode, err := deps.MuziService.Encrypt(paramsDto.IDCard)
|
||||
encryptedIDCard, err := deps.ZhichaService.Encrypt(paramsDto.IDCard)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
// 处理 returnType 参数,默认为 "1"
|
||||
returnType := paramsDto.ReturnType
|
||||
if returnType == "" {
|
||||
returnType = "1"
|
||||
}
|
||||
paramSign := map[string]interface{}{
|
||||
"returnType": returnType,
|
||||
"realName": encryptedName,
|
||||
"certCode": encryptedCertCode,
|
||||
}
|
||||
|
||||
reqData := map[string]interface{}{
|
||||
"realName": encryptedName,
|
||||
"certCode": encryptedCertCode,
|
||||
"returnType": returnType,
|
||||
"name": encryptedName,
|
||||
"idCard": encryptedIDCard,
|
||||
"authorized": "1",
|
||||
}
|
||||
|
||||
|
||||
respData, err := deps.MuziService.CallAPI(ctx, "PC0041", "/academic",reqData,paramSign)
|
||||
respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI1004", reqData)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, muzi.ErrDatasource):
|
||||
if errors.Is(err, zhicha.ErrDatasource) {
|
||||
return nil, errors.Join(processors.ErrDatasource, err)
|
||||
case errors.Is(err, muzi.ErrSystem):
|
||||
}
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
out, err := mapZCI1004ToIVYZ3P9M(respData, paramsDto.Name, paramsDto.IDCard)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
return json.Marshal(out)
|
||||
}
|
||||
|
||||
type zci1004Item struct {
|
||||
EndDate string `json:"endDate"`
|
||||
EducationLevel string `json:"educationLevel"`
|
||||
LearningForm string `json:"learningForm"`
|
||||
}
|
||||
|
||||
type ivyz3p9mItem struct {
|
||||
GraduationDate string `json:"graduationDate"`
|
||||
StudentName string `json:"studentName"`
|
||||
EducationLevel string `json:"educationLevel"`
|
||||
LearningForm string `json:"learningForm"`
|
||||
IDNumber string `json:"idNumber"`
|
||||
}
|
||||
|
||||
func mapZCI1004ToIVYZ3P9M(respData interface{}, name, idCard string) ([]ivyz3p9mItem, error) {
|
||||
respBytes, err := json.Marshal(respData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var source []zci1004Item
|
||||
if err := json.Unmarshal(respBytes, &source); err != nil {
|
||||
var wrapped struct {
|
||||
Data []zci1004Item `json:"data"`
|
||||
}
|
||||
if err2 := json.Unmarshal(respBytes, &wrapped); err2 != nil {
|
||||
return nil, err
|
||||
}
|
||||
source = wrapped.Data
|
||||
}
|
||||
|
||||
out := make([]ivyz3p9mItem, 0, len(source))
|
||||
for _, it := range source {
|
||||
out = append(out, ivyz3p9mItem{
|
||||
GraduationDate: normalizeDateDigits(it.EndDate),
|
||||
StudentName: name,
|
||||
EducationLevel: mapEducationLevelToCode(it.EducationLevel),
|
||||
LearningForm: mapLearningFormToCode(it.LearningForm),
|
||||
IDNumber: idCard,
|
||||
})
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func mapEducationLevelToCode(level string) string {
|
||||
v := normalizeText(level)
|
||||
switch {
|
||||
case strings.Contains(v, "第二学士"):
|
||||
return "5"
|
||||
case strings.Contains(v, "博士"):
|
||||
return "4"
|
||||
case strings.Contains(v, "硕士"):
|
||||
return "3"
|
||||
case strings.Contains(v, "本科"):
|
||||
return "2"
|
||||
case strings.Contains(v, "专科"), strings.Contains(v, "大专"):
|
||||
return "1"
|
||||
default:
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
return "99"
|
||||
}
|
||||
}
|
||||
|
||||
return respData, nil
|
||||
func mapLearningFormToCode(form string) string {
|
||||
v := normalizeText(form)
|
||||
switch {
|
||||
case strings.Contains(v, "脱产"):
|
||||
return "1"
|
||||
case strings.Contains(v, "普通全日制"):
|
||||
return "2"
|
||||
case strings.Contains(v, "全日制"):
|
||||
return "3"
|
||||
case strings.Contains(v, "开放教育"), strings.Contains(v, "开放大学"):
|
||||
return "4"
|
||||
case strings.Contains(v, "夜大学"), strings.Contains(v, "夜大"):
|
||||
return "5"
|
||||
case strings.Contains(v, "函授"):
|
||||
return "6"
|
||||
case strings.Contains(v, "网络教育"), strings.Contains(v, "网教"), strings.Contains(v, "远程教育"):
|
||||
return "7"
|
||||
case strings.Contains(v, "非全日制"):
|
||||
return "8"
|
||||
case strings.Contains(v, "业余"):
|
||||
return "9"
|
||||
case strings.Contains(v, "自学考试"), strings.Contains(v, "自考"):
|
||||
// 自考在既有枚举中无直对应,兼容并入“业余”
|
||||
return "9"
|
||||
default:
|
||||
return "99"
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeDateDigits(s string) string {
|
||||
trimmed := strings.TrimSpace(s)
|
||||
if trimmed == "" {
|
||||
return ""
|
||||
}
|
||||
var b strings.Builder
|
||||
for _, ch := range trimmed {
|
||||
if ch >= '0' && ch <= '9' {
|
||||
b.WriteRune(ch)
|
||||
}
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func normalizeText(s string) string {
|
||||
v := strings.TrimSpace(strings.ToLower(s))
|
||||
v = strings.ReplaceAll(v, " ", "")
|
||||
v = strings.ReplaceAll(v, "-", "")
|
||||
v = strings.ReplaceAll(v, "_", "")
|
||||
return v
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package ivyz
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"tyapi-server/internal/domains/api/dto"
|
||||
"tyapi-server/internal/domains/api/services/processors"
|
||||
"tyapi-server/internal/infrastructure/external/shumai"
|
||||
)
|
||||
|
||||
// ProcessIVYZFIC1Request IVYZFIC1 人脸身份证比对 API 处理方法(数脉)
|
||||
func ProcessIVYZFIC1Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||
var paramsDto dto.IVYZFIC1Req
|
||||
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)
|
||||
}
|
||||
|
||||
if strings.TrimSpace(paramsDto.PhotoData) == "" && strings.TrimSpace(paramsDto.ImageUrl) == "" {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, errors.New("image和url至少传一个"))
|
||||
}
|
||||
|
||||
reqFormData := map[string]interface{}{
|
||||
"idcard": paramsDto.IDCard,
|
||||
"name": paramsDto.Name,
|
||||
"image": paramsDto.PhotoData,
|
||||
"url": paramsDto.ImageUrl,
|
||||
}
|
||||
|
||||
apiPath := "/v4/face_id_card/compare"
|
||||
|
||||
// 先尝试政务接口,再回退实时接口
|
||||
respBytes, err := deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData, true)
|
||||
if err != nil {
|
||||
respBytes, err = deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData, false)
|
||||
if err != nil {
|
||||
if errors.Is(err, shumai.ErrNotFound) {
|
||||
return nil, errors.Join(processors.ErrNotFound, err)
|
||||
} else if errors.Is(err, shumai.ErrDatasource) {
|
||||
return nil, errors.Join(processors.ErrDatasource, err)
|
||||
} else if errors.Is(err, shumai.ErrSystem) {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
}
|
||||
|
||||
return respBytes, nil
|
||||
}
|
||||
@@ -4,10 +4,15 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"tyapi-server/internal/domains/api/dto"
|
||||
"tyapi-server/internal/domains/api/services/processors"
|
||||
"tyapi-server/internal/infrastructure/external/xingwei"
|
||||
"tyapi-server/internal/infrastructure/external/shumai"
|
||||
)
|
||||
|
||||
// ProcessIVYZZQT3Request IVYZZQT3 人脸比对V3API处理方法
|
||||
@@ -21,31 +26,187 @@ func ProcessIVYZZQT3Request(ctx context.Context, params []byte, deps *processors
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
|
||||
// 构建请求数据,使用xingwei服务的正确字段名
|
||||
reqData := map[string]interface{}{
|
||||
// 使用数脉接口进行人脸身份证比对
|
||||
reqFormData := map[string]interface{}{
|
||||
"idcard": paramsDto.IDCard,
|
||||
"name": paramsDto.Name,
|
||||
"idCardNum": paramsDto.IDCard,
|
||||
"image": paramsDto.PhotoData,
|
||||
}
|
||||
|
||||
// 调用行为数据API,使用指定的project_id
|
||||
projectID := "CDJ-1104321430396268544"
|
||||
respBytes, err := deps.XingweiService.CallAPI(ctx, projectID, reqData)
|
||||
apiPath := "/v4/face_id_card/compare"
|
||||
|
||||
// 先尝试政务接口,再回退实时接口
|
||||
respBytes, err := deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData, true)
|
||||
if err != nil {
|
||||
if errors.Is(err, xingwei.ErrNotFound) {
|
||||
// 查空情况,返回特定的查空错误
|
||||
respBytes, err = deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData, false)
|
||||
if err != nil {
|
||||
if errors.Is(err, shumai.ErrNotFound) {
|
||||
return nil, errors.Join(processors.ErrNotFound, err)
|
||||
} else if errors.Is(err, xingwei.ErrDatasource) {
|
||||
// 数据源错误
|
||||
} else if errors.Is(err, shumai.ErrDatasource) {
|
||||
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)
|
||||
} else {
|
||||
// 其他未知错误
|
||||
}
|
||||
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"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package qygl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"tyapi-server/internal/domains/api/dto"
|
||||
"tyapi-server/internal/domains/api/services/processors"
|
||||
"tyapi-server/internal/infrastructure/external/shujubao"
|
||||
)
|
||||
|
||||
// ProcessQYGLDJ33Request QYGLDJ33 企业进出口信用核查 API 处理方法(使用数据宝服务示例)
|
||||
func ProcessQYGLDJ33Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||
var paramsDto dto.QYGLDJ33Req
|
||||
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)
|
||||
}
|
||||
|
||||
// 企业名称(entName)、统一社会信用代码(creditCode)、企业注册号(entRegNo) 至少传其一;多填时优先用 creditCode 传参
|
||||
hasEntName := paramsDto.EntName != ""
|
||||
hasEntCode := paramsDto.EntCode != ""
|
||||
hasEntRegNo := paramsDto.EntRegNo != ""
|
||||
if !hasEntName && !hasEntCode && !hasEntRegNo { // 三个都未填才报错
|
||||
return nil, errors.Join(processors.ErrInvalidParam, errors.New("ent_name、ent_code、ent_reg_no 至少需要传其中一个"))
|
||||
}
|
||||
|
||||
// 构建数据宝入参(sign 外的业务参数可按需 AES 加密后作为 bodyData)
|
||||
reqParams := map[string]interface{}{
|
||||
"key": "f51ed30b0d4208bf7e6f2ba499d49d4f",
|
||||
}
|
||||
if hasEntCode {
|
||||
reqParams["creditCode"] = paramsDto.EntCode
|
||||
} else if hasEntName {
|
||||
reqParams["entName"] = paramsDto.EntName
|
||||
} else if hasEntRegNo {
|
||||
reqParams["regCode"] = paramsDto.EntRegNo
|
||||
}
|
||||
// 最终请求 URL = https://api.chinadatapay.com/communication + 拼接接口地址值,如 personal/197
|
||||
apiPath := "/communication/personal/10254"
|
||||
data, err := deps.ShujubaoService.CallAPI(ctx, apiPath, reqParams)
|
||||
if err != nil {
|
||||
if errors.Is(err, shujubao.ErrDatasource) {
|
||||
return nil, errors.Join(processors.ErrDatasource, err)
|
||||
}
|
||||
if errors.Is(err, shujubao.ErrQueryEmpty) {
|
||||
return nil, errors.Join(processors.ErrNotFound, err)
|
||||
}
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
// 解析响应中的 JSON 字符串(使用 qyglb4c0 中的 RecursiveParse)
|
||||
parsedResp, err := RecursiveParse(data)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
respBytes, err := json.Marshal(parsedResp)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
return respBytes, nil
|
||||
}
|
||||
@@ -2,46 +2,44 @@ package yysy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"tyapi-server/internal/domains/api/dto"
|
||||
"tyapi-server/internal/domains/api/services/processors"
|
||||
"tyapi-server/internal/infrastructure/external/xingwei"
|
||||
)
|
||||
|
||||
// ProcessYYSY8C2DRequest YYSY8C2D API处理方法 - 运营商三要素查询
|
||||
func ProcessYYSY8C2DRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||
var paramsDto dto.YYSY8C2DReq
|
||||
if err := json.Unmarshal(params, ¶msDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
return ProcessYYSY9A1BRequest(ctx, params, deps)
|
||||
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
// var paramsDto dto.YYSY8C2DReq
|
||||
// if err := json.Unmarshal(params, ¶msDto); err != nil {
|
||||
// return nil, errors.Join(processors.ErrSystem, err)
|
||||
// }
|
||||
|
||||
// 构建请求数据,将项目规范的字段名转换为 XingweiService 需要的字段名
|
||||
reqData := map[string]interface{}{
|
||||
"name": paramsDto.Name,
|
||||
"idCardNum": paramsDto.IDCard,
|
||||
"phoneNumber": paramsDto.MobileNo,
|
||||
}
|
||||
// if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
// return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
// }
|
||||
|
||||
// 调用行为数据API,使用指定的project_id
|
||||
projectID := "CDJ-1100244702166183936"
|
||||
respBytes, err := deps.XingweiService.CallAPI(ctx, projectID, reqData)
|
||||
if err != nil {
|
||||
if errors.Is(err, xingwei.ErrNotFound) {
|
||||
return nil, errors.Join(processors.ErrNotFound, err)
|
||||
} else if errors.Is(err, xingwei.ErrDatasource) {
|
||||
return nil, errors.Join(processors.ErrDatasource, err)
|
||||
} else if errors.Is(err, xingwei.ErrSystem) {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
} else {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
}
|
||||
// // 构建请求数据,将项目规范的字段名转换为 XingweiService 需要的字段名
|
||||
// reqData := map[string]interface{}{
|
||||
// "name": paramsDto.Name,
|
||||
// "idCardNum": paramsDto.IDCard,
|
||||
// "phoneNumber": paramsDto.MobileNo,
|
||||
// }
|
||||
|
||||
return respBytes, nil
|
||||
// // 调用行为数据API,使用指定的project_id
|
||||
// projectID := "CDJ-1100244702166183936"
|
||||
// respBytes, err := deps.XingweiService.CallAPI(ctx, projectID, reqData)
|
||||
// if err != nil {
|
||||
// if errors.Is(err, xingwei.ErrNotFound) {
|
||||
// return nil, errors.Join(processors.ErrNotFound, err)
|
||||
// } else if errors.Is(err, xingwei.ErrDatasource) {
|
||||
// return nil, errors.Join(processors.ErrDatasource, err)
|
||||
// } else if errors.Is(err, xingwei.ErrSystem) {
|
||||
// return nil, errors.Join(processors.ErrSystem, err)
|
||||
// } else {
|
||||
// return nil, errors.Join(processors.ErrSystem, err)
|
||||
// }
|
||||
// }
|
||||
|
||||
// return respBytes, nil
|
||||
}
|
||||
|
||||
@@ -213,11 +213,10 @@ func newWechatPayServiceWithWxPayPubKey(c config.Config, logger *zap.Logger) *We
|
||||
panic(fmt.Sprintf("初始化失败,服务停止: %v", err))
|
||||
}
|
||||
|
||||
// 初始化 notify.Handler
|
||||
certificateVisitor := downloader.MgrInstance().GetCertificateVisitor(mchID)
|
||||
// 初始化 notify.Handler(纯支付公钥验签)
|
||||
notifyHandler := notify.NewNotifyHandler(
|
||||
mchAPIv3Key,
|
||||
verifiers.NewSHA256WithRSACombinedVerifier(certificateVisitor, mchPublicKeyID, *mchPublicKey))
|
||||
verifiers.NewSHA256WithRSAPubkeyVerifier(mchPublicKeyID, *mchPublicKey))
|
||||
|
||||
logger.Info("微信支付客户端初始化成功(微信支付公钥方式)")
|
||||
return &WechatPayService{
|
||||
|
||||
Reference in New Issue
Block a user