f
This commit is contained in:
@@ -939,7 +939,7 @@ type YYSYK8R3Req struct {
|
||||
|
||||
type YYSYF2T7Req struct {
|
||||
MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"`
|
||||
DateRange string `json:"date_range" validate:"required,validAuthDate" `
|
||||
DateRange string `json:"date_range" validate:"required,validDateRange"`
|
||||
}
|
||||
|
||||
type QYGL5S1IReq struct {
|
||||
@@ -974,11 +974,6 @@ type IVYZP0T4Req struct {
|
||||
MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"`
|
||||
}
|
||||
|
||||
type IVYZF2T7Req struct {
|
||||
MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"`
|
||||
DateRange string `json:"date_range" validate:"required,validAuthDate" `
|
||||
}
|
||||
|
||||
type IVYZX5QZReq struct {
|
||||
ReturnURL string `json:"return_url" validate:"required,validReturnURL"`
|
||||
}
|
||||
|
||||
@@ -206,6 +206,7 @@ func registerAllProcessors(combService *comb.CombService) {
|
||||
"YYSYK8R3": yysy.ProcessYYSYK8R3Request, //手机空号检测查询
|
||||
"YYSYH6F3": yysy.ProcessYYSYH6F3Request, //运营商三要素即时版查询
|
||||
"YYSYK9R4": yysy.ProcessYYSYK9R4Request, //全网手机三要素验证1979周更新版
|
||||
"YYSYF2T7": yysy.ProcessYYSYF2T7Request, //手机二次放号检测查询
|
||||
|
||||
// IVYZ系列处理器
|
||||
"IVYZ0B03": ivyz.ProcessIVYZ0B03Request,
|
||||
|
||||
@@ -361,6 +361,8 @@ func (s *FormConfigServiceImpl) parseValidationRules(validateTag string) string
|
||||
frontendRules = append(frontendRules, "日期格式")
|
||||
case rule == "validAuthDate":
|
||||
frontendRules = append(frontendRules, "授权日期格式")
|
||||
case rule == "validDateRange":
|
||||
frontendRules = append(frontendRules, "日期范围格式(YYYYMMDD-YYYYMMDD)")
|
||||
case rule == "validTimeRange":
|
||||
frontendRules = append(frontendRules, "时间范围格式")
|
||||
case rule == "validMobileType":
|
||||
@@ -398,6 +400,8 @@ func (s *FormConfigServiceImpl) getFieldType(fieldType reflect.Type, validation
|
||||
return "text" // time_range是HH:MM-HH:MM格式,使用文本输入
|
||||
} else if strings.Contains(validation, "授权日期格式") {
|
||||
return "text" // auth_date是YYYYMMDD-YYYYMMDD格式,使用文本输入
|
||||
} else if strings.Contains(validation, "日期范围格式") {
|
||||
return "text" // date_range 为 YYYYMMDD-YYYYMMDD,使用文本输入便于直接输入
|
||||
} else if strings.Contains(validation, "日期") {
|
||||
return "date"
|
||||
} else if strings.Contains(validation, "链接") {
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"tyapi-server/internal/domains/api/dto"
|
||||
"tyapi-server/internal/domains/api/services/processors"
|
||||
"tyapi-server/internal/infrastructure/external/zhicha"
|
||||
"tyapi-server/internal/infrastructure/external/shumai"
|
||||
)
|
||||
|
||||
// ProcessIVYZP2Q6Request IVYZP2Q6 API处理方法 - 身份认证二要素
|
||||
@@ -21,37 +21,66 @@ func ProcessIVYZP2Q6Request(ctx context.Context, params []byte, deps *processors
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
|
||||
// 加密姓名
|
||||
encryptedName, err := deps.ZhichaService.Encrypt(paramsDto.Name)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
reqFormData := map[string]interface{}{
|
||||
"idcard": paramsDto.IDCard,
|
||||
"name": paramsDto.Name,
|
||||
}
|
||||
|
||||
// 加密身份证号
|
||||
encryptedIDCard, err := deps.ZhichaService.Encrypt(paramsDto.IDCard)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
// 以表单方式调用数脉 API;参数在 CallAPIForm 内转为 application/x-www-form-urlencoded
|
||||
apiPath := "/v4/id_card/check" // 接口路径,根据数脉文档填写(如 v4/xxx)
|
||||
|
||||
reqData := map[string]interface{}{
|
||||
"name": encryptedName,
|
||||
"idCard": encryptedIDCard,
|
||||
}
|
||||
|
||||
respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI011", reqData)
|
||||
// 先尝试使用政务接口(app_id2 和 app_secret2)
|
||||
respBytes, err := deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData, true)
|
||||
if err != nil {
|
||||
if errors.Is(err, zhicha.ErrDatasource) {
|
||||
return nil, errors.Join(processors.ErrDatasource, err)
|
||||
} else {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
// 使用实时接口(app_id 和 app_secret)重试
|
||||
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)
|
||||
} else {
|
||||
// 其他未知错误
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 将响应数据转换为JSON字节
|
||||
respBytes, err := json.Marshal(respData)
|
||||
if err != nil {
|
||||
// 数据源返回 result(0-一致/1-不一致/2-无记录),映射为 state(1-匹配/2-不匹配/3-异常情况)
|
||||
var dsResp struct {
|
||||
Result int `json:"result"` // 0-一致 1-不一致 2-无记录(预留)
|
||||
}
|
||||
if err := json.Unmarshal(respBytes, &dsResp); err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
return respBytes, nil
|
||||
state := resultToState(dsResp.Result)
|
||||
|
||||
out := map[string]interface{}{
|
||||
"data": map[string]interface{}{
|
||||
"errMsg": "",
|
||||
"state": state,
|
||||
},
|
||||
}
|
||||
return json.Marshal(out)
|
||||
}
|
||||
|
||||
// resultToState 将数据源 result 映射为接口 state:1-匹配 2-不匹配 3-异常情况
|
||||
func resultToState(result int) int {
|
||||
switch result {
|
||||
case 0: // 一致 → 匹配
|
||||
return 1
|
||||
case 1: // 不一致 → 不匹配
|
||||
return 2
|
||||
case 2: // 无记录(预留) → 异常情况
|
||||
return 3
|
||||
default:
|
||||
return 3 // 未知/异常 → 异常情况
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"tyapi-server/internal/domains/api/dto"
|
||||
"tyapi-server/internal/domains/api/services/processors"
|
||||
"tyapi-server/internal/infrastructure/external/zhicha"
|
||||
"tyapi-server/internal/infrastructure/external/shumai"
|
||||
)
|
||||
|
||||
// ProcessYYSY3E7FRequest YYSY3E7F API处理方法 - 空号检测
|
||||
@@ -20,30 +20,25 @@ func ProcessYYSY3E7FRequest(ctx context.Context, params []byte, deps *processors
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
|
||||
encryptedMobileNo, err := deps.ZhichaService.Encrypt(paramsDto.MobileNo)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
reqFormData := map[string]interface{}{
|
||||
"mobile": paramsDto.MobileNo,
|
||||
}
|
||||
|
||||
reqData := map[string]interface{}{
|
||||
"phone": encryptedMobileNo,
|
||||
}
|
||||
|
||||
respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI010", reqData)
|
||||
// 以表单方式调用数脉 API;参数在 CallAPIForm 内转为 application/x-www-form-urlencoded
|
||||
apiPath := "/v4/mobile_empty/check" // 接口路径,根据数脉文档填写(
|
||||
respBytes, err := deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData)
|
||||
if err != nil {
|
||||
if errors.Is(err, zhicha.ErrDatasource) {
|
||||
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)
|
||||
} else {
|
||||
// 其他未知错误
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
}
|
||||
|
||||
// 将响应数据转换为JSON字节
|
||||
respBytes, err := json.Marshal(respData)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
return respBytes, nil
|
||||
}
|
||||
|
||||
@@ -4,12 +4,21 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"tyapi-server/internal/domains/api/dto"
|
||||
"tyapi-server/internal/domains/api/services/processors"
|
||||
"tyapi-server/internal/infrastructure/external/zhicha"
|
||||
"tyapi-server/internal/infrastructure/external/shumai"
|
||||
)
|
||||
|
||||
// shumaiMobileTransferResp 数脉 /v4/mobile-transfer/query 返回结构
|
||||
type shumaiMobileTransferResp struct {
|
||||
OrderNo string `json:"order_no"`
|
||||
Channel string `json:"channel"` // 移动/电信/联通
|
||||
Status int `json:"status"` // 0-在网 1-不在网
|
||||
Desc string `json:"desc"` // 不在网原因(status=1时有效)
|
||||
}
|
||||
|
||||
// ProcessYYSY6D9ARequest YYSY6D9A API处理方法 - 全网手机号状态验证A
|
||||
func ProcessYYSY6D9ARequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||
var paramsDto dto.YYSY6D9AReq
|
||||
@@ -21,29 +30,72 @@ func ProcessYYSY6D9ARequest(ctx context.Context, params []byte, deps *processors
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
|
||||
encryptedMobileNo, err := deps.ZhichaService.Encrypt(paramsDto.MobileNo)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
reqFormData := map[string]interface{}{
|
||||
"mobile": paramsDto.MobileNo,
|
||||
}
|
||||
|
||||
reqData := map[string]interface{}{
|
||||
"phone": encryptedMobileNo,
|
||||
}
|
||||
|
||||
respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI030", reqData)
|
||||
// 以表单方式调用数脉 API;参数在 CallAPIForm 内转为 application/x-www-form-urlencoded
|
||||
apiPath := "/v4/mobile-transfer/query"
|
||||
respBytes, err := deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData)
|
||||
if err != nil {
|
||||
if errors.Is(err, zhicha.ErrDatasource) {
|
||||
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)
|
||||
} else {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
}
|
||||
|
||||
// 将响应数据转换为JSON字节
|
||||
respBytes, err := json.Marshal(respData)
|
||||
mapped, err := mapShumaiToYYSY6D9A(respBytes)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
return respBytes, nil
|
||||
return json.Marshal(mapped)
|
||||
}
|
||||
|
||||
// mapShumaiToYYSY6D9A 将数脉 mobile-transfer 响应映射为最终 state/operators 格式
|
||||
// state: 1-正常 2-不在网(空号) 3-无短信能力 4-欠费 5-长时间关机 6-关机 7-通话中 -1-查询失败
|
||||
// operators: 1-移动 2-联通 3-电信
|
||||
func mapShumaiToYYSY6D9A(dataBytes []byte) (map[string]string, error) {
|
||||
var r shumaiMobileTransferResp
|
||||
if err := json.Unmarshal(dataBytes, &r); err != nil {
|
||||
return map[string]string{"state": "-1", "operators": ""}, nil // 解析失败视为查询失败
|
||||
}
|
||||
|
||||
operators := ispNameToCode(strings.TrimSpace(r.Channel))
|
||||
state := statusDescToState(r.Status, r.Desc)
|
||||
|
||||
return map[string]string{
|
||||
"state": state,
|
||||
"operators": operators,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// statusDescToState status: 0-在网 1-不在网;desc 为不在网原因
|
||||
func statusDescToState(status int, desc string) string {
|
||||
if status == 0 {
|
||||
return "1" // 正常
|
||||
}
|
||||
// status == 1 不在网,根据 desc 推断 state
|
||||
d := strings.TrimSpace(desc)
|
||||
if strings.Contains(d, "销号") || strings.Contains(d, "空号") {
|
||||
return "2" // 不在网(空号)
|
||||
}
|
||||
if strings.Contains(d, "无短信") || strings.Contains(d, "在网不可用") {
|
||||
return "3" // 无短信能力
|
||||
}
|
||||
if strings.Contains(d, "欠费") {
|
||||
return "4" // 欠费
|
||||
}
|
||||
if strings.Contains(d, "长时间关机") {
|
||||
return "5" // 长时间关机
|
||||
}
|
||||
if strings.Contains(d, "关机") {
|
||||
return "6" // 关机
|
||||
}
|
||||
if strings.Contains(d, "通话中") {
|
||||
return "7" // 通话中
|
||||
}
|
||||
return "2" // 不在网但未明确原因,默认空号
|
||||
}
|
||||
|
||||
@@ -4,12 +4,35 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
// shumaiMobileTransferResp 数脉 /v4/mobile-transfer/query 携号转网返回结构
|
||||
type shumaiMobileTransfer7d3eResp struct {
|
||||
OrderNo string `json:"order_no"`
|
||||
Mobile string `json:"mobile"`
|
||||
Area *string `json:"area"`
|
||||
IspType string `json:"ispType"` // 转网前运营商
|
||||
NewIspType string `json:"newIspType"` // 转网后运营商
|
||||
}
|
||||
|
||||
// yysy7d3eResp 携号转网查询对外响应结构
|
||||
type yysy7d3eResp struct {
|
||||
BatchNo string `json:"batchNo"`
|
||||
QueryResult []yysy7d3eQueryItem `json:"queryResult"`
|
||||
}
|
||||
|
||||
type yysy7d3eQueryItem struct {
|
||||
Mobile string `json:"mobile"`
|
||||
Result string `json:"result"` // 0:否 1:是
|
||||
After string `json:"after"` // 转网后:-1未知 1移动 2联通 3电信 4广电
|
||||
Before string `json:"before"` // 转网前:同上
|
||||
}
|
||||
|
||||
// ProcessYYSY7D3ERequest YYSY7D3E API处理方法 - 携号转网查询
|
||||
func ProcessYYSY7D3ERequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||
var paramsDto dto.YYSY7D3EReq
|
||||
@@ -21,25 +44,70 @@ func ProcessYYSY7D3ERequest(ctx context.Context, params []byte, deps *processors
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
|
||||
// 构建请求数据,将项目规范的字段名转换为 XingweiService 需要的字段名
|
||||
reqData := map[string]interface{}{
|
||||
"phoneNumber": paramsDto.MobileNo,
|
||||
reqFormData := map[string]interface{}{
|
||||
"mobile": paramsDto.MobileNo,
|
||||
}
|
||||
|
||||
// 调用行为数据API,使用指定的project_id
|
||||
projectID := "CDJ-1100244706893164544"
|
||||
respBytes, err := deps.XingweiService.CallAPI(ctx, projectID, reqData)
|
||||
// 以表单方式调用数脉 API;参数在 CallAPIForm 内转为 application/x-www-form-urlencoded
|
||||
apiPath := "/v4/mobile-transfer/query"
|
||||
respBytes, err := deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData)
|
||||
if err != nil {
|
||||
if errors.Is(err, xingwei.ErrNotFound) {
|
||||
return nil, errors.Join(processors.ErrNotFound, err)
|
||||
} else if errors.Is(err, xingwei.ErrDatasource) {
|
||||
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
|
||||
mapped, err := mapShumaiToYYSY7D3E(respBytes)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
return json.Marshal(mapped)
|
||||
}
|
||||
|
||||
// mapShumaiToYYSY7D3E 将数脉携号转网响应映射为 batchNo + queryResult 格式
|
||||
func mapShumaiToYYSY7D3E(dataBytes []byte) (*yysy7d3eResp, error) {
|
||||
var r shumaiMobileTransfer7d3eResp
|
||||
if err := json.Unmarshal(dataBytes, &r); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
before := ispNameToCodeTransfer(strings.TrimSpace(r.IspType))
|
||||
after := ispNameToCodeTransfer(strings.TrimSpace(r.NewIspType))
|
||||
result := "0"
|
||||
if r.IspType != "" && r.NewIspType != "" && strings.TrimSpace(r.IspType) != strings.TrimSpace(r.NewIspType) {
|
||||
result = "1" // 转网前与转网后不同即为携号转网
|
||||
}
|
||||
|
||||
out := &yysy7d3eResp{
|
||||
BatchNo: r.OrderNo,
|
||||
QueryResult: []yysy7d3eQueryItem{
|
||||
{
|
||||
Mobile: r.Mobile,
|
||||
Result: result,
|
||||
After: after,
|
||||
Before: before,
|
||||
},
|
||||
},
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// ispNameToCodeTransfer 运营商名称转编码:-1未知 1移动 2联通 3电信 4广电
|
||||
func ispNameToCodeTransfer(name string) string {
|
||||
switch name {
|
||||
case "移动":
|
||||
return "1"
|
||||
case "联通":
|
||||
return "2"
|
||||
case "电信":
|
||||
return "3"
|
||||
case "广电":
|
||||
return "4"
|
||||
default:
|
||||
return "-1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"tyapi-server/internal/domains/api/dto"
|
||||
"tyapi-server/internal/domains/api/services/processors"
|
||||
"tyapi-server/internal/infrastructure/external/zhicha"
|
||||
"tyapi-server/internal/infrastructure/external/shumai"
|
||||
)
|
||||
|
||||
// ProcessYYSY8B1CRequest YYSY8B1C API处理方法 - 手机在网时长
|
||||
@@ -20,30 +20,81 @@ func ProcessYYSY8B1CRequest(ctx context.Context, params []byte, deps *processors
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
|
||||
encryptedMobileNo, err := deps.ZhichaService.Encrypt(paramsDto.MobileNo)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
reqFormData := map[string]interface{}{
|
||||
"mobile": paramsDto.MobileNo,
|
||||
}
|
||||
|
||||
reqData := map[string]interface{}{
|
||||
"phone": encryptedMobileNo,
|
||||
}
|
||||
|
||||
respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI003", reqData)
|
||||
// 以表单方式调用数脉 API;参数在 CallAPIForm 内转为 application/x-www-form-urlencoded
|
||||
apiPath := "/v2/mobile_online/check" // 接口路径,根据数脉文档填写(如 v4/xxx)
|
||||
respBytes, err := deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData)
|
||||
if err != nil {
|
||||
if errors.Is(err, zhicha.ErrDatasource) {
|
||||
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)
|
||||
} else {
|
||||
// 其他未知错误
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
}
|
||||
|
||||
// 将响应数据转换为JSON字节
|
||||
respBytes, err := json.Marshal(respData)
|
||||
if err != nil {
|
||||
var shumaiResp struct {
|
||||
OrderNo string `json:"order_no"`
|
||||
Channel string `json:"channel"` // cmcc/cucc/ctcc/gdcc
|
||||
Time string `json:"time"` // [0,3),[3,6),[6,12),[12,24),[24,-1)
|
||||
}
|
||||
if err := json.Unmarshal(respBytes, &shumaiResp); err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
return respBytes, nil
|
||||
// 映射 channel -> operators
|
||||
operators := channelToOperators(shumaiResp.Channel)
|
||||
// 映射 time 区间 -> inTime
|
||||
inTime := timeIntervalToInTime(shumaiResp.Time)
|
||||
|
||||
out := map[string]string{
|
||||
"inTime": inTime,
|
||||
"operators": operators,
|
||||
}
|
||||
return json.Marshal(out)
|
||||
}
|
||||
|
||||
// channelToOperators 运营商编码转名称:cmcc-移动 cucc-联通 ctcc-电信 gdcc-广电
|
||||
func channelToOperators(channel string) string {
|
||||
switch channel {
|
||||
case "cmcc":
|
||||
return "移动"
|
||||
case "cucc":
|
||||
return "联通"
|
||||
case "ctcc":
|
||||
return "电信"
|
||||
case "gdcc":
|
||||
return "广电"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// timeIntervalToInTime 在网时间区间转 inTime 值
|
||||
// [0,3)->0, [3,6)->3, [6,12)->6, [12,24)->12, [24,-1)->24
|
||||
// 空或异常->99, 查无记录->-1(此处按空/未知处理为99)
|
||||
func timeIntervalToInTime(timeInterval string) string {
|
||||
switch timeInterval {
|
||||
case "[0,3)":
|
||||
return "0"
|
||||
case "[3,6)":
|
||||
return "3"
|
||||
case "[6,12)":
|
||||
return "6"
|
||||
case "[12,24)":
|
||||
return "12"
|
||||
case "[24,-1)":
|
||||
return "24"
|
||||
case "":
|
||||
return "-1"
|
||||
default:
|
||||
return "99"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"tyapi-server/internal/domains/api/dto"
|
||||
"tyapi-server/internal/domains/api/services/processors"
|
||||
"tyapi-server/internal/infrastructure/external/zhicha"
|
||||
"tyapi-server/internal/infrastructure/external/shumai"
|
||||
)
|
||||
|
||||
// ProcessYYSY9F1BYequest YYSY9F1B API处理方法 - 手机二要素验证
|
||||
@@ -21,37 +21,94 @@ func ProcessYYSY9F1BYequest(ctx context.Context, params []byte, deps *processors
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
|
||||
encryptedName, err := deps.ZhichaService.Encrypt(paramsDto.Name)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
reqFormData := map[string]interface{}{
|
||||
"mobile": paramsDto.MobileNo,
|
||||
"name": paramsDto.Name,
|
||||
}
|
||||
|
||||
encryptedMobileNo, err := deps.ZhichaService.Encrypt(paramsDto.MobileNo)
|
||||
// 3m8s 运营商二要素:order_no, fee, result(0-一致 1-不一致)。失败则直接返回,不再调携号转网接口。
|
||||
apiPath := "/v4/mobile_two/check"
|
||||
respBytes, err := deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
reqData := map[string]interface{}{
|
||||
"name": encryptedName,
|
||||
"phone": encryptedMobileNo,
|
||||
"authorized": paramsDto.Authorized,
|
||||
}
|
||||
|
||||
respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI040", reqData)
|
||||
if err != nil {
|
||||
if errors.Is(err, zhicha.ErrDatasource) {
|
||||
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)
|
||||
} else {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
}
|
||||
|
||||
// 将响应数据转换为JSON字节
|
||||
respBytes, err := json.Marshal(respData)
|
||||
// s9w1 手机携号转网(仅在上方二要素成功后再调)
|
||||
apiPath2 := "/v4/mobile-transfer/query"
|
||||
respBytes2, err := deps.ShumaiService.CallAPIForm(ctx, apiPath2, reqFormData)
|
||||
if err != nil {
|
||||
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)
|
||||
} else {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
}
|
||||
|
||||
var twoFactorResp struct {
|
||||
OrderNo string `json:"order_no"`
|
||||
Fee int `json:"fee"`
|
||||
Result int `json:"result"` // 0-一致 1-不一致
|
||||
}
|
||||
if err := json.Unmarshal(respBytes, &twoFactorResp); err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
return respBytes, nil
|
||||
var transferResp struct {
|
||||
OrderNo string `json:"order_no"`
|
||||
Mobile string `json:"mobile"`
|
||||
Area *string `json:"area"`
|
||||
IspType string `json:"ispType"` // 转网前运营商
|
||||
NewIspType string `json:"newIspType"` // 转网后运营商
|
||||
}
|
||||
if err := json.Unmarshal(respBytes2, &transferResp); err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
// state: 1-一致 2-不一致 3-异常
|
||||
state := "3"
|
||||
switch twoFactorResp.Result {
|
||||
case 0:
|
||||
state = "1"
|
||||
case 1:
|
||||
state = "2"
|
||||
}
|
||||
|
||||
operator := ispNameToCode(transferResp.IspType)
|
||||
operatorReal := ispNameToCode(transferResp.NewIspType)
|
||||
|
||||
// is_xhzw: 0-否 1-是(转网前与转网后运营商不同即为携号转网)
|
||||
isXhzw := "0"
|
||||
if transferResp.IspType != "" && transferResp.NewIspType != "" && transferResp.IspType != transferResp.NewIspType {
|
||||
isXhzw = "1"
|
||||
}
|
||||
|
||||
out := map[string]string{
|
||||
"operator_real": operatorReal,
|
||||
"state": state,
|
||||
"is_xhzw": isXhzw,
|
||||
"operator": operator,
|
||||
}
|
||||
return json.Marshal(out)
|
||||
}
|
||||
|
||||
// ispNameToCode 运营商名称转编码:1-移动 2-联通 3-电信
|
||||
func ispNameToCode(name string) string {
|
||||
switch name {
|
||||
case "移动":
|
||||
return "1"
|
||||
case "联通":
|
||||
return "2"
|
||||
case "电信":
|
||||
return "3"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"tyapi-server/internal/domains/api/dto"
|
||||
"tyapi-server/internal/domains/api/services/processors"
|
||||
@@ -20,9 +21,12 @@ func ProcessYYSYF2T7Request(ctx context.Context, params []byte, deps *processors
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
// 从入参 date_range(YYYYMMDD-YYYYMMDD)提取右区间作为 date
|
||||
parts := strings.SplitN(paramsDto.DateRange, "-", 2)
|
||||
dateEnd := strings.TrimSpace(parts[1]) // 校验已保证格式正确,取结束日期
|
||||
reqFormData := map[string]interface{}{
|
||||
"mobile": paramsDto.MobileNo,
|
||||
"date": paramsDto.DateRange,
|
||||
"date": dateEnd,
|
||||
}
|
||||
|
||||
// 以表单方式调用数脉 API;参数在 CallAPIForm 内转为 application/x-www-form-urlencoded
|
||||
|
||||
@@ -48,11 +48,11 @@ func ProcessYYSYF7DBRequest(ctx context.Context, params []byte, deps *processors
|
||||
|
||||
// 组装日期:开始日期 + 当前日期(YYYYMMDD-YYYYMMDD)
|
||||
today := time.Now().Format("20060102")
|
||||
dateRange := paramsDto.StartDate + "-" + today
|
||||
// dateRange := startDateYyyymmdd + "-" + today
|
||||
|
||||
reqFormData := map[string]interface{}{
|
||||
"mobile": paramsDto.MobileNo,
|
||||
"date": dateRange,
|
||||
"date": today,
|
||||
}
|
||||
|
||||
// 以表单方式调用数脉 API;参数在 CallAPIForm 内转为 application/x-www-form-urlencoded
|
||||
|
||||
@@ -78,10 +78,13 @@ func RegisterCustomValidators(validate *validator.Validate) {
|
||||
// 非空字符串验证器(不能为空字符串或只包含空格)
|
||||
validate.RegisterValidation("notEmpty", validateNotEmpty)
|
||||
|
||||
// 授权日期验证器
|
||||
// 授权日期验证器(格式 YYYYMMDD-YYYYMMDD,且范围必须包含今天)
|
||||
validate.RegisterValidation("auth_date", validateAuthDate)
|
||||
validate.RegisterValidation("validAuthDate", validateAuthDate)
|
||||
|
||||
// 日期范围验证器(格式 YYYYMMDD-YYYYMMDD,开始≤结束,不要求包含今天)
|
||||
validate.RegisterValidation("validDateRange", validateDateRange)
|
||||
|
||||
// 授权书URL验证器
|
||||
validate.RegisterValidation("authorization_url", validateAuthorizationURL)
|
||||
|
||||
@@ -312,6 +315,32 @@ func validateAuthDate(fl validator.FieldLevel) bool {
|
||||
return !startDate.After(today) && !endDate.Before(today)
|
||||
}
|
||||
|
||||
// validateDateRange 日期范围验证器
|
||||
// 格式:YYYYMMDD-YYYYMMDD,开始日期不能晚于结束日期(不要求范围包含今天)
|
||||
func validateDateRange(fl validator.FieldLevel) bool {
|
||||
s := fl.Field().String()
|
||||
if s == "" {
|
||||
return true
|
||||
}
|
||||
parts := strings.Split(s, "-")
|
||||
if len(parts) != 2 {
|
||||
return false
|
||||
}
|
||||
startStr, endStr := parts[0], parts[1]
|
||||
if len(startStr) != 8 || len(endStr) != 8 {
|
||||
return false
|
||||
}
|
||||
startDate, err := parseYYYYMMDD(startStr)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
endDate, err := parseYYYYMMDD(endStr)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return !startDate.After(endDate)
|
||||
}
|
||||
|
||||
// parseYYYYMMDD 解析YYYYMMDD格式的日期字符串
|
||||
func parseYYYYMMDD(dateStr string) (time.Time, error) {
|
||||
if len(dateStr) != 8 {
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
func RegisterCustomTranslations(validate *validator.Validate, trans ut.Translator) {
|
||||
// 注册标准字段翻译
|
||||
registerStandardTranslations(validate, trans)
|
||||
|
||||
|
||||
// 注册自定义字段翻译
|
||||
registerCustomFieldTranslations(validate, trans)
|
||||
}
|
||||
@@ -194,7 +194,7 @@ func registerCustomFieldTranslations(validate *validator.Validate, trans ut.Tran
|
||||
t, _ := ut.T("auth_date", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
|
||||
validate.RegisterTranslation("validAuthDate", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("validAuthDate", "{0}格式不正确,必须是YYYYMMDD-YYYYMMDD格式,且日期范围必须包括今天", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
@@ -202,6 +202,13 @@ func registerCustomFieldTranslations(validate *validator.Validate, trans ut.Tran
|
||||
return t
|
||||
})
|
||||
|
||||
validate.RegisterTranslation("validDateRange", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("validDateRange", "{0}格式不正确,必须是YYYYMMDD-YYYYMMDD格式,且开始日期不能晚于结束日期", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
t, _ := ut.T("validDateRange", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
// 时间范围翻译
|
||||
validate.RegisterTranslation("validTimeRange", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("validTimeRange", "{0}格式不正确,必须是HH:MM-HH:MM格式", true)
|
||||
@@ -305,7 +312,7 @@ func registerCustomFieldTranslations(validate *validator.Validate, trans ut.Tran
|
||||
t, _ := ut.T("validEnterpriseName", getFieldDisplayName(fe.Field()))
|
||||
return t
|
||||
})
|
||||
|
||||
|
||||
validate.RegisterTranslation("enterprise_name", trans, func(ut ut.Translator) error {
|
||||
return ut.Add("enterprise_name", "{0}格式不正确,必须包含至少一个汉字,长度2-100字符", true)
|
||||
}, func(ut ut.Translator, fe validator.FieldError) string {
|
||||
@@ -410,4 +417,4 @@ func getFieldDisplayName(field string) string {
|
||||
return displayName
|
||||
}
|
||||
return field
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user