This commit is contained in:
Mrx
2026-05-29 12:28:08 +08:00
parent 43acbeb8f4
commit 0973bb099c
21 changed files with 2964 additions and 156 deletions

View File

@@ -9,24 +9,24 @@ import (
"strings" "strings"
"time" "time"
"hyapi-server/internal/application/certification/dto/commands" "tyapi-server/internal/application/certification/dto/commands"
"hyapi-server/internal/application/certification/dto/queries" "tyapi-server/internal/application/certification/dto/queries"
"hyapi-server/internal/application/certification/dto/responses" "tyapi-server/internal/application/certification/dto/responses"
"hyapi-server/internal/config" "tyapi-server/internal/config"
api_service "hyapi-server/internal/domains/api/services" api_service "tyapi-server/internal/domains/api/services"
"hyapi-server/internal/domains/certification/entities" "tyapi-server/internal/domains/certification/entities"
certification_value_objects "hyapi-server/internal/domains/certification/entities/value_objects" certification_value_objects "tyapi-server/internal/domains/certification/entities/value_objects"
"hyapi-server/internal/domains/certification/enums" "tyapi-server/internal/domains/certification/enums"
"hyapi-server/internal/domains/certification/repositories" "tyapi-server/internal/domains/certification/repositories"
"hyapi-server/internal/domains/certification/services" "tyapi-server/internal/domains/certification/services"
finance_service "hyapi-server/internal/domains/finance/services" finance_service "tyapi-server/internal/domains/finance/services"
user_entities "hyapi-server/internal/domains/user/entities" user_entities "tyapi-server/internal/domains/user/entities"
user_service "hyapi-server/internal/domains/user/services" user_service "tyapi-server/internal/domains/user/services"
"hyapi-server/internal/infrastructure/external/notification" "tyapi-server/internal/infrastructure/external/notification"
"hyapi-server/internal/infrastructure/external/storage" "tyapi-server/internal/infrastructure/external/storage"
"hyapi-server/internal/shared/database" "tyapi-server/internal/shared/database"
"hyapi-server/internal/shared/esign" "tyapi-server/internal/shared/esign"
sharedOCR "hyapi-server/internal/shared/ocr" sharedOCR "tyapi-server/internal/shared/ocr"
"go.uber.org/zap" "go.uber.org/zap"
) )

View File

@@ -0,0 +1,59 @@
package flxg
import (
"context"
"encoding/json"
"errors"
"tyapi-server/internal/domains/api/dto"
"tyapi-server/internal/domains/api/services/processors"
"tyapi-server/internal/infrastructure/external/zhicha"
)
// ProcessFLXG5A3BCOPYRequest FLXG5A3B COPY API处理方法 - 个人司法涉诉
func ProcessFLXG5A3BCOPYRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
var paramsDto dto.FLXG5A3BReq
if err := json.Unmarshal(params, &paramsDto); 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 paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" || paramsDto.IDCard == "420116198907031413" || paramsDto.IDCard == "13032319930128263X" {
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
}
encryptedName, err := deps.ZhichaService.Encrypt(paramsDto.Name)
if err != nil {
return nil, errors.Join(processors.ErrSystem, err)
}
encryptedIDCard, err := deps.ZhichaService.Encrypt(paramsDto.IDCard)
if err != nil {
return nil, errors.Join(processors.ErrSystem, err)
}
reqData := map[string]interface{}{
"name": encryptedName,
"idCard": encryptedIDCard,
"authorized": paramsDto.Authorized,
}
respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI006", reqData)
if err != nil {
if errors.Is(err, zhicha.ErrDatasource) {
return nil, errors.Join(processors.ErrDatasource, 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
}

View File

@@ -7,7 +7,7 @@ import (
"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/zhicha" "tyapi-server/internal/infrastructure/external/nuoer"
) )
// ProcessFLXG5A3BRequest FLXG5A3B API处理方法 - 个人司法涉诉 // ProcessFLXG5A3BRequest FLXG5A3B API处理方法 - 个人司法涉诉
@@ -24,33 +24,33 @@ func ProcessFLXG5A3BRequest(ctx context.Context, params []byte, deps *processors
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空")) return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
} }
encryptedName, err := deps.ZhichaService.Encrypt(paramsDto.Name) body := map[string]string{
if err != nil { "name": paramsDto.Name,
return nil, errors.Join(processors.ErrSystem, err) "idCard": paramsDto.IDCard,
} }
encryptedIDCard, err := deps.ZhichaService.Encrypt(paramsDto.IDCard) nuoerDoCheckAPIKey := "personalLawsuit_cv1"
if err != nil { ApiPath := "/v1/doCheck"
return nil, errors.Join(processors.ErrSystem, err)
}
reqData := map[string]interface{}{ resp, err := deps.NuoerService.CallAPI(ctx, nuoerDoCheckAPIKey, ApiPath, body)
"name": encryptedName,
"idCard": encryptedIDCard,
"authorized": paramsDto.Authorized,
}
respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI006", reqData)
if err != nil { if err != nil {
if errors.Is(err, zhicha.ErrDatasource) { if errors.Is(err, nuoer.ErrDatasource) {
return nil, errors.Join(processors.ErrDatasource, err) return nil, errors.Join(processors.ErrDatasource, err)
} else {
return nil, errors.Join(processors.ErrSystem, err)
} }
if errors.Is(err, nuoer.ErrNotFound) {
return nil, errors.Join(processors.ErrNotFound, err)
}
return nil, errors.Join(processors.ErrSystem, err)
} }
// 将响应数据转换为JSON字节 rawData, ok := resp.Data.(map[string]interface{})
respBytes, err := json.Marshal(respData) if !ok {
return nil, errors.Join(processors.ErrSystem, errors.New("响应格式错误"))
}
result := mapNuoerPersonalLawsuitToResponse(rawData)
respBytes, err := json.Marshal(result)
if err != nil { if err != nil {
return nil, errors.Join(processors.ErrSystem, err) return nil, errors.Join(processors.ErrSystem, err)
} }

View File

@@ -0,0 +1,177 @@
package flxg
// mapNuoerPersonalLawsuitToResponse 将 nuoer 响应转为对外结构2.md
// lawsuitStat -> entout, breachCaseList -> sxbzxr, consumptionRestrictionList -> xgbzxr
func mapNuoerPersonalLawsuitToResponse(data map[string]interface{}) map[string]interface{} {
if data == nil {
return defaultFLXG5A3BResponse()
}
if _, hasEntout := data["entout"]; hasEntout {
return data
}
flat := unwrapNuoerPersonalLawsuitData(data)
return map[string]interface{}{
"sxbzxr": asSlice(flat["breachCaseList"]),
"xgbzxr": asSlice(flat["consumptionRestrictionList"]),
"entout": pickEntout(flat["lawsuitStat"]),
}
}
func defaultFLXG5A3BResponse() map[string]interface{} {
return map[string]interface{}{
"sxbzxr": []interface{}{},
"entout": map[string]interface{}{},
"xgbzxr": []interface{}{},
}
}
func pickEntout(v interface{}) interface{} {
if v == nil {
return map[string]interface{}{}
}
return v
}
// unwrapNuoerPersonalLawsuitData 解包 nuoer 嵌套结构 data.result.detail[],提取 1.md 平铺字段。
func unwrapNuoerPersonalLawsuitData(data map[string]interface{}) map[string]interface{} {
if _, ok := data["lawsuitStat"]; ok {
return data
}
if result, ok := data["result"].(map[string]interface{}); ok {
for _, item := range asSlice(result["detail"]) {
if flat := extractFromNuoerDetailItem(asMap(item)); len(flat) > 0 {
return flat
}
}
}
return data
}
func extractFromNuoerDetailItem(item map[string]interface{}) map[string]interface{} {
if len(item) == 0 {
return nil
}
lawsuitStat := extractLawsuitStatFromDetailItem(item)
if !isLawsuitStatPayload(asMap(lawsuitStat)) {
return nil
}
return map[string]interface{}{
"lawsuitStat": lawsuitStat,
"breachCaseList": extractBreachCaseRecords(item),
"consumptionRestrictionList": extractConsumptionRestrictionRecords(item),
}
}
func extractLawsuitStatFromDetailItem(item map[string]interface{}) interface{} {
// nuoer 真实涉诉数据通常在 breachCaseList/consumptionRestrictionList 的包装对象内
for _, v := range asSlice(item["breachCaseList"]) {
if m := asMap(v); m != nil {
if ls := asMap(m["lawsuitStat"]); isLawsuitStatPayload(ls) {
return ls
}
}
}
for _, v := range asSlice(item["consumptionRestrictionList"]) {
if m := asMap(v); m != nil {
if ls := asMap(m["lawsuitStat"]); isLawsuitStatPayload(ls) {
return ls
}
}
}
if lsWrap := asMap(item["lawsuitStat"]); lsWrap != nil {
if ls := extractLawsuitStatFromWrapper(lsWrap); ls != nil {
return ls
}
}
return map[string]interface{}{}
}
func extractLawsuitStatFromWrapper(wrap map[string]interface{}) map[string]interface{} {
if isLawsuitStatPayload(wrap) {
return wrap
}
for _, v := range asSlice(wrap["detail"]) {
if m := asMap(v); m != nil {
if ls := asMap(m["lawsuitStat"]); isLawsuitStatPayload(ls) {
return ls
}
if isLawsuitStatPayload(m) {
return m
}
}
}
return nil
}
func isLawsuitStatPayload(m map[string]interface{}) bool {
if len(m) == 0 {
return false
}
for _, k := range []string{"criminal", "civil", "administrative", "cases_tree", "count", "crc", "implement", "preservation", "bankrupt"} {
if _, ok := m[k]; ok {
return true
}
}
return false
}
func extractBreachCaseRecords(item map[string]interface{}) []interface{} {
out := make([]interface{}, 0)
collectBreachCaseRecords(asSlice(item["breachCaseList"]), &out)
if lsWrap := asMap(item["lawsuitStat"]); lsWrap != nil {
for _, v := range asSlice(lsWrap["detail"]) {
collectBreachCaseRecords(asSlice(asMap(v)["breachCaseList"]), &out)
}
}
return out
}
func collectBreachCaseRecords(list []interface{}, out *[]interface{}) {
for _, v := range list {
m := asMap(v)
if len(m) == 0 {
continue
}
if _, isWrapper := m["lawsuitStat"]; isWrapper {
if inner := asSlice(m["breachCaseList"]); len(inner) > 0 {
collectBreachCaseRecords(inner, out)
}
continue
}
if _, hasAh := m["ah"]; hasAh {
*out = append(*out, m)
}
}
}
func extractConsumptionRestrictionRecords(item map[string]interface{}) []interface{} {
out := make([]interface{}, 0)
collectConsumptionRestrictionRecords(asSlice(item["consumptionRestrictionList"]), &out)
if lsWrap := asMap(item["lawsuitStat"]); lsWrap != nil {
for _, v := range asSlice(lsWrap["detail"]) {
collectConsumptionRestrictionRecords(asSlice(asMap(v)["consumptionRestrictionList"]), &out)
}
}
return out
}
func collectConsumptionRestrictionRecords(list []interface{}, out *[]interface{}) {
for _, v := range list {
m := asMap(v)
if len(m) == 0 {
continue
}
if _, isWrapper := m["lawsuitStat"]; isWrapper {
if inner := asSlice(m["consumptionRestrictionList"]); len(inner) > 0 {
collectConsumptionRestrictionRecords(inner, out)
}
continue
}
if _, hasAh := m["ah"]; hasAh {
*out = append(*out, m)
}
}
}

View File

@@ -7,7 +7,7 @@ import (
"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/zhicha" "tyapi-server/internal/infrastructure/external/nuoer"
) )
// ProcessFLXGDEA9Request FLXGDEA9 API处理方法 // ProcessFLXGDEA9Request FLXGDEA9 API处理方法
@@ -21,34 +21,36 @@ func ProcessFLXGDEA9Request(ctx context.Context, params []byte, deps *processors
return nil, errors.Join(processors.ErrInvalidParam, err) return nil, errors.Join(processors.ErrInvalidParam, err)
} }
encryptedName, err := deps.ZhichaService.Encrypt(paramsDto.Name)
if err != nil {
return nil, errors.Join(processors.ErrSystem, err)
}
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" || paramsDto.IDCard == "420116198907031413" || paramsDto.IDCard == "13032319930128263X" { if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "431027198810290730" || paramsDto.IDCard == "362502199510298017" || paramsDto.IDCard == "340826199008250378" || paramsDto.IDCard == "321027198304072129" || paramsDto.IDCard == "420116198907031413" || paramsDto.IDCard == "13032319930128263X" {
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空")) return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
} }
encryptedIDCard, err := deps.ZhichaService.Encrypt(paramsDto.IDCard) body := map[string]string{
"name": paramsDto.Name,
"idCard": paramsDto.IDCard,
}
nuoerDoCheckAPIKey := "idRiskTagV106"
ApiPath := "/v1/doCheck"
resp, err := deps.NuoerService.CallAPI(ctx, nuoerDoCheckAPIKey, ApiPath, body)
if err != nil { if err != nil {
if errors.Is(err, nuoer.ErrDatasource) {
return nil, errors.Join(processors.ErrDatasource, err)
}
if errors.Is(err, nuoer.ErrNotFound) {
return nil, errors.Join(processors.ErrNotFound, err)
}
return nil, errors.Join(processors.ErrSystem, err) return nil, errors.Join(processors.ErrSystem, err)
} }
reqData := map[string]interface{}{
"name": encryptedName, rawData, ok := resp.Data.(map[string]interface{})
"idCard": encryptedIDCard, if !ok {
"authorized": paramsDto.Authorized, return nil, errors.Join(processors.ErrSystem, errors.New("响应格式错误"))
} }
respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI005", reqData) result := mapNuoerIdRiskToResponse(rawData)
if err != nil {
if errors.Is(err, zhicha.ErrDatasource) {
return nil, errors.Join(processors.ErrDatasource, err)
} else {
return nil, errors.Join(processors.ErrSystem, err)
}
}
// 将响应数据转换为JSON字节 respBytes, err := json.Marshal(result)
respBytes, err := json.Marshal(respData)
if err != nil { if err != nil {
return nil, errors.Join(processors.ErrSystem, err) return nil, errors.Join(processors.ErrSystem, err)
} }

View File

@@ -0,0 +1,154 @@
package flxg
import (
"fmt"
"strconv"
"strings"
)
// riskCode 位序(从左至右 1-25到 level 代码映射,见 2.md / 1.md。
var riskCodeBitLevelMappings = map[int]string{
1: "A",
2: "B2",
3: "B",
4: "B",
5: "B4",
6: "B3",
7: "B3",
8: "B3",
9: "B3",
10: "B3",
11: "A",
12: "B",
13: "J",
14: "C3",
15: "C3",
16: "A",
17: "D",
18: "D4",
19: "D2",
20: "D",
21: "E",
22: "E",
23: "E",
24: "E",
}
// mapNuoerIdRiskToResponse 将 nuoer 响应2json.md转为对外结构1json.md
// 解包 result将 riskCode 转为 level。
func mapNuoerIdRiskToResponse(data map[string]interface{}) map[string]interface{} {
if data == nil {
return map[string]interface{}{"level": "0"}
}
if level, ok := data["level"]; ok && level != nil {
if s := strings.TrimSpace(stringifyRiskCodeVal(level)); s != "" {
return map[string]interface{}{"level": s}
}
}
payload := unwrapNuoerIdRiskData(data)
riskCode := strings.TrimSpace(stringifyRiskCodeVal(payload["riskCode"]))
return map[string]interface{}{
"level": mapRiskCodeToLevel(riskCode),
}
}
func unwrapNuoerIdRiskData(data map[string]interface{}) map[string]interface{} {
if _, ok := data["riskCode"]; ok {
return data
}
if result, ok := data["result"].(map[string]interface{}); ok {
return result
}
return data
}
func mapRiskCodeToLevel(riskCode string) string {
if riskCode == "" {
return "0"
}
codes := make([]string, 0, 8)
for i, ch := range riskCode {
if ch != '1' {
continue
}
pos := i + 1
if pos == 25 {
continue
}
if code, ok := riskCodeBitLevelMappings[pos]; ok && code != "" {
codes = append(codes, code)
}
}
codes = dedupeLevelCodes(codes)
codes = collapseParentLevelCodes(codes)
if len(codes) == 0 {
return "0"
}
return strings.Join(codes, ",")
}
func dedupeLevelCodes(codes []string) []string {
if len(codes) == 0 {
return codes
}
seen := make(map[string]struct{}, len(codes))
out := make([]string, 0, len(codes))
for _, code := range codes {
if _, ok := seen[code]; ok {
continue
}
seen[code] = struct{}{}
out = append(out, code)
}
return out
}
func collapseParentLevelCodes(codes []string) []string {
if len(codes) == 0 {
return codes
}
hasChild := func(parent string) bool {
for _, code := range codes {
if code != parent && strings.HasPrefix(code, parent) {
return true
}
}
return false
}
out := make([]string, 0, len(codes))
for _, code := range codes {
switch code {
case "A", "B", "C", "D":
if hasChild(code) {
continue
}
}
out = append(out, code)
}
return out
}
func stringifyRiskCodeVal(v interface{}) string {
if v == nil {
return ""
}
switch val := v.(type) {
case string:
return val
case float64:
if val == float64(int64(val)) {
return strconv.FormatInt(int64(val), 10)
}
return strconv.FormatFloat(val, 'f', -1, 64)
case int:
return strconv.Itoa(val)
case int64:
return strconv.FormatInt(val, 10)
default:
return fmt.Sprint(val)
}
}

View File

@@ -0,0 +1,99 @@
## 返回参数字段说明
| 字段名 | 类型 | 说明 |
|-------------------------|---------|--------------------------------------------------------------|
| seqNo | string | 调用唯一标识(如有接口问题,请提供此值) |
| orgName | string | 查询人对应的企业名 |
| pName | string | 查询人名称 |
| relationship | array | 查询人与企业的关联sh:股东lp:法人tm:高管his_sh:历史股东his_tm:历史高管) |
| basicInfo | object | 查询人所在企业的基本信息 |
| his_stockHolderItem | object | 历史持股信息 |
| stockHolderItem | object | 持股信息 |
| adminPenalty | object | 行政处罚 |
| executedPerson | object | 被执行人 |
| dishonestExecutedPerson | object | 失信被执行人 |
### basicInfo 返回信息
| 字段名 | 类型 | 说明 |
|-------------------|---------|----------------|
| regStatus | string | 企业状态 |
| estiblishTime | string | 注册日期 |
| regCapital | string | 注册资本 |
| industry | string | 行业 |
| type | string | 类型 1-人 2-公司|
| regCapitalCurrency| string | 注册资本币种 |
| legalPersonName | string | 法人姓名 |
| regNumber | string | 注册号 |
| creditCode | string | 统一社会信用代码|
| name | string | 企业名 |
| companyOrgType | string | 企业类型 |
| base | string | 省份简称 |
| revdate | string | 吊销日期 |
| apprdate | string | 核准日期 |
| candate | string | 注销日期 |
| reccap | string | 实收注册资本 |
| reccapcur | string | 实收注册资本币种|
| province | string | 省 |
| city | string | 市 |
| district | string | 区 |
| regorg | string | 登记机关 |
| opscope | string | 经营范围 |
| nic_code | string | 国民经济行业代码|
| nic_name | string | 国民经济行业名称|
| industry_code | string | 门类代码 |
| his_staffList | object | 历史高管 |
| tel | string | 电话 |
### stockHolderItem 返回信息
| 字段名 | 类型 | 说明 |
|----------------|--------|----------|
| orgHolderType | string | 股东类型 |
| investDate | string | 出资时间 |
| investRate | string | 占比 |
| subscriptAmt | string | 出资金额 |
| orgHolderName | string | 股东名 |
### adminPenalty 返回信息
| 字段名 | 类型 | 说明 |
|----------------|--------|----------|
| departmentName | string | 处罚单位 |
| reason | string | 处罚事由 |
| punishNumber | string | 决定书文号|
| type | string | 处罚类别 |
| content | string | 处罚结果 |
| decisionDate | string | 处罚日期 |
| legalPersonName| string | 法人 |
### executedPerson 返回信息
| 字段名 | 类型 | 说明 |
|---------------|--------|----------------|
| caseCode | string | 案号 |
| pname | string | 被执行人名称 |
| caseCreateTime| string | 立案日期 |
| execCourtName | string | 执行法院 |
| execMoney | string | 执行标的(元) |
### dishonestExecutedPerson 返回信息
| 字段名 | 类型 | 说明 |
|----------------|--------|------------------------------|
| businessentity | string | 法人/负责人姓名 |
| areaname | string | 省份地区 |
| courtname | string | 法院 |
| unperformPart | string | 未履行部分 |
| type | string | 失信人类型 0 代表人 |
| performedPart | string | 已履行部分 |
| iname | string | 失信人名称 |
| disrupttypename| string | 失信被执行人行为具体情形 |
| casecode | string | 案号 |
| performance | string | 履行情况 |
| regdate | string | 立案时间 |
| duty | string | 生效法律文书确定的义务 |
| gistunit | string | 做出执行的依据单位 |
| publishdate | string | 发布时间 |
| gistid | string | 执行依据文号 |

View File

@@ -0,0 +1,120 @@
```
{
"datalist": [
{
"orgName": "南阳市宛城区张明健康服务中心",
"pName": "张明",
"relationship": [
"lp"
],
"basicInfo": {
"regStatus": "在营(开业)企业",
"regCapital": "10.0万元人民币",
"reccap": 0,
"city": "南阳市",
"industry_code": "O",
"industry": "居民服务业",
"type": "1",
"nic_code": "O805",
"legalPersonName": "张明",
"regNumber": "411302601209908",
"creditCode": "92411302MA3P3WW37Y",
"province": "河南省",
"regorg": "南阳市宛城区市场监督管理局",
"companyOrgType": "个体",
"tel": "",
"revdate": "",
"estiblishTime": "2019-01-28",
"opscope": "",
"reccapcur": "人民币",
"regCapitalCurrency": "人民币",
"nic_name": "居民服务、修理和其他服务业-居民服务业-洗浴和保健养生服务",
"candate": "",
"district": "宛城区",
"name": "南阳市宛城区张明健康服务中心",
"base": "hn",
"apprdate": "2019-01-28"
}
},
{
"orgName": "南阳市宛城区宇旺日用品厂",
"pName": "张明",
"relationship": [
"lp"
],
"basicInfo": {
"regStatus": "已吊销",
"regCapital": "8.0万元人民币",
"reccap": 0,
"city": "南阳市",
"industry_code": "C",
"industry": "化学原料和化学制品制造业",
"type": "1",
"nic_code": "C2681",
"legalPersonName": "张明",
"regNumber": "411302600394898",
"creditCode": "",
"province": "河南省",
"regorg": "南阳市宛城区市场监督管理局",
"companyOrgType": "个体",
"tel": "",
"revdate": "2011-01-14",
"estiblishTime": "2008-05-27",
"opscope": "",
"reccapcur": "人民币",
"regCapitalCurrency": "人民币",
"nic_name": "制造业-化学原料和化学制品制造业-日用化学产品制造-肥皂及洗涤剂制造",
"candate": "",
"district": "宛城区",
"name": "南阳市宛城区宇旺日用品厂",
"base": "hn",
"apprdate": "2008-05-27"
}
},
{
"orgName": "南阳市信和工程有限公司",
"pName": "张明",
"stockHolderItem": {
"orgHolderType": "自然人",
"investDate": "2011-12-19",
"investRate": "10.0%",
"subscriptAmt": 10,
"orgHolderName": "张明"
},
"relationship": [
"sh",
"lp",
"tm"
],
"basicInfo": {
"regStatus": "注销企业",
"regCapital": "100.000000万人民币",
"reccap": 0,
"city": "南阳市",
"industry_code": "E",
"industry": "建筑安装业",
"type": "1",
"nic_code": "E499",
"legalPersonName": "张明",
"regNumber": "411327200008561",
"creditCode": "91411300MA9312Y112",
"province": "河南省",
"regorg": "南阳市高新技术开发区市场监督管理局",
"companyOrgType": "有限责任公司(自然人投资或控股)",
"tel": "",
"revdate": "",
"estiblishTime": "2012-01-04",
"opscope": "通讯设备安装维护;电气设备安装维护;通信及电气工程施工。(不含无线电发射及卫星接收设备。需许可经营的,须凭许可证经营)(依法须经批准的项目,经相关部门批准后方可开展经营活动)。",
"reccapcur": "人民币",
"regCapitalCurrency": "人民币",
"nic_name": "建筑业-建筑安装业-其他建筑安装业",
"candate": "2015-08-31",
"district": "宛城区",
"name": "南阳市信和工程有限公司",
"base": "hn",
"apprdate": "2015-08-31"
}
}
]
}
```

View File

@@ -0,0 +1,97 @@
# 5. 返回参数说明
## result.items 整体结构(数组)
| 参数名 | 类型 | 说明 |
| ---- | ---- | ---- |
| total | number | 条数 |
| datalist | array | 结果列表 |
### datalist 子参数
| 参数名 | 类型 | 说明 |
| ---- | ---- | ---- |
| orgName | string | 查询人对应的企业名 |
| pName | string | 查询人名称 |
| relationship | array | 查询人与这家企业的关联sh股东lp法人tm高管 |
| basicInfo | object | 查询人作为股东所在公司的基本信息 |
| stockholder | object | 查询人持股信息 |
| adminPenalty | array | 行政处罚 |
| executedPerson | array | 被执行人(人员) |
| dishonestExecutedPerson | array | 失信被执行人(人员) |
#### basicInfo 企业基本信息object
| 参数名 | 类型 | 说明 |
| ---- | ---- | ---- |
| regStatus | string | 企业状态 |
| estiblishTime | string | 成立日期(注册日期) |
| regCapital | string | 注册资本 |
| industry | string | 行业 |
| staffList | object | 主要人员 |
| regCapitalCurrency | string | 注册资本币种:人民币、美元、欧元等 |
| legalPersonName | string | 法人姓名 |
| regNumber | string | 注册号 |
| creditCode | string | 统一社会信用代码 |
| name | string | 企业名 |
| companyOrgType | string | 企业类型 |
| base | string | 省份简称 |
##### staffList 主要人员object
| 参数名 | 类型 | 说明 |
| ---- | ---- | ---- |
| result | array | 人员列表 |
###### staffList.result 人员列表array
| 参数名 | 类型 | 说明 |
| ---- | ---- | ---- |
| name | string | 名称 |
| type | string | 1-公司 2-人 |
| typeJoin | array | 职位 |
| type | string | 法人类型1 人 2 公司 |
#### stockholder 持股信息object
| 参数名 | 类型 | 说明 |
| ---- | ---- | ---- |
| orgHolderType | string | 股东类型 |
| investDate | string | 出资时间 |
| investRate | string | 占比 |
| subscriptAmt | string | 出资金额(万元) |
| orgHolderName | string | 股东名 |
#### adminPenalty 行政处罚array
| 参数名 | 类型 | 说明 |
| ---- | ---- | ---- |
| departmentName | string | 处罚单位 |
| reason | string | 处罚事由/违法行为类型 |
| punishNumber | string | 决定文书号 |
| type | string | 处罚类别 |
| content | string | 处罚结果/内容 |
| decisionDate | string | 处罚日期 |
| legalPersonName | string | 法定代表人 |
#### executedPerson 被执行人人员array
| 参数名 | 类型 | 说明 |
| ---- | ---- | ---- |
| caseCode | string | 案号 |
| partyCardNum | string | 身份证号/组织机构代码 |
| pname | string | 被执行人名称 |
| caseCreateTime | string | 立案日期 |
| execCourtName | string | 执行法院 |
| execMoney | string | 执行标的(元) |
#### dishonestExecutedPerson 失信被执行人人员array
| 参数名 | 类型 | 说明 |
| ---- | ---- | ---- |
| businessentity | string | 法人、负责人姓名 |
| areaname | string | 省份地区 |
| courtname | string | 法院 |
| unperformPart | string | 未履行部分 |
| type | string | 失信人类型0代表人1代表公司 |
| performedPart | string | 已履行部分 |
| iname | string | 失信人名称 |
| disrupttypename | string | 失信被执行人行为具体情形 |
| casecode | string | 案号 |
| cardnum | string | 身份证号码/组织机构代码 |
| performance | string | 履行情况 |
| regdate | string | 立案时间 |
| duty | string | 生效法律文书确定的义务 |
| gistunit | string | 做出执行的依据单位 |
| publishdate | string | 发布时间 |
| gistid | string | 执行依据文号 |

View File

@@ -0,0 +1,81 @@
{
"result": {
"total": 2,
"datalist": [
{
"pName": "张三",
"fsource": "1",
"orgName": "****科技(杭州)有限公司",
"basicInfo": {
"dom": "浙江省杭州市余杭区************室",
"tel": "",
"base": "zj",
"city": "杭州市",
"name": "****科技(杭州)有限公司",
"opto": "9999-09-09",
"type": "1",
"email": "",
"taxid": "913**********EU3M",
"empnum": 6,
"opfrom": "2021-09-23",
"reccap": 9.8376,
"regcap": 500,
"regorg": "杭州市余杭区市场监督管理局",
"candate": "",
"opscope": "一般项目:技术服务、技术开发、技术咨询、技术交流、技术转让、技术推广;信息系统集成服务;计算机系统服务;信息咨询服务(不含许可类信息咨询服务);专业设计服务;社会经济咨询服务;信息技术咨询服务;企业管理咨询;数据处理和存储支持服务;数字内容制作服务(不含出版发行);互联网销售(除销售需要许可的商品);计算机软硬件及辅助设备批发;计算机软硬件及辅助设备零售;国内贸易代理;电子产品销售;通讯设备销售;广告发布;广告设计、代理;广告制作;工程和技术研究和试验发展;市场调查(不含涉外调查);劳务服务(不含劳务派遣);信息系统运行维护服务;大数据服务(除依法须经批准的项目外,凭营业执照依法自主开展经营活动)。",
"revdate": "",
"apprdate": "2023-07-25",
"district": "余杭区",
"industry": "软件和信息技术服务业",
"isListed": "0",
"nic_code": "I6511",
"nic_name": "信息传输、软件和信息技术服务业-软件和信息技术服务业-软件开发-应用软件开发",
"province": "浙江省",
"reccapcur": "人民币",
"regNumber": "330106*******258",
"regStatus": "存续",
"staffList": {},
"tax_level": "",
"creditCode": "913**********EU3M",
"regCapital": "500.000000万人民币",
"estiblishTime": "2021-09-23",
"his_staffList": {
"result": [
{
"name": "张三",
"type": "2",
"typeJoin": [
"监事"
],
"isLerepsign": 0
}
]
},
"industry_code": "I",
"companyOrgType": "有限责任公司(自然人独资)",
"legalPersonName": "李四",
"companyOrgTypeCode": "1151",
"regCapitalCurrency": "人民币"
},
"adminPenalty": [],
"relationship": [
"his_sh",
"his_tm"
],
"executedPerson": [],
"stockHolderItem": {},
"his_stockHolderItem": {
"acconam": "",
"confrom": "",
"currency": "人民币",
"investDate": "2023-05-15",
"investRate": "",
"subscriptAmt": "",
"orgHolderName": "张三",
"orgHolderType": "自然人"
},
"dishonestExecutedPerson": []
}
]
}
}

View File

@@ -7,7 +7,7 @@ import (
"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/zhicha" "tyapi-server/internal/infrastructure/external/nuoer"
) )
// ProcessJRZQ3C7BRequest JRZQ3C7B API处理方法 - 借贷意向验证 // ProcessJRZQ3C7BRequest JRZQ3C7B API处理方法 - 借贷意向验证
@@ -21,39 +21,34 @@ func ProcessJRZQ3C7BRequest(ctx context.Context, params []byte, deps *processors
return nil, errors.Join(processors.ErrInvalidParam, err) return nil, errors.Join(processors.ErrInvalidParam, err)
} }
encryptedName, err := deps.ZhichaService.Encrypt(paramsDto.Name) body := map[string]string{
if err != nil { "name": paramsDto.Name,
return nil, errors.Join(processors.ErrSystem, err) "idCard": paramsDto.IDCard,
"mobile": paramsDto.MobileNo,
} }
encryptedIDCard, err := deps.ZhichaService.Encrypt(paramsDto.IDCard) nuoerDoCheckAPIKey := "loanRiskTagV11"
if err != nil { ApiPath := "/v1/doCheck"
return nil, errors.Join(processors.ErrSystem, err)
}
encryptedMobileNo, err := deps.ZhichaService.Encrypt(paramsDto.MobileNo) resp, err := deps.NuoerService.CallAPI(ctx, nuoerDoCheckAPIKey, ApiPath, body)
if err != nil { if err != nil {
return nil, errors.Join(processors.ErrSystem, err) if errors.Is(err, nuoer.ErrDatasource) {
}
reqData := map[string]interface{}{
"name": encryptedName,
"idCard": encryptedIDCard,
"phone": encryptedMobileNo,
"authorized": paramsDto.Authorized,
}
respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI017", reqData)
if err != nil {
if errors.Is(err, zhicha.ErrDatasource) {
return nil, errors.Join(processors.ErrDatasource, err) return nil, errors.Join(processors.ErrDatasource, err)
} else {
return nil, errors.Join(processors.ErrSystem, err)
} }
if errors.Is(err, nuoer.ErrNotFound) {
return nil, errors.Join(processors.ErrNotFound, err)
}
return nil, errors.Join(processors.ErrSystem, err)
} }
// 将响应数据转换为JSON字节 rawData, ok := resp.Data.(map[string]interface{})
respBytes, err := json.Marshal(respData) if !ok {
return nil, errors.Join(processors.ErrSystem, errors.New("响应格式错误"))
}
result := mapNuoerApplyLoanToResponse(rawData)
respBytes, err := json.Marshal(result)
if err != nil { if err != nil {
return nil, errors.Join(processors.ErrSystem, err) return nil, errors.Join(processors.ErrSystem, err)
} }

View File

@@ -0,0 +1,38 @@
package jrzq
import "strings"
// mapNuoerApplyLoanToResponse 将 nuoer 响应2json.md转为对外结构1json.md
// 解包 result保留 Rule_* / als_* 平铺字段。
func mapNuoerApplyLoanToResponse(data map[string]interface{}) map[string]interface{} {
if data == nil {
return map[string]interface{}{}
}
return normalizeApplyLoanFlatMap(unwrapNuoerApplyLoanData(data))
}
func unwrapNuoerApplyLoanData(data map[string]interface{}) map[string]interface{} {
if result, ok := data["result"].(map[string]interface{}); ok {
return result
}
return data
}
func normalizeApplyLoanFlatMap(flat map[string]interface{}) map[string]interface{} {
result := make(map[string]interface{}, len(flat))
for key, val := range flat {
if !isApplyLoanResponseKey(key) {
continue
}
strVal := stringifyVal(val)
if strVal == "" {
continue
}
result[key] = strVal
}
return result
}
func isApplyLoanResponseKey(key string) bool {
return strings.HasPrefix(key, "Rule_") || strings.HasPrefix(key, "als_")
}

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ import (
"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/zhicha" "tyapi-server/internal/infrastructure/external/nuoer"
) )
// ProcessJRZQ5E9FRequest JRZQ5E9F API处理方法 - 借选指数 // ProcessJRZQ5E9FRequest JRZQ5E9F API处理方法 - 借选指数
@@ -21,39 +21,34 @@ func ProcessJRZQ5E9FRequest(ctx context.Context, params []byte, deps *processors
return nil, errors.Join(processors.ErrInvalidParam, err) return nil, errors.Join(processors.ErrInvalidParam, err)
} }
encryptedName, err := deps.ZhichaService.Encrypt(paramsDto.Name) body := map[string]string{
if err != nil { "name": paramsDto.Name,
return nil, errors.Join(processors.ErrSystem, err) "idCard": paramsDto.IDCard,
"mobile": paramsDto.MobileNo,
} }
encryptedIDCard, err := deps.ZhichaService.Encrypt(paramsDto.IDCard) nuoerDoCheckAPIKey := "loanRiskTagV21"
if err != nil { ApiPath := "/v1/doCheck"
return nil, errors.Join(processors.ErrSystem, err)
}
encryptedMobileNo, err := deps.ZhichaService.Encrypt(paramsDto.MobileNo) resp, err := deps.NuoerService.CallAPI(ctx, nuoerDoCheckAPIKey, ApiPath, body)
if err != nil { if err != nil {
return nil, errors.Join(processors.ErrSystem, err) if errors.Is(err, nuoer.ErrDatasource) {
}
reqData := map[string]interface{}{
"name": encryptedName,
"idCard": encryptedIDCard,
"phone": encryptedMobileNo,
"authorized": paramsDto.Authorized,
}
respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI021", reqData)
if err != nil {
if errors.Is(err, zhicha.ErrDatasource) {
return nil, errors.Join(processors.ErrDatasource, err) return nil, errors.Join(processors.ErrDatasource, err)
} else {
return nil, errors.Join(processors.ErrSystem, err)
} }
if errors.Is(err, nuoer.ErrNotFound) {
return nil, errors.Join(processors.ErrNotFound, err)
}
return nil, errors.Join(processors.ErrSystem, err)
} }
// 将响应数据转换为JSON字节 rawData, ok := resp.Data.(map[string]interface{})
respBytes, err := json.Marshal(respData) if !ok {
return nil, errors.Join(processors.ErrSystem, errors.New("响应格式错误"))
}
result := mapNuoerLoanRiskToResponse(rawData)
respBytes, err := json.Marshal(result)
if err != nil { if err != nil {
return nil, errors.Join(processors.ErrSystem, err) return nil, errors.Join(processors.ErrSystem, err)
} }

View File

@@ -0,0 +1,93 @@
package jrzq
import (
"math"
"strconv"
"strings"
)
type loanRiskInterval struct {
output string
min float64
max float64
minInclusive bool
maxInclusive bool
}
// mapNuoerLoanRiskToResponse 将 nuoer 响应2json.md转为对外结构1json.md
// 解包 result区间化字段按 1.md 映射,其余字段原样透传。
func mapNuoerLoanRiskToResponse(data map[string]interface{}) map[string]interface{} {
if data == nil {
return map[string]interface{}{}
}
return normalizeLoanRiskFlatMap(unwrapNuoerLoanRiskData(data))
}
func unwrapNuoerLoanRiskData(data map[string]interface{}) map[string]interface{} {
if result, ok := data["result"].(map[string]interface{}); ok {
return result
}
return data
}
func normalizeLoanRiskFlatMap(flat map[string]interface{}) map[string]interface{} {
result := make(map[string]interface{}, len(flat))
for key, val := range flat {
if !strings.HasPrefix(key, "xyp_") {
continue
}
raw := strings.TrimSpace(stringifyVal(val))
if raw == "" {
result[key] = "0"
continue
}
if rules, ok := loanRiskIntervalMappings[key]; ok {
result[key] = mapLoanRiskIntervalValue(raw, rules)
continue
}
result[key] = raw
}
return result
}
func mapLoanRiskIntervalValue(raw string, rules []loanRiskInterval) string {
value, ok := parseLoanRiskNumber(raw)
if !ok {
return "0"
}
for _, rule := range rules {
if loanRiskValueInInterval(value, rule) {
return rule.output
}
}
return "0"
}
func parseLoanRiskNumber(raw string) (float64, bool) {
f, err := strconv.ParseFloat(strings.TrimSpace(raw), 64)
if err != nil {
return 0, false
}
if math.IsNaN(f) || math.IsInf(f, 0) {
return 0, false
}
return f, true
}
func loanRiskValueInInterval(value float64, rule loanRiskInterval) bool {
if rule.minInclusive {
if value < rule.min {
return false
}
} else if value <= rule.min {
return false
}
if rule.max >= math.MaxFloat64/2 {
return true
}
if rule.maxInclusive {
return value <= rule.max
}
return value < rule.max
}

View File

@@ -0,0 +1,107 @@
package jrzq
import (
"encoding/json"
"testing"
)
func TestMapNuoerLoanRiskToResponse_UnwrapsResult(t *testing.T) {
raw := map[string]interface{}{
"result": map[string]interface{}{
"xyp_cpl0076": "2",
"xyp_cpl0073": "0.58",
"xyp_cpl0081": "0.6532",
},
}
got := mapNuoerLoanRiskToResponse(raw)
if got["xyp_cpl0073"] != "0.58" {
t.Fatalf("xyp_cpl0073 = %v, want 0.58", got["xyp_cpl0073"])
}
if got["xyp_cpl0081"] != "0.6532" {
t.Fatalf("xyp_cpl0081 = %v, want 0.6532", got["xyp_cpl0081"])
}
if got["xyp_cpl0076"] != "2" {
t.Fatalf("xyp_cpl0076 = %v, want 2", got["xyp_cpl0076"])
}
}
func TestMapNuoerLoanRiskToResponse_IntervalMapping(t *testing.T) {
raw := map[string]interface{}{
"xyp_cpl0001": "14",
"xyp_cpl0070": "0",
}
got := mapNuoerLoanRiskToResponse(raw)
if got["xyp_cpl0001"] != "3" {
t.Fatalf("xyp_cpl0001 = %v, want 3", got["xyp_cpl0001"])
}
if got["xyp_cpl0070"] != "0" {
t.Fatalf("xyp_cpl0070 = %v, want 0", got["xyp_cpl0070"])
}
}
func TestMapNuoerLoanRiskToResponse_PreservesEmptyFieldsAsZero(t *testing.T) {
raw := map[string]interface{}{
"result": map[string]interface{}{
"xyp_cpl0001": "",
"xyp_cpl0073": "",
"xyp_model_score_high": "-1",
"xyp_model_score_mid": "-1",
"xyp_model_score_low": "-1",
},
}
got := mapNuoerLoanRiskToResponse(raw)
if len(got) != 5 {
t.Fatalf("got %d fields, want 5: %#v", len(got), got)
}
if got["xyp_cpl0001"] != "0" {
t.Fatalf("xyp_cpl0001 = %v, want 0", got["xyp_cpl0001"])
}
if got["xyp_cpl0073"] != "0" {
t.Fatalf("xyp_cpl0073 = %v, want 0", got["xyp_cpl0073"])
}
if got["xyp_model_score_high"] != "-1" {
t.Fatalf("xyp_model_score_high = %v, want -1", got["xyp_model_score_high"])
}
}
func TestMapNuoerLoanRiskToResponse_2jsonSample(t *testing.T) {
const sample = `{
"result": {
"xyp_cpl0063": "0",
"xyp_cpl0065": "0",
"xyp_cpl0070": "0",
"xyp_cpl0071": "0",
"xyp_cpl0073": "0.58",
"xyp_cpl0074": "0.6",
"xyp_cpl0076": "2",
"xyp_cpl0081": "0.6532",
"xyp_model_score_high": "668"
}
}`
var raw map[string]interface{}
if err := json.Unmarshal([]byte(sample), &raw); err != nil {
t.Fatalf("unmarshal sample: %v", err)
}
got := mapNuoerLoanRiskToResponse(raw)
if got["xyp_cpl0073"] != "0.58" {
t.Fatalf("xyp_cpl0073 = %v, want 0.58", got["xyp_cpl0073"])
}
if got["xyp_cpl0076"] != "2" {
t.Fatalf("xyp_cpl0076 = %v, want 2", got["xyp_cpl0076"])
}
if got["xyp_model_score_high"] != "668" {
t.Fatalf("xyp_model_score_high = %v, want 668", got["xyp_model_score_high"])
}
if _, ok := got["result"]; ok {
t.Fatal("result wrapper should be removed")
}
}

View File

@@ -7,7 +7,7 @@ import (
"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/zhicha" "tyapi-server/internal/infrastructure/external/nuoer"
) )
// ProcessJRZQ8A2DRequest JRZQ8A2D API处理方法 - 特殊名单验证 // ProcessJRZQ8A2DRequest JRZQ8A2D API处理方法 - 特殊名单验证
@@ -20,40 +20,55 @@ func ProcessJRZQ8A2DRequest(ctx context.Context, params []byte, deps *processors
if err := deps.Validator.ValidateStruct(paramsDto); err != nil { if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
return nil, errors.Join(processors.ErrInvalidParam, err) return nil, errors.Join(processors.ErrInvalidParam, err)
} }
encryptedName, err := deps.ZhichaService.Encrypt(paramsDto.Name)
if err != nil { body := map[string]string{
return nil, errors.Join(processors.ErrSystem, err) "name": paramsDto.Name,
} "idCard": paramsDto.IDCard,
encryptedIDCard, err := deps.ZhichaService.Encrypt(paramsDto.IDCard) "mobile": paramsDto.MobileNo,
if err != nil {
return nil, errors.Join(processors.ErrSystem, err)
}
encryptedMobileNo, err := deps.ZhichaService.Encrypt(paramsDto.MobileNo)
if err != nil {
return nil, errors.Join(processors.ErrSystem, err)
} }
reqData := map[string]interface{}{ nuoerDoCheckAPIKey := "loanRiskTagV12"
"name": encryptedName, ApiPath := "/v1/doCheck"
"idCard": encryptedIDCard,
"phone": encryptedMobileNo,
"authorized": paramsDto.Authorized,
}
respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI018", reqData) resp, err := deps.NuoerService.CallAPI(ctx, nuoerDoCheckAPIKey, ApiPath, body)
if err != nil { if err != nil {
if errors.Is(err, zhicha.ErrDatasource) { if errors.Is(err, nuoer.ErrDatasource) {
return nil, errors.Join(processors.ErrDatasource, err) return nil, errors.Join(processors.ErrDatasource, err)
} else {
return nil, errors.Join(processors.ErrSystem, err)
} }
if errors.Is(err, nuoer.ErrNotFound) {
return nil, errors.Join(processors.ErrNotFound, err)
}
return nil, errors.Join(processors.ErrSystem, err)
} }
// 将响应数据转换为JSON字节 rawData, ok := resp.Data.(map[string]interface{})
respBytes, err := json.Marshal(respData) if !ok {
return nil, errors.Join(processors.ErrSystem, errors.New("响应格式错误"))
}
result := mapNuoerSpecialListToResponse(rawData)
respBytes, err := json.Marshal(result)
if err != nil { if err != nil {
return nil, errors.Join(processors.ErrSystem, err) return nil, errors.Join(processors.ErrSystem, err)
} }
return respBytes, nil return respBytes, nil
// respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI018", reqData)
// if err != nil {
// if errors.Is(err, zhicha.ErrDatasource) {
// return nil, errors.Join(processors.ErrDatasource, 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
} }

View File

@@ -0,0 +1,102 @@
package jrzq
import (
"fmt"
"strconv"
)
// mapNuoerSpecialListToResponse 将 nuoer 响应2.md转为对外结构1.md
// SpecialList_c -> id/cellRule -> Rule_final_* / Rule_name_odr* / Rule_weight_odr*
func mapNuoerSpecialListToResponse(data map[string]interface{}) map[string]interface{} {
if data == nil {
return map[string]interface{}{
"id": map[string]interface{}{},
"cell": map[string]interface{}{},
}
}
if _, hasID := data["id"]; hasID {
if _, hasSpecialList := data["SpecialList_c"]; !hasSpecialList {
return data
}
}
payload := unwrapNuoerSpecialListData(data)
result := make(map[string]interface{})
specialList := asMap(payload["SpecialList_c"])
result["id"] = normalizeStringMap(specialList["id"])
result["cell"] = normalizeStringMap(specialList["cell"])
rule := asMap(payload["Rule"])
ruleResult := asMap(rule["result"])
if v := stringifyVal(ruleResult["final_decision"]); v != "" {
result["Rule_final_decision"] = v
}
if v := stringifyVal(ruleResult["final_weight"]); v != "" {
result["Rule_final_weight"] = v
}
hitRules := asMap(rule["hit_rules"])
ruleSpecialList := asMap(hitRules["rulespeciallist_c"])
for odrID, raw := range ruleSpecialList {
odrRule := asMap(raw)
if name := stringifyVal(odrRule["name_rule"]); name != "" {
result["Rule_name_"+odrID] = name
}
if weight := stringifyVal(odrRule["weight"]); weight != "" {
result["Rule_weight_"+odrID] = weight
}
}
return result
}
func unwrapNuoerSpecialListData(data map[string]interface{}) map[string]interface{} {
if _, ok := data["SpecialList_c"]; ok {
return data
}
if result, ok := data["result"].(map[string]interface{}); ok {
if _, ok := result["SpecialList_c"]; ok {
return result
}
}
return data
}
func normalizeStringMap(v interface{}) map[string]interface{} {
src := asMap(v)
if len(src) == 0 {
return map[string]interface{}{}
}
out := make(map[string]interface{}, len(src))
for k, val := range src {
out[k] = stringifyVal(val)
}
return out
}
func stringifyVal(v interface{}) string {
if v == nil {
return ""
}
switch val := v.(type) {
case string:
return val
case float64:
if val == float64(int64(val)) {
return strconv.FormatInt(int64(val), 10)
}
return strconv.FormatFloat(val, 'f', -1, 64)
case int:
return strconv.Itoa(val)
case int32:
return strconv.FormatInt(int64(val), 10)
case int64:
return strconv.FormatInt(val, 10)
case bool:
return strconv.FormatBool(val)
default:
return fmt.Sprint(val)
}
}

View File

@@ -7,7 +7,7 @@ import (
"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/westdex" "tyapi-server/internal/infrastructure/external/nuoer"
) )
// ProcessQYGL6F2DRequest QYGL6F2D API处理方法 // ProcessQYGL6F2DRequest QYGL6F2D API处理方法
@@ -20,26 +20,35 @@ func ProcessQYGL6F2DRequest(ctx context.Context, params []byte, deps *processors
if err := deps.Validator.ValidateStruct(paramsDto); err != nil { if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
return nil, errors.Join(processors.ErrInvalidParam, err) return nil, errors.Join(processors.ErrInvalidParam, err)
} }
body := map[string]string{
"idCard": paramsDto.IDCard,
}
encryptedIDCard, err := deps.WestDexService.Encrypt(paramsDto.IDCard) nuoerDoCheckAPIKey := "idRelationV101"
ApiPath := "/v1/doCheck"
resp, err := deps.NuoerService.CallAPI(ctx, nuoerDoCheckAPIKey, ApiPath, body)
if err != nil {
if errors.Is(err, nuoer.ErrDatasource) {
return nil, errors.Join(processors.ErrDatasource, err)
}
if errors.Is(err, nuoer.ErrNotFound) {
return nil, errors.Join(processors.ErrNotFound, err)
}
return nil, errors.Join(processors.ErrSystem, err)
}
rawData, ok := resp.Data.(map[string]interface{})
if !ok {
return nil, errors.Join(processors.ErrSystem, errors.New("响应格式错误"))
}
result := mapNuoerIdRelationToResponse(rawData)
respBytes, err := json.Marshal(result)
if err != nil { if err != nil {
return nil, errors.Join(processors.ErrSystem, err) return nil, errors.Join(processors.ErrSystem, err)
} }
reqData := map[string]interface{}{
"data": map[string]interface{}{
"idno": encryptedIDCard,
},
}
respBytes, err := deps.WestDexService.CallAPI(ctx, "G05XM02", reqData)
if err != nil {
if errors.Is(err, westdex.ErrDatasource) {
return nil, errors.Join(processors.ErrDatasource, err)
} else {
return nil, errors.Join(processors.ErrSystem, err)
}
}
return respBytes, nil return respBytes, nil
} }

View File

@@ -0,0 +1,150 @@
package qygl
var qygl6f2dBasicInfoFields = []string{
"regStatus", "estiblishTime", "regCapital", "industry", "type",
"regCapitalCurrency", "legalPersonName", "regNumber", "creditCode", "name",
"companyOrgType", "base", "revdate", "apprdate", "candate", "reccap",
"reccapcur", "province", "city", "district", "regorg", "opscope",
"nic_code", "nic_name", "industry_code", "his_staffList", "tel",
}
var qygl6f2dStockHolderFields = []string{
"orgHolderType", "investDate", "investRate", "subscriptAmt", "orgHolderName",
}
var qygl6f2dAdminPenaltyFields = []string{
"departmentName", "reason", "punishNumber", "type", "content",
"decisionDate", "legalPersonName",
}
var qygl6f2dExecutedPersonFields = []string{
"caseCode", "pname", "caseCreateTime", "execCourtName", "execMoney",
}
var qygl6f2dDishonestExecutedPersonFields = []string{
"businessentity", "areaname", "courname", "unperformPart", "type",
"performedPart", "iname", "disrupttypename", "casecode", "performance",
"regdate", "duty", "gistunit", "publishdate", "gistid",
}
// mapNuoerIdRelationToResponse 将 nuoer 响应2json.md转为对外结构1json.md
// 解包 result去掉 total/fsource并按 1.md 裁剪字段。
func mapNuoerIdRelationToResponse(data map[string]interface{}) map[string]interface{} {
if data == nil {
return map[string]interface{}{"datalist": []interface{}{}}
}
payload := unwrapNuoerIdRelationData(data)
rawList := asSlice(payload["datalist"])
datalist := make([]interface{}, 0, len(rawList))
for _, item := range rawList {
if mapped := mapNuoerIdRelationItem(asMap(item)); mapped != nil {
datalist = append(datalist, mapped)
}
}
return map[string]interface{}{"datalist": datalist}
}
func unwrapNuoerIdRelationData(data map[string]interface{}) map[string]interface{} {
if _, ok := data["datalist"]; ok {
return data
}
if result, ok := data["result"].(map[string]interface{}); ok {
return result
}
return data
}
func mapNuoerIdRelationItem(item map[string]interface{}) map[string]interface{} {
if len(item) == 0 {
return nil
}
out := make(map[string]interface{}, 8)
for _, key := range []string{"orgName", "pName", "relationship"} {
if val, ok := item[key]; ok && val != nil {
out[key] = val
}
}
if basicInfo := pickFields(asMap(item["basicInfo"]), qygl6f2dBasicInfoFields); isNonemptyMap(basicInfo) {
out["basicInfo"] = basicInfo
}
if stockHolder := pickFields(asMap(item["stockHolderItem"]), qygl6f2dStockHolderFields); isNonemptyMap(stockHolder) {
out["stockHolderItem"] = stockHolder
}
if hisStockHolder := pickFields(asMap(item["his_stockHolderItem"]), qygl6f2dStockHolderFields); isNonemptyMap(hisStockHolder) {
out["his_stockHolderItem"] = hisStockHolder
}
if adminPenalty := mapNuoerIdRelationRecords(item["adminPenalty"], qygl6f2dAdminPenaltyFields); len(adminPenalty) > 0 {
out["adminPenalty"] = adminPenalty
}
if executedPerson := mapNuoerIdRelationRecords(item["executedPerson"], qygl6f2dExecutedPersonFields); len(executedPerson) > 0 {
out["executedPerson"] = executedPerson
}
if dishonestExecutedPerson := mapNuoerIdRelationRecords(item["dishonestExecutedPerson"], qygl6f2dDishonestExecutedPersonFields); len(dishonestExecutedPerson) > 0 {
out["dishonestExecutedPerson"] = dishonestExecutedPerson
}
return out
}
func mapNuoerIdRelationRecords(v interface{}, allowlist []string) []interface{} {
list := asSlice(v)
if len(list) == 0 {
return nil
}
out := make([]interface{}, 0, len(list))
for _, item := range list {
if mapped := pickFields(asMap(item), allowlist); isNonemptyMap(mapped) {
out = append(out, mapped)
}
}
return out
}
func pickFields(src map[string]interface{}, allowlist []string) map[string]interface{} {
if len(src) == 0 {
return nil
}
out := make(map[string]interface{}, len(allowlist))
for _, key := range allowlist {
val, ok := src[key]
if !ok || val == nil {
continue
}
out[key] = val
}
return out
}
func isNonemptyMap(m map[string]interface{}) bool {
return len(m) > 0
}
func asMap(v interface{}) map[string]interface{} {
if v == nil {
return nil
}
m, ok := v.(map[string]interface{})
if !ok {
return nil
}
return m
}
func asSlice(v interface{}) []interface{} {
if v == nil {
return nil
}
switch val := v.(type) {
case []interface{}:
return val
default:
return nil
}
}

View File

@@ -156,7 +156,7 @@ func (s *NuoerService) CallAPI(ctx context.Context, apiKey, apiPath string, body
nuoerErr := NewNuoerError(nuoerResp.Code, nuoerResp.Msg) nuoerErr := NewNuoerError(nuoerResp.Code, nuoerResp.Msg)
err = errors.Join(GetErrByPlatformCode(nuoerResp.Code), nuoerErr) err = errors.Join(GetErrByPlatformCode(nuoerResp.Code), nuoerErr)
if s.logger != nil { if s.logger != nil {
s.logger.LogError(requestID, transactionID, apiKey, nuoerErr, requestPayload) s.logger.LogErrorWithResponseID(requestID, transactionID, apiKey, nuoerErr, requestPayload, nuoerResp.SeqNo)
} }
return nil, err return nil, err
} }
@@ -164,7 +164,7 @@ func (s *NuoerService) CallAPI(ctx context.Context, apiKey, apiPath string, body
if nuoerResp.Data == nil { if nuoerResp.Data == nil {
err = errors.Join(ErrSystem, errors.New("响应 data 为空")) err = errors.Join(ErrSystem, errors.New("响应 data 为空"))
if s.logger != nil { if s.logger != nil {
s.logger.LogError(requestID, transactionID, apiKey, err, requestPayload) s.logger.LogErrorWithResponseID(requestID, transactionID, apiKey, err, requestPayload, nuoerResp.SeqNo)
} }
return nil, err return nil, err
} }
@@ -173,7 +173,7 @@ func (s *NuoerService) CallAPI(ctx context.Context, apiKey, apiPath string, body
if !ok { if !ok {
err = errors.Join(ErrSystem, errors.New("响应 data 无法解析 busiCode")) err = errors.Join(ErrSystem, errors.New("响应 data 无法解析 busiCode"))
if s.logger != nil { if s.logger != nil {
s.logger.LogError(requestID, transactionID, apiKey, err, requestPayload) s.logger.LogErrorWithResponseID(requestID, transactionID, apiKey, err, requestPayload, nuoerResp.SeqNo)
} }
return nil, err return nil, err
} }
@@ -182,7 +182,7 @@ func (s *NuoerService) CallAPI(ctx context.Context, apiKey, apiPath string, body
busiErr := NewNuoerBusiError(busiCode, busiMsg) busiErr := NewNuoerBusiError(busiCode, busiMsg)
err = errors.Join(GetErrByBusiCode(busiCode), busiErr) err = errors.Join(GetErrByBusiCode(busiCode), busiErr)
if s.logger != nil { if s.logger != nil {
s.logger.LogError(requestID, transactionID, apiKey, busiErr, requestPayload) s.logger.LogErrorWithResponseID(requestID, transactionID, apiKey, busiErr, requestPayload, nuoerResp.SeqNo)
} }
return nil, err return nil, err
} }
@@ -191,7 +191,7 @@ func (s *NuoerService) CallAPI(ctx context.Context, apiKey, apiPath string, body
if err != nil { if err != nil {
err = errors.Join(ErrSystem, fmt.Errorf("响应 data 清理失败: %w", err)) err = errors.Join(ErrSystem, fmt.Errorf("响应 data 清理失败: %w", err))
if s.logger != nil { if s.logger != nil {
s.logger.LogError(requestID, transactionID, apiKey, err, requestPayload) s.logger.LogErrorWithResponseID(requestID, transactionID, apiKey, err, requestPayload, nuoerResp.SeqNo)
} }
return nil, err return nil, err
} }