Compare commits
51 Commits
da0990e015
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| e246271a24 | |||
| a1024ed4b2 | |||
| d6b78a5d6d | |||
| 61c6cc4f35 | |||
| cdd1e00745 | |||
| 46ba4e048c | |||
| 3156539319 | |||
| dad8abad16 | |||
| 5f62261c11 | |||
| a0b2105339 | |||
| 83e71ae81b | |||
| 8675961207 | |||
| 4bd6f51728 | |||
| cd1db5276a | |||
| 2f653be375 | |||
| 9c3fb97b3f | |||
| b6053983d9 | |||
| c3b16c0ffe | |||
| 5f6cca5369 | |||
| a01226c7c0 | |||
| e67465a58d | |||
| 75316b10cb | |||
| ebcf3be923 | |||
| cff3fb8814 | |||
| e76fcd89bb | |||
| 10605afe1e | |||
| d3554e8b44 | |||
| 35a2eb03d8 | |||
| 2a6ec6e3ca | |||
| 44d8b3d28c | |||
| cad1d354f5 | |||
| 711dc83e47 | |||
| 65fdc9bf21 | |||
| e9fe7ac303 | |||
| 130f49fb9d | |||
| d66ef0b15f | |||
| a6a2d8d9c5 | |||
| e095553ba8 | |||
| a73097aed3 | |||
| 8bbd098f97 | |||
| 5f0224ad3b | |||
| 9438ccee5e | |||
| 8771261118 | |||
| 96c5870aa0 | |||
| 5e658f2527 | |||
| e03e6b983c | |||
| 6ab9bb21e7 | |||
| 7c4bcefc81 | |||
| 8eec9685db | |||
| 6a801acee1 | |||
| 6120020a7c |
@@ -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. 验证钱包状态
|
||||
@@ -319,15 +328,28 @@ func (s *ApiApplicationServiceImpl) callExternalApi(ctx context.Context, cmd *co
|
||||
callContext)
|
||||
|
||||
if err != nil {
|
||||
mappedErrorType := entities.ApiCallErrorSystem
|
||||
if errors.Is(err, processors.ErrDatasource) {
|
||||
return "", ErrSystem
|
||||
mappedErrorType = entities.ApiCallErrorDatasource
|
||||
} else if errors.Is(err, processors.ErrInvalidParam) {
|
||||
return "", ErrInvalidParam
|
||||
mappedErrorType = entities.ApiCallErrorInvalidParam
|
||||
} else if errors.Is(err, processors.ErrNotFound) {
|
||||
return "", ErrQueryEmpty
|
||||
} else {
|
||||
return "", ErrSystem
|
||||
mappedErrorType = entities.ApiCallErrorQueryEmpty
|
||||
}
|
||||
|
||||
s.logger.Error("调用第三方接口失败",
|
||||
zap.String("transaction_id", validation.ApiCall.TransactionId),
|
||||
zap.String("api_name", cmd.ApiName),
|
||||
zap.String("error_type", mappedErrorType),
|
||||
zap.Error(err))
|
||||
|
||||
if mappedErrorType == entities.ApiCallErrorInvalidParam {
|
||||
return "", ErrInvalidParam
|
||||
}
|
||||
if mappedErrorType == entities.ApiCallErrorQueryEmpty {
|
||||
return "", ErrQueryEmpty
|
||||
}
|
||||
return "", ErrSystem
|
||||
}
|
||||
|
||||
return string(response), nil
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"tyapi-server/internal/application/certification/dto/commands"
|
||||
@@ -107,17 +108,54 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(
|
||||
cmd *commands.SubmitEnterpriseInfoCommand,
|
||||
) (*responses.CertificationResponse, error) {
|
||||
s.logger.Info("开始提交企业信息",
|
||||
zap.String("user_id", cmd.UserID))
|
||||
zap.String("user_id", cmd.UserID),
|
||||
zap.String("company_name", cmd.CompanyName),
|
||||
zap.String("unified_social_code", cmd.UnifiedSocialCode))
|
||||
|
||||
// 0. 若该用户已有待审核(认证状态仍在待审核),则不允许重复提交
|
||||
latestRecord, err := s.enterpriseInfoSubmitRecordRepo.FindLatestByUserID(ctx, cmd.UserID)
|
||||
if err == nil && latestRecord != nil {
|
||||
s.logger.Info("步骤0-检测到历史提交记录",
|
||||
zap.String("user_id", cmd.UserID),
|
||||
zap.String("latest_record_id", latestRecord.ID))
|
||||
cert, loadErr := s.aggregateService.LoadCertificationByUserID(ctx, cmd.UserID)
|
||||
if loadErr == nil && cert != nil && cert.Status == enums.StatusInfoPendingReview {
|
||||
s.logger.Warn("步骤0-存在待审核记录,拒绝重复提交",
|
||||
zap.String("user_id", cmd.UserID),
|
||||
zap.String("cert_status", string(cert.Status)))
|
||||
return nil, fmt.Errorf("您已有待审核的提交,请等待管理员审核后再操作")
|
||||
}
|
||||
}
|
||||
|
||||
// 0.5 已通过人工审核或已进入后续流程:幂等返回当前认证数据(不调 e签宝、不新建提交记录)
|
||||
existsCertEarly, err := s.aggregateService.ExistsByUserID(ctx, cmd.UserID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("检查认证记录失败: %w", err)
|
||||
}
|
||||
if existsCertEarly {
|
||||
certEarly, loadErr := s.aggregateService.LoadCertificationByUserID(ctx, cmd.UserID)
|
||||
if loadErr != nil {
|
||||
return nil, fmt.Errorf("加载认证信息失败: %w", loadErr)
|
||||
}
|
||||
switch certEarly.Status {
|
||||
case enums.StatusInfoSubmitted, enums.StatusEnterpriseVerified, enums.StatusContractApplied,
|
||||
enums.StatusContractSigned, enums.StatusCompleted, enums.StatusContractRejected, enums.StatusContractExpired:
|
||||
meta, metaErr := s.AddStatusMetadata(ctx, certEarly)
|
||||
if metaErr != nil {
|
||||
return nil, metaErr
|
||||
}
|
||||
resp := s.convertToResponse(certEarly)
|
||||
if meta != nil {
|
||||
resp.Metadata = meta
|
||||
} else {
|
||||
resp.Metadata = map[string]interface{}{}
|
||||
}
|
||||
resp.Metadata["next_action"] = enums.GetUserActionHint(certEarly.Status)
|
||||
s.logger.Info("企业信息提交幂等返回", zap.String("user_id", cmd.UserID), zap.String("status", string(certEarly.Status)))
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
|
||||
// 1.5 插入企业信息提交记录(包含扩展字段)
|
||||
record := entities.NewEnterpriseInfoSubmitRecord(
|
||||
cmd.UserID,
|
||||
@@ -162,7 +200,11 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(
|
||||
// 验证验证码
|
||||
// 特殊验证码"768005"直接跳过验证环节
|
||||
if cmd.VerificationCode != "768005" {
|
||||
s.logger.Info("步骤1-开始验证短信验证码", zap.String("user_id", cmd.UserID))
|
||||
if err := s.smsCodeService.VerifyCode(ctx, cmd.LegalPersonPhone, cmd.VerificationCode, user_entities.SMSSceneCertification); err != nil {
|
||||
s.logger.Warn("步骤1-短信验证码校验失败",
|
||||
zap.String("user_id", cmd.UserID),
|
||||
zap.Error(err))
|
||||
record.MarkAsFailed(err.Error())
|
||||
saveErr := s.enterpriseInfoSubmitRecordService.Save(ctx, record)
|
||||
if saveErr != nil {
|
||||
@@ -170,6 +212,9 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(
|
||||
}
|
||||
return nil, fmt.Errorf("验证码错误或已过期")
|
||||
}
|
||||
s.logger.Info("步骤1-短信验证码校验通过", zap.String("user_id", cmd.UserID))
|
||||
} else {
|
||||
s.logger.Info("步骤1-命中特殊验证码,跳过校验", zap.String("user_id", cmd.UserID))
|
||||
}
|
||||
s.logger.Info("开始处理企业信息提交",
|
||||
zap.String("user_id", cmd.UserID))
|
||||
@@ -177,6 +222,10 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(
|
||||
// 1.1 已写入用户域 enterprise_infos 的(已完成认证)
|
||||
exists, err := s.userAggregateService.CheckUnifiedSocialCodeExists(ctx, cmd.UnifiedSocialCode, cmd.UserID)
|
||||
if err != nil {
|
||||
s.logger.Error("步骤2.1-检查用户域统一社会信用代码失败",
|
||||
zap.String("user_id", cmd.UserID),
|
||||
zap.String("unified_social_code", cmd.UnifiedSocialCode),
|
||||
zap.Error(err))
|
||||
record.MarkAsFailed(err.Error())
|
||||
saveErr := s.enterpriseInfoSubmitRecordService.Save(ctx, record)
|
||||
if saveErr != nil {
|
||||
@@ -185,6 +234,9 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(
|
||||
return nil, fmt.Errorf("检查企业信息失败: %s", err.Error())
|
||||
}
|
||||
if exists {
|
||||
s.logger.Warn("步骤2.1-统一社会信用代码已被占用(用户域)",
|
||||
zap.String("user_id", cmd.UserID),
|
||||
zap.String("unified_social_code", cmd.UnifiedSocialCode))
|
||||
record.MarkAsFailed("该企业信息已被其他用户使用,请确认企业信息是否正确")
|
||||
saveErr := s.enterpriseInfoSubmitRecordService.Save(ctx, record)
|
||||
if saveErr != nil {
|
||||
@@ -195,6 +247,10 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(
|
||||
// 1.2 已提交/已通过验证的提交记录(尚未完成认证但已占用的信用代码)
|
||||
existsInSubmit, err := s.enterpriseInfoSubmitRecordRepo.ExistsByUnifiedSocialCodeExcludeUser(ctx, cmd.UnifiedSocialCode, cmd.UserID)
|
||||
if err != nil {
|
||||
s.logger.Error("步骤2.2-检查提交记录统一社会信用代码失败",
|
||||
zap.String("user_id", cmd.UserID),
|
||||
zap.String("unified_social_code", cmd.UnifiedSocialCode),
|
||||
zap.Error(err))
|
||||
record.MarkAsFailed(err.Error())
|
||||
saveErr := s.enterpriseInfoSubmitRecordService.Save(ctx, record)
|
||||
if saveErr != nil {
|
||||
@@ -203,6 +259,9 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(
|
||||
return nil, fmt.Errorf("检查企业信息失败: %s", err.Error())
|
||||
}
|
||||
if existsInSubmit {
|
||||
s.logger.Warn("步骤2.2-统一社会信用代码已被占用(提交记录)",
|
||||
zap.String("user_id", cmd.UserID),
|
||||
zap.String("unified_social_code", cmd.UnifiedSocialCode))
|
||||
record.MarkAsFailed("该企业信息已被其他用户使用,请确认企业信息是否正确")
|
||||
saveErr := s.enterpriseInfoSubmitRecordService.Save(ctx, record)
|
||||
if saveErr != nil {
|
||||
@@ -229,6 +288,9 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(
|
||||
}
|
||||
return nil, fmt.Errorf("企业信息验证失败: %s", err.Error())
|
||||
}
|
||||
s.logger.Info("步骤3-企业信息基础校验通过",
|
||||
zap.String("user_id", cmd.UserID),
|
||||
zap.String("company_name", enterpriseInfo.CompanyName))
|
||||
err = s.enterpriseInfoSubmitRecordService.ValidateWithWestdex(ctx, enterpriseInfo)
|
||||
if err != nil {
|
||||
s.logger.Error("企业信息验证失败", zap.Error(err))
|
||||
@@ -239,10 +301,14 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(
|
||||
}
|
||||
return nil, fmt.Errorf("企业信息验证失败, %s", err.Error())
|
||||
}
|
||||
s.logger.Info("步骤4-企业信息三方校验通过",
|
||||
zap.String("user_id", cmd.UserID),
|
||||
zap.String("company_name", enterpriseInfo.CompanyName))
|
||||
record.MarkAsVerified()
|
||||
|
||||
var response *responses.CertificationResponse
|
||||
err = s.txManager.ExecuteInTx(ctx, func(txCtx context.Context) error {
|
||||
s.logger.Info("步骤5-开始事务处理认证提交流程", zap.String("user_id", cmd.UserID))
|
||||
// 2. 检查用户认证是否存在
|
||||
existsCert, err := s.aggregateService.ExistsByUserID(txCtx, cmd.UserID)
|
||||
if err != nil {
|
||||
@@ -250,10 +316,12 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(
|
||||
}
|
||||
if !existsCert {
|
||||
// 创建
|
||||
s.logger.Info("步骤5.1-认证记录不存在,开始创建", zap.String("user_id", cmd.UserID))
|
||||
_, err := s.aggregateService.CreateCertification(txCtx, cmd.UserID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("创建认证信息失败: %s", err.Error())
|
||||
}
|
||||
s.logger.Info("步骤5.1-认证记录创建成功", zap.String("user_id", cmd.UserID))
|
||||
}
|
||||
|
||||
// 3. 加载认证聚合根
|
||||
@@ -262,30 +330,11 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(
|
||||
return fmt.Errorf("加载认证信息失败: %s", err.Error())
|
||||
}
|
||||
|
||||
// 4. 提交企业信息:暂时跳过人工审核,直接进入「已提交」状态(第三步企业认证)
|
||||
// 恢复人工审核时改为 cert.SubmitEnterpriseInfoForReview(enterpriseInfo),并将 next_action 改为「请等待管理员审核企业信息」
|
||||
|
||||
// 生成企业认证链接
|
||||
authURL, err := s.esignClient.GenerateEnterpriseAuth(&esign.EnterpriseAuthRequest{
|
||||
CompanyName: enterpriseInfo.CompanyName,
|
||||
UnifiedSocialCode: enterpriseInfo.UnifiedSocialCode,
|
||||
LegalPersonName: enterpriseInfo.LegalPersonName,
|
||||
LegalPersonID: enterpriseInfo.LegalPersonID,
|
||||
TransactorName: enterpriseInfo.LegalPersonName,
|
||||
TransactorMobile: enterpriseInfo.LegalPersonPhone,
|
||||
TransactorID: enterpriseInfo.LegalPersonID,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("生成企业认证链接失败: %w", err)
|
||||
}
|
||||
|
||||
err = cert.SubmitEnterpriseInfo(enterpriseInfo, authURL.AuthShortURL, authURL.AuthFlowID)
|
||||
if err != nil {
|
||||
// 4. 提交企业信息:进入人工审核(三真/企业信息审核);e签宝链接仅在管理员审核通过后生成(见 AdminApproveSubmitRecord)
|
||||
if err := cert.SubmitEnterpriseInfoForReview(enterpriseInfo); err != nil {
|
||||
return fmt.Errorf("提交企业信息失败: %s", err.Error())
|
||||
}
|
||||
|
||||
err = s.aggregateService.SaveCertification(txCtx, cert)
|
||||
if err != nil {
|
||||
if err := s.aggregateService.SaveCertification(txCtx, cert); err != nil {
|
||||
return fmt.Errorf("保存认证信息失败: %s", err.Error())
|
||||
}
|
||||
|
||||
@@ -293,29 +342,62 @@ func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(
|
||||
if saveErr := s.enterpriseInfoSubmitRecordService.Save(txCtx, record); saveErr != nil {
|
||||
return fmt.Errorf("保存企业信息提交记录失败: %s", saveErr.Error())
|
||||
}
|
||||
s.logger.Info("步骤5.3-企业信息提交记录保存成功",
|
||||
zap.String("user_id", cmd.UserID),
|
||||
zap.String("record_id", record.ID))
|
||||
|
||||
var enterpriseInfoMeta map[string]interface{}
|
||||
if raw, mErr := json.Marshal(enterpriseInfo); mErr == nil {
|
||||
_ = json.Unmarshal(raw, &enterpriseInfoMeta)
|
||||
}
|
||||
if enterpriseInfoMeta == nil {
|
||||
enterpriseInfoMeta = map[string]interface{}{}
|
||||
}
|
||||
enterpriseInfoMeta["submit_at"] = record.SubmitAt.Format(time.RFC3339)
|
||||
|
||||
respMeta := map[string]interface{}{
|
||||
"enterprise_info": enterpriseInfo,
|
||||
"next_action": "请完成企业认证",
|
||||
"enterprise_info": enterpriseInfoMeta,
|
||||
"polling": map[string]interface{}{
|
||||
"enabled": false,
|
||||
"endpoint": "/api/v1/certifications/confirm-auth",
|
||||
"interval_seconds": 3,
|
||||
},
|
||||
"next_action": "请等待管理员审核企业信息",
|
||||
"target_view": "manual_review",
|
||||
}
|
||||
// 6. 转换为响应 DTO
|
||||
response = s.convertToResponse(cert)
|
||||
if respMeta != nil {
|
||||
response.Metadata = respMeta
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 提醒管理员处理待审核申请(配置企业微信 Webhook 时生效)
|
||||
if s.wechatWorkService != nil {
|
||||
contactPhone := cmd.LegalPersonPhone
|
||||
if strings.TrimSpace(cmd.AuthorizedRepPhone) != "" {
|
||||
contactPhone = fmt.Sprintf("法人 %s;授权代表 %s", cmd.LegalPersonPhone, cmd.AuthorizedRepPhone)
|
||||
} else {
|
||||
contactPhone = fmt.Sprintf("%s(法人)", cmd.LegalPersonPhone)
|
||||
}
|
||||
_ = s.wechatWorkService.SendCertificationNotification(ctx, "pending_manual_review", map[string]interface{}{
|
||||
"company_name": cmd.CompanyName,
|
||||
"legal_person_name": cmd.LegalPersonName,
|
||||
"authorized_rep_name": cmd.AuthorizedRepName,
|
||||
"contact_phone": contactPhone,
|
||||
"api_usage": cmd.APIUsage,
|
||||
"submit_at": record.SubmitAt.Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
}
|
||||
|
||||
s.logger.Info("企业信息提交成功", zap.String("user_id", cmd.UserID))
|
||||
return response, nil
|
||||
}
|
||||
|
||||
// 审核状态检查(步骤二)
|
||||
// 规则:企业信息提交成功后进入待审核;审核通过后才允许进行企业认证确认(ConfirmAuth)。
|
||||
// 当前暂时跳过人工审核(待审核状态视为通过);启用审核时恢复对 StatusInfoPendingReview 返回错误。
|
||||
func (s *CertificationApplicationServiceImpl) checkAuditStatus(ctx context.Context, cert *entities.Certification) error {
|
||||
switch cert.Status {
|
||||
case enums.StatusInfoSubmitted,
|
||||
@@ -325,9 +407,7 @@ func (s *CertificationApplicationServiceImpl) checkAuditStatus(ctx context.Conte
|
||||
enums.StatusCompleted:
|
||||
return nil
|
||||
case enums.StatusInfoPendingReview:
|
||||
// 暂时跳过人工审核:待审核状态视为通过,后续启用审核时还原为 return fmt.Errorf("企业信息已提交,正在审核中")
|
||||
s.logger.Info("跳过人工审核状态检查", zap.String("user_id", cert.UserID))
|
||||
return nil
|
||||
return fmt.Errorf("企业信息已提交,正在审核中")
|
||||
case enums.StatusInfoRejected:
|
||||
return fmt.Errorf("企业信息审核未通过")
|
||||
default:
|
||||
@@ -347,13 +427,23 @@ func (s *CertificationApplicationServiceImpl) ConfirmAuth(
|
||||
}
|
||||
|
||||
// 步骤二:审核状态检查(审核通过后才能进入企业认证确认)
|
||||
s.logger.Info("确认状态-步骤1-开始审核状态检查", zap.String("user_id", cmd.UserID))
|
||||
if err := s.checkAuditStatus(ctx, cert); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
s.logger.Info("确认状态-步骤1-审核状态检查通过",
|
||||
zap.String("user_id", cmd.UserID),
|
||||
zap.String("cert_status", string(cert.Status)))
|
||||
record, err := s.enterpriseInfoSubmitRecordRepo.FindLatestByUserID(ctx, cert.UserID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查找企业信息失败: %w", err)
|
||||
}
|
||||
s.logger.Info("确认状态-步骤2-获取最近提交记录成功",
|
||||
zap.String("user_id", cmd.UserID),
|
||||
zap.String("record_id", record.ID))
|
||||
s.logger.Info("确认状态-步骤3-开始查询三方实名状态",
|
||||
zap.String("user_id", cmd.UserID),
|
||||
zap.String("company_name", record.CompanyName))
|
||||
identity, err := s.esignClient.QueryOrgIdentityInfo(&esign.QueryOrgIdentityRequest{
|
||||
OrgName: record.CompanyName,
|
||||
})
|
||||
@@ -363,6 +453,8 @@ func (s *CertificationApplicationServiceImpl) ConfirmAuth(
|
||||
}
|
||||
reason := ""
|
||||
if identity != nil && identity.Data.RealnameStatus == 1 {
|
||||
s.logger.Info("确认状态-步骤3-三方实名状态已完成,准备事务内推进认证",
|
||||
zap.String("user_id", cmd.UserID))
|
||||
err = s.txManager.ExecuteInTx(ctx, func(txCtx context.Context) error {
|
||||
err = s.completeEnterpriseVerification(txCtx, cert, cert.UserID, record.CompanyName, record.LegalPersonName)
|
||||
if err != nil {
|
||||
@@ -374,8 +466,13 @@ func (s *CertificationApplicationServiceImpl) ConfirmAuth(
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("完成企业认证失败: %w", err)
|
||||
}
|
||||
s.logger.Info("确认状态-步骤4-认证状态推进完成",
|
||||
zap.String("user_id", cmd.UserID),
|
||||
zap.String("cert_status", string(cert.Status)))
|
||||
} else {
|
||||
reason = "企业未完成"
|
||||
s.logger.Info("确认状态-步骤3-三方实名状态未完成",
|
||||
zap.String("user_id", cmd.UserID))
|
||||
}
|
||||
return &responses.ConfirmAuthResponse{
|
||||
Status: cert.Status,
|
||||
@@ -847,6 +944,9 @@ func (s *CertificationApplicationServiceImpl) AdminApproveSubmitRecord(ctx conte
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取提交记录失败: %w", err)
|
||||
}
|
||||
if record.Status != "verified" {
|
||||
return fmt.Errorf("该条提交记录未通过前置校验或已失败,无法审核通过")
|
||||
}
|
||||
cert, err := s.aggregateService.LoadCertificationByUserID(ctx, record.UserID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("加载认证信息失败: %w", err)
|
||||
@@ -875,7 +975,7 @@ func (s *CertificationApplicationServiceImpl) AdminApproveSubmitRecord(ctx conte
|
||||
LegalPersonPhone: record.LegalPersonPhone,
|
||||
EnterpriseAddress: record.EnterpriseAddress,
|
||||
}
|
||||
authURL, err := s.esignClient.GenerateEnterpriseAuth(&esign.EnterpriseAuthRequest{
|
||||
authReq := &esign.EnterpriseAuthRequest{
|
||||
CompanyName: enterpriseInfo.CompanyName,
|
||||
UnifiedSocialCode: enterpriseInfo.UnifiedSocialCode,
|
||||
LegalPersonName: enterpriseInfo.LegalPersonName,
|
||||
@@ -883,16 +983,35 @@ func (s *CertificationApplicationServiceImpl) AdminApproveSubmitRecord(ctx conte
|
||||
TransactorName: enterpriseInfo.LegalPersonName,
|
||||
TransactorMobile: enterpriseInfo.LegalPersonPhone,
|
||||
TransactorID: enterpriseInfo.LegalPersonID,
|
||||
})
|
||||
}
|
||||
authURL, alreadyVerified, err := s.generateEnterpriseAuthOrDetectVerified(ctx, authReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("生成企业认证链接失败: %w", err)
|
||||
}
|
||||
if alreadyVerified {
|
||||
if err := cert.ApproveEnterpriseInfoReview("", "", adminID); err != nil {
|
||||
return fmt.Errorf("更新认证状态失败: %w", err)
|
||||
}
|
||||
if err := s.completeEnterpriseVerification(ctx, cert, cert.UserID, record.CompanyName, record.LegalPersonName); err != nil {
|
||||
return err
|
||||
}
|
||||
record.MarkManualApproved(adminID, remark)
|
||||
if err := s.enterpriseInfoSubmitRecordService.Save(ctx, record); err != nil {
|
||||
return fmt.Errorf("保存企业信息提交记录失败: %w", err)
|
||||
}
|
||||
s.logger.Info("管理员审核通过企业信息", zap.String("record_id", recordID), zap.String("admin_id", adminID))
|
||||
return nil
|
||||
}
|
||||
if err := cert.ApproveEnterpriseInfoReview(authURL.AuthShortURL, authURL.AuthFlowID, adminID); err != nil {
|
||||
return fmt.Errorf("更新认证状态失败: %w", err)
|
||||
}
|
||||
if err := s.aggregateService.SaveCertification(ctx, cert); err != nil {
|
||||
return fmt.Errorf("保存认证信息失败: %w", err)
|
||||
}
|
||||
record.MarkManualApproved(adminID, remark)
|
||||
if err := s.enterpriseInfoSubmitRecordService.Save(ctx, record); err != nil {
|
||||
return fmt.Errorf("保存企业信息提交记录失败: %w", err)
|
||||
}
|
||||
s.logger.Info("管理员审核通过企业信息", zap.String("record_id", recordID), zap.String("admin_id", adminID))
|
||||
return nil
|
||||
}
|
||||
@@ -906,6 +1025,9 @@ func (s *CertificationApplicationServiceImpl) AdminRejectSubmitRecord(ctx contex
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取提交记录失败: %w", err)
|
||||
}
|
||||
if record.Status != "verified" {
|
||||
return fmt.Errorf("该条提交记录未通过前置校验或已失败,无法从后台拒绝(请查看历史失败原因)")
|
||||
}
|
||||
cert, err := s.aggregateService.LoadCertificationByUserID(ctx, record.UserID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("加载认证信息失败: %w", err)
|
||||
@@ -931,6 +1053,10 @@ func (s *CertificationApplicationServiceImpl) AdminRejectSubmitRecord(ctx contex
|
||||
if err := s.aggregateService.SaveCertification(ctx, cert); err != nil {
|
||||
return fmt.Errorf("保存认证信息失败: %w", err)
|
||||
}
|
||||
record.MarkManualRejected(adminID, remark)
|
||||
if err := s.enterpriseInfoSubmitRecordService.Save(ctx, record); err != nil {
|
||||
return fmt.Errorf("保存企业信息提交记录失败: %w", err)
|
||||
}
|
||||
s.logger.Info("管理员审核拒绝企业信息", zap.String("record_id", recordID), zap.String("admin_id", adminID))
|
||||
return nil
|
||||
}
|
||||
@@ -964,20 +1090,39 @@ func (s *CertificationApplicationServiceImpl) AdminTransitionCertificationStatus
|
||||
LegalPersonName: record.LegalPersonName, LegalPersonID: record.LegalPersonID,
|
||||
LegalPersonPhone: record.LegalPersonPhone, EnterpriseAddress: record.EnterpriseAddress,
|
||||
}
|
||||
authURL, err := s.esignClient.GenerateEnterpriseAuth(&esign.EnterpriseAuthRequest{
|
||||
authReq := &esign.EnterpriseAuthRequest{
|
||||
CompanyName: enterpriseInfo.CompanyName, UnifiedSocialCode: enterpriseInfo.UnifiedSocialCode,
|
||||
LegalPersonName: enterpriseInfo.LegalPersonName, LegalPersonID: enterpriseInfo.LegalPersonID,
|
||||
TransactorName: enterpriseInfo.LegalPersonName, TransactorMobile: enterpriseInfo.LegalPersonPhone, TransactorID: enterpriseInfo.LegalPersonID,
|
||||
})
|
||||
}
|
||||
authURL, alreadyVerified, err := s.generateEnterpriseAuthOrDetectVerified(ctx, authReq)
|
||||
if err != nil {
|
||||
return fmt.Errorf("生成企业认证链接失败: %w", err)
|
||||
}
|
||||
if alreadyVerified {
|
||||
if err := cert.ApproveEnterpriseInfoReview("", "", cmd.AdminID); err != nil {
|
||||
return fmt.Errorf("更新认证状态失败: %w", err)
|
||||
}
|
||||
if err := s.completeEnterpriseVerification(ctx, cert, cert.UserID, record.CompanyName, record.LegalPersonName); err != nil {
|
||||
return err
|
||||
}
|
||||
record.MarkManualApproved(cmd.AdminID, cmd.Remark)
|
||||
if err := s.enterpriseInfoSubmitRecordService.Save(ctx, record); err != nil {
|
||||
return fmt.Errorf("保存企业信息提交记录失败: %w", err)
|
||||
}
|
||||
s.logger.Info("管理端变更认证状态为通过", zap.String("user_id", cmd.UserID), zap.String("admin_id", cmd.AdminID))
|
||||
return nil
|
||||
}
|
||||
if err := cert.ApproveEnterpriseInfoReview(authURL.AuthShortURL, authURL.AuthFlowID, cmd.AdminID); err != nil {
|
||||
return fmt.Errorf("更新认证状态失败: %w", err)
|
||||
}
|
||||
if err := s.aggregateService.SaveCertification(ctx, cert); err != nil {
|
||||
return fmt.Errorf("保存认证信息失败: %w", err)
|
||||
}
|
||||
record.MarkManualApproved(cmd.AdminID, cmd.Remark)
|
||||
if err := s.enterpriseInfoSubmitRecordService.Save(ctx, record); err != nil {
|
||||
return fmt.Errorf("保存企业信息提交记录失败: %w", err)
|
||||
}
|
||||
s.logger.Info("管理端变更认证状态为通过", zap.String("user_id", cmd.UserID), zap.String("admin_id", cmd.AdminID))
|
||||
return nil
|
||||
case string(enums.StatusInfoRejected):
|
||||
@@ -995,6 +1140,10 @@ func (s *CertificationApplicationServiceImpl) AdminTransitionCertificationStatus
|
||||
if err := s.aggregateService.SaveCertification(ctx, cert); err != nil {
|
||||
return fmt.Errorf("保存认证信息失败: %w", err)
|
||||
}
|
||||
record.MarkManualRejected(cmd.AdminID, cmd.Remark)
|
||||
if err := s.enterpriseInfoSubmitRecordService.Save(ctx, record); err != nil {
|
||||
return fmt.Errorf("保存企业信息提交记录失败: %w", err)
|
||||
}
|
||||
s.logger.Info("管理端变更认证状态为拒绝", zap.String("user_id", cmd.UserID), zap.String("admin_id", cmd.AdminID))
|
||||
return nil
|
||||
default:
|
||||
@@ -1049,6 +1198,66 @@ func (s *CertificationApplicationServiceImpl) convertToResponse(cert *entities.C
|
||||
return response
|
||||
}
|
||||
|
||||
func (s *CertificationApplicationServiceImpl) generateEnterpriseAuthOrDetectVerified(
|
||||
ctx context.Context,
|
||||
req *esign.EnterpriseAuthRequest,
|
||||
) (*esign.EnterpriseAuthResult, bool, error) {
|
||||
s.logger.Info("企业认证链接生成-步骤1-开始调用三方创建认证链接",
|
||||
zap.String("company_name", req.CompanyName),
|
||||
zap.String("unified_social_code", req.UnifiedSocialCode))
|
||||
authURL, err := s.esignClient.GenerateEnterpriseAuth(req)
|
||||
if err == nil {
|
||||
s.logger.Info("企业认证链接生成-步骤1-创建成功",
|
||||
zap.String("company_name", req.CompanyName),
|
||||
zap.String("auth_flow_id", authURL.AuthFlowID))
|
||||
return authURL, false, nil
|
||||
}
|
||||
if !isEnterpriseAlreadyRealnamedErr(err) {
|
||||
s.logger.Error("企业认证链接生成-步骤1-创建失败且非已实名场景",
|
||||
zap.String("company_name", req.CompanyName),
|
||||
zap.Error(err))
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
s.logger.Warn("企业已实名,跳过生成认证链接并转为自动确认",
|
||||
zap.String("company_name", req.CompanyName),
|
||||
zap.String("unified_social_code", req.UnifiedSocialCode),
|
||||
zap.Error(err))
|
||||
|
||||
identity, identityErr := s.esignClient.QueryOrgIdentityInfo(&esign.QueryOrgIdentityRequest{
|
||||
OrgIDCardNum: req.UnifiedSocialCode,
|
||||
OrgIDCardType: esign.OrgIDCardTypeUSCC,
|
||||
})
|
||||
if identityErr != nil {
|
||||
s.logger.Warn("企业认证链接生成-步骤2-按信用代码查询实名状态失败,回退按企业名查询",
|
||||
zap.String("company_name", req.CompanyName),
|
||||
zap.Error(identityErr))
|
||||
identity, identityErr = s.esignClient.QueryOrgIdentityInfo(&esign.QueryOrgIdentityRequest{
|
||||
OrgName: req.CompanyName,
|
||||
})
|
||||
}
|
||||
if identityErr != nil {
|
||||
return nil, false, fmt.Errorf("企业用户已实名,但查询实名状态失败: %w", identityErr)
|
||||
}
|
||||
s.logger.Info("企业认证链接生成-步骤2-实名状态查询成功",
|
||||
zap.String("company_name", req.CompanyName),
|
||||
zap.Int32("realname_status", identity.Data.RealnameStatus))
|
||||
if identity == nil || identity.Data.RealnameStatus != 1 {
|
||||
return nil, false, err
|
||||
}
|
||||
s.logger.Info("企业认证链接生成-步骤3-确认企业已实名,返回自动确认标记",
|
||||
zap.String("company_name", req.CompanyName))
|
||||
return nil, true, nil
|
||||
}
|
||||
|
||||
func isEnterpriseAlreadyRealnamedErr(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
msg := err.Error()
|
||||
return strings.Contains(msg, "企业用户已实名") || strings.Contains(msg, "已实名")
|
||||
}
|
||||
|
||||
// validateApplyContractCommand 验证申请合同命令
|
||||
func (s *CertificationApplicationServiceImpl) validateApplyContractCommand(cmd *commands.ApplyContractCommand) error {
|
||||
if cmd.UserID == "" {
|
||||
@@ -1104,6 +1313,9 @@ func (s *CertificationApplicationServiceImpl) completeEnterpriseVerification(
|
||||
companyName string,
|
||||
legalPersonName string,
|
||||
) error {
|
||||
s.logger.Info("完成企业认证-步骤1-开始状态流转",
|
||||
zap.String("user_id", userID),
|
||||
zap.String("company_name", companyName))
|
||||
// 完成企业认证
|
||||
err := cert.CompleteEnterpriseVerification()
|
||||
if err != nil {
|
||||
@@ -1117,6 +1329,9 @@ func (s *CertificationApplicationServiceImpl) completeEnterpriseVerification(
|
||||
s.logger.Error("查找企业信息失败", zap.Error(err))
|
||||
return fmt.Errorf("查找企业信息失败: %w", err)
|
||||
}
|
||||
s.logger.Info("完成企业认证-步骤2-获取提交记录成功",
|
||||
zap.String("user_id", userID),
|
||||
zap.String("record_id", record.ID))
|
||||
|
||||
err = s.userAggregateService.CreateEnterpriseInfo(
|
||||
ctx,
|
||||
@@ -1140,6 +1355,7 @@ func (s *CertificationApplicationServiceImpl) completeEnterpriseVerification(
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.logger.Info("完成企业认证-步骤3-合同文件生成并写入认证成功", zap.String("user_id", userID))
|
||||
|
||||
// 保存认证信息
|
||||
err = s.aggregateService.SaveCertification(ctx, cert)
|
||||
@@ -1147,6 +1363,7 @@ func (s *CertificationApplicationServiceImpl) completeEnterpriseVerification(
|
||||
s.logger.Error("保存认证信息失败", zap.Error(err))
|
||||
return fmt.Errorf("保存认证信息失败: %w", err)
|
||||
}
|
||||
s.logger.Info("完成企业认证-步骤4-认证信息保存成功", zap.String("user_id", userID))
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1162,6 +1379,9 @@ func (s *CertificationApplicationServiceImpl) generateAndAddContractFile(
|
||||
legalPersonPhone string,
|
||||
legalPersonID string,
|
||||
) error {
|
||||
s.logger.Info("合同生成-步骤1-开始填充合同模板",
|
||||
zap.String("user_id", cert.UserID),
|
||||
zap.String("company_name", companyName))
|
||||
fileComponent := map[string]string{
|
||||
"YFCompanyName": companyName,
|
||||
"YFCompanyName2": companyName,
|
||||
@@ -1180,11 +1400,17 @@ func (s *CertificationApplicationServiceImpl) generateAndAddContractFile(
|
||||
s.logger.Error("生成合同失败", zap.Error(err))
|
||||
return fmt.Errorf("生成合同失败: %s", err.Error())
|
||||
}
|
||||
s.logger.Info("合同生成-步骤1-模板填充成功",
|
||||
zap.String("user_id", cert.UserID),
|
||||
zap.String("file_id", fillTemplateResp.FileID))
|
||||
err = cert.AddContractFileID(fillTemplateResp.FileID, fillTemplateResp.FileDownloadUrl)
|
||||
if err != nil {
|
||||
s.logger.Error("加入合同文件ID链接失败", zap.Error(err))
|
||||
return fmt.Errorf("加入合同文件ID链接失败: %s", err.Error())
|
||||
}
|
||||
s.logger.Info("合同生成-步骤2-合同文件写入认证实体成功",
|
||||
zap.String("user_id", cert.UserID),
|
||||
zap.String("file_id", fillTemplateResp.FileID))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1379,7 +1605,7 @@ func (s *CertificationApplicationServiceImpl) AddStatusMetadata(ctx context.Cont
|
||||
metadata := make(map[string]interface{})
|
||||
metadata = cert.GetDataByStatus()
|
||||
switch cert.Status {
|
||||
case enums.StatusPending, enums.StatusInfoSubmitted, enums.StatusEnterpriseVerified:
|
||||
case enums.StatusPending, enums.StatusInfoPendingReview, enums.StatusInfoRejected, enums.StatusInfoSubmitted, enums.StatusEnterpriseVerified:
|
||||
record, err := s.enterpriseInfoSubmitRecordRepo.FindLatestByUserID(ctx, cert.UserID)
|
||||
if err == nil && record != nil {
|
||||
enterpriseInfo := map[string]interface{}{
|
||||
@@ -1389,6 +1615,7 @@ func (s *CertificationApplicationServiceImpl) AddStatusMetadata(ctx context.Cont
|
||||
"enterprise_address": record.EnterpriseAddress,
|
||||
"legal_person_phone": record.LegalPersonPhone,
|
||||
"legal_person_id": record.LegalPersonID,
|
||||
"submit_at": record.SubmitAt.Format(time.RFC3339),
|
||||
}
|
||||
metadata["enterprise_info"] = enterpriseInfo
|
||||
}
|
||||
|
||||
@@ -1070,6 +1070,7 @@ func (s *ProductApplicationServiceImpl) getDTOMap() map[string]interface{} {
|
||||
"DWBG8B4D": &dto.DWBG8B4DReq{},
|
||||
"FLXG8B4D": &dto.FLXG8B4DReq{},
|
||||
"IVYZ81NC": &dto.IVYZ81NCReq{},
|
||||
"IVYZ2MN6": &dto.IVYZ2MN6Req{},
|
||||
"IVYZ7F3A": &dto.IVYZ7F3AReq{},
|
||||
"IVYZ3P9M": &dto.IVYZ3P9MReq{},
|
||||
"IVYZ3A7F": &dto.IVYZ3A7FReq{},
|
||||
|
||||
@@ -72,6 +72,11 @@ type IVYZ81NCReq struct {
|
||||
Name string `json:"name" validate:"required,min=1,validName"`
|
||||
IDCard string `json:"id_card" validate:"required,validIDCard"`
|
||||
}
|
||||
type IVYZ2MN6Req struct {
|
||||
IDCard string `json:"id_card" validate:"required,validIDCard"`
|
||||
Name string `json:"name" validate:"required,min=1,validName"`
|
||||
Authorized string `json:"authorized" validate:"required,oneof=0 1"`
|
||||
}
|
||||
type IVYZ9363Req struct {
|
||||
ManName string `json:"man_name" validate:"required,min=1,validName"`
|
||||
ManIDCard string `json:"man_id_card" validate:"required,validIDCard"`
|
||||
@@ -410,7 +415,7 @@ type QCXGP00WReq struct {
|
||||
VinCode string `json:"vin_code" validate:"required"`
|
||||
PlateNo string `json:"plate_no" validate:"omitempty"`
|
||||
ReturnURL string `json:"return_url" validate:"required,validReturnURL"`
|
||||
VlPhotoData string `json:"vlphoto_data" validate:"omitempty,validBase64Image"`
|
||||
VlPhotoData string `json:"vlphoto_data" validate:"required,validBase64Image"`
|
||||
}
|
||||
|
||||
type QCXG4D2EReq struct {
|
||||
@@ -494,6 +499,12 @@ type IVYZ7F3AReq struct {
|
||||
Name string `json:"name" validate:"required,min=1,validName"`
|
||||
Authorized string `json:"authorized" validate:"required,oneof=0 1"`
|
||||
}
|
||||
type IVYZRAX1Req struct {
|
||||
Name string `json:"name" validate:"required,min=1,validName"`
|
||||
IDCard string `json:"id_card" validate:"required,validIDCard"`
|
||||
MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"`
|
||||
Authorized string `json:"authorized" validate:"required,oneof=0 1"`
|
||||
}
|
||||
|
||||
type IVYZ3P9MReq struct {
|
||||
IDCard string `json:"id_card" validate:"required,validIDCard"`
|
||||
@@ -633,6 +644,13 @@ type IVYZ38SRReq struct {
|
||||
WomanIDCard string `json:"woman_id_card" validate:"required,validIDCard"`
|
||||
}
|
||||
|
||||
type IVYZ5E22Req struct {
|
||||
ManName string `json:"man_name" validate:"required,min=1,validName"`
|
||||
ManIDCard string `json:"man_id_card" validate:"required,validIDCard"`
|
||||
WomanName string `json:"woman_name" validate:"required,min=1,validName"`
|
||||
WomanIDCard string `json:"woman_id_card" validate:"required,validIDCard"`
|
||||
Authorized string `json:"authorized" validate:"required,oneof=0 1"`
|
||||
}
|
||||
type IVYZ48SRReq struct {
|
||||
ManName string `json:"man_name" validate:"required,min=1,validName"`
|
||||
ManIDCard string `json:"man_id_card" validate:"required,validIDCard"`
|
||||
@@ -653,6 +671,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"`
|
||||
@@ -676,6 +699,13 @@ type FLXG9C1DReq struct {
|
||||
Authorized string `json:"authorized" validate:"required,oneof=0 1"`
|
||||
}
|
||||
|
||||
type DWBG5SAMReq struct {
|
||||
IDCard string `json:"id_card" validate:"required,validIDCard"`
|
||||
Name string `json:"name" validate:"required,min=1,validName"`
|
||||
MobileNo string `json:"mobile_no" validate:"required,min=11,max=11,validMobileNo"`
|
||||
AuthorizationURL string `json:"authorization_url" validate:"required,authorization_url"`
|
||||
}
|
||||
|
||||
// 法院被执行人限高版
|
||||
type FLXG3A9BReq struct {
|
||||
IDCard string `json:"id_card" validate:"required,validIDCard"`
|
||||
@@ -1029,6 +1059,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, //运营商归属地数卖
|
||||
@@ -301,6 +302,7 @@ func registerAllProcessors(combService *comb.CombService) {
|
||||
"IVYZ3A7F": ivyz.ProcessIVYZ3A7FRequest,
|
||||
"IVYZ9D2E": ivyz.ProcessIVYZ9D2ERequest,
|
||||
"IVYZ81NC": ivyz.ProcessIVYZ81NCRequest,
|
||||
"IVYZ2MN6": ivyz.ProcessIVYZ2MN6Request,
|
||||
"IVYZ6G7H": ivyz.ProcessIVYZ6G7HRequest,
|
||||
"IVYZ8I9J": ivyz.ProcessIVYZ8I9JRequest,
|
||||
"IVYZ9K2L": ivyz.ProcessIVYZ9K2LRequest,
|
||||
@@ -317,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, //活体识别步骤二
|
||||
@@ -326,6 +329,10 @@ func registerAllProcessors(combService *comb.CombService) {
|
||||
"IVYZ28HY": ivyz.ProcessIVYZ28HYRequest, //婚姻状况核验(单人)
|
||||
"IVYZ38SR": ivyz.ProcessIVYZ38SRRequest, //婚姻状态核验(双人)
|
||||
"IVYZ48SR": ivyz.ProcessIVYZ48SRRequest, //婚姻状态核验V2(双人)
|
||||
"IVYZ5E22": ivyz.ProcessIVYZ5E22Request, //双人婚姻评估查询zhicha版本
|
||||
"IVYZRAX1": ivyz.ProcessIVYZRAX1Request, //融安信用分
|
||||
"IVYZRAX2": ivyz.ProcessIVYZRAX2Request,//融御反欺诈分
|
||||
|
||||
|
||||
// COMB系列处理器 - 只注册有自定义逻辑的组合包
|
||||
"COMB86PM": comb.ProcessCOMB86PMRequest, // 有自定义逻辑:重命名ApiCode
|
||||
@@ -364,6 +371,7 @@ func registerAllProcessors(combService *comb.CombService) {
|
||||
"DWBG6A2C": dwbg.ProcessDWBG6A2CRequest,
|
||||
"DWBG8B4D": dwbg.ProcessDWBG8B4DRequest,
|
||||
"DWBG7F3A": dwbg.ProcessDWBG7F3ARequest,
|
||||
"DWBG5SAM": dwbg.ProcessDWBG5SAMRequest,
|
||||
|
||||
// FLXG系列处理器 - 风险管控 (包含原FXHY功能)
|
||||
"FLXG8B4D": flxg.ProcessFLXG8B4DRequest,
|
||||
|
||||
@@ -152,6 +152,7 @@ func (s *FormConfigServiceImpl) getDTOStruct(ctx context.Context, apiCode string
|
||||
"DWBG8B4D": &dto.DWBG8B4DReq{},
|
||||
"FLXG8B4D": &dto.FLXG8B4DReq{},
|
||||
"IVYZ81NC": &dto.IVYZ81NCReq{},
|
||||
"IVYZ2MN6": &dto.IVYZ2MN6Req{},
|
||||
"IVYZ7F3A": &dto.IVYZ7F3AReq{},
|
||||
"IVYZ3P9M": &dto.IVYZ3P9MReq{},
|
||||
"IVYZ3A7F": &dto.IVYZ3A7FReq{},
|
||||
@@ -240,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{}, //运营商三要素简版即时版查询
|
||||
@@ -271,6 +273,11 @@ func (s *FormConfigServiceImpl) getDTOStruct(ctx context.Context, apiCode string
|
||||
"IVYZ28HY": &dto.IVYZ28HYReq{}, //婚姻状况核验(单人)
|
||||
"IVYZ38SR": &dto.IVYZ38SRReq{}, //婚姻状态核验(双人)
|
||||
"IVYZ48SR": &dto.IVYZ48SRReq{}, //婚姻状态核验V2(双人)
|
||||
"IVYZ5E22": &dto.IVYZ5E22Req{}, //双人婚姻评估查询zhicha版本
|
||||
"DWBG5SAM": &dto.DWBG5SAMReq{}, //天远指迷报告
|
||||
"QYGLDJ33": &dto.QYGLDJ33Req{}, //企业年报信息核验
|
||||
"IVYZRAX1": &dto.IVYZRAX1Req{},//融安信用分
|
||||
"IVYZRAX2": &dto.IVYZRAX1Req{},//融御反欺诈
|
||||
}
|
||||
|
||||
// 优先返回已配置的DTO
|
||||
@@ -448,6 +455,7 @@ func (s *FormConfigServiceImpl) generateFieldLabel(jsonTag string) string {
|
||||
labelMap := map[string]string{
|
||||
"mobile_no": "手机号码",
|
||||
"id_card": "身份证号",
|
||||
"idCard": "身份证号",
|
||||
"name": "姓名",
|
||||
"man_name": "男方姓名",
|
||||
"woman_name": "女方姓名",
|
||||
@@ -515,6 +523,7 @@ func (s *FormConfigServiceImpl) generateExampleValue(fieldType reflect.Type, jso
|
||||
exampleMap := map[string]string{
|
||||
"mobile_no": "13800138000",
|
||||
"id_card": "110101199001011234",
|
||||
"idCard": "110101199001011234",
|
||||
"name": "张三",
|
||||
"man_name": "张三",
|
||||
"woman_name": "李四",
|
||||
@@ -589,6 +598,7 @@ func (s *FormConfigServiceImpl) generatePlaceholder(jsonTag string, fieldType st
|
||||
placeholderMap := map[string]string{
|
||||
"mobile_no": "请输入11位手机号码",
|
||||
"id_card": "请输入18位身份证号码",
|
||||
"idCard": "请输入18位身份证号码",
|
||||
"name": "请输入真实姓名",
|
||||
"man_name": "请输入男方真实姓名",
|
||||
"woman_name": "请输入女方真实姓名",
|
||||
@@ -629,7 +639,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": "请输入车型名称",
|
||||
@@ -665,6 +675,7 @@ func (s *FormConfigServiceImpl) generateDescription(jsonTag string, validation s
|
||||
descMap := map[string]string{
|
||||
"mobile_no": "请输入11位手机号码",
|
||||
"id_card": "请输入18位身份证号码最后一位如是字母请大写",
|
||||
"idCard": "请输入18位身份证号码最后一位如是字母请大写",
|
||||
"name": "请输入真实姓名",
|
||||
"man_name": "请输入男方真实姓名",
|
||||
"woman_name": "请输入女方真实姓名",
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package dwbg
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"tyapi-server/internal/domains/api/dto"
|
||||
"tyapi-server/internal/domains/api/services/processors"
|
||||
"tyapi-server/internal/infrastructure/external/zhicha"
|
||||
)
|
||||
|
||||
// ProcessDWBG5SAMRequest DWBG5SAM 天远指迷报告
|
||||
func ProcessDWBG5SAMRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||
var paramsDto dto.DWBG5SAMReq
|
||||
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.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)
|
||||
}
|
||||
|
||||
encryptedMobileNo, err := deps.ZhichaService.Encrypt(paramsDto.MobileNo)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
reqData := map[string]interface{}{
|
||||
"name": encryptedName,
|
||||
"idCard": encryptedIDCard,
|
||||
"phone": encryptedMobileNo,
|
||||
"accessoryUrl": paramsDto.AuthorizationURL,
|
||||
}
|
||||
|
||||
respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI112", 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)
|
||||
}
|
||||
}
|
||||
|
||||
// 过滤响应数据,删除指定字段
|
||||
if respMap, ok := respData.(map[string]interface{}); ok {
|
||||
delete(respMap, "reportUrl")
|
||||
}
|
||||
|
||||
// 将响应数据转换为JSON字节
|
||||
respBytes, err := json.Marshal(respData)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
return respBytes, nil
|
||||
}
|
||||
@@ -24,7 +24,8 @@ func ProcessFLXG0V4BRequest(ctx context.Context, params []byte, deps *processors
|
||||
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" {
|
||||
// 去掉司法案件案件去掉身份证号码
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" {
|
||||
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
|
||||
}
|
||||
encryptedName, err := deps.WestDexService.Encrypt(paramsDto.Name)
|
||||
|
||||
@@ -20,7 +20,9 @@ func ProcessFLXG3A9BRequest(ctx context.Context, params []byte, deps *processors
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
|
||||
if paramsDto.IDCard == "410482198504029333" {
|
||||
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)
|
||||
|
||||
@@ -20,7 +20,7 @@ func ProcessFLXG5A3BRequest(ctx context.Context, params []byte, deps *processors
|
||||
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" {
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" {
|
||||
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
|
||||
}
|
||||
encryptedName, err := deps.ZhichaService.Encrypt(paramsDto.Name)
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"tyapi-server/internal/domains/api/dto"
|
||||
"tyapi-server/internal/domains/api/services/processors"
|
||||
"tyapi-server/internal/infrastructure/external/xingwei"
|
||||
"tyapi-server/internal/infrastructure/external/zhicha"
|
||||
)
|
||||
|
||||
// ProcessFLXG7E8FRequest FLXG7E8F API处理方法 - 个人司法数据查询
|
||||
@@ -20,30 +20,225 @@ func ProcessFLXG7E8FRequest(ctx context.Context, params []byte, deps *processors
|
||||
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" {
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" {
|
||||
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
|
||||
}
|
||||
// 构建请求数据,将项目规范的字段名转换为 XingweiService 需要的字段名
|
||||
reqData := map[string]interface{}{
|
||||
"name": paramsDto.Name,
|
||||
"idCardNum": paramsDto.IDCard,
|
||||
"phoneNumber": paramsDto.MobileNo,
|
||||
|
||||
encryptedName, err := deps.ZhichaService.Encrypt(paramsDto.Name)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
// 调用行为数据API,使用指定的project_id
|
||||
projectID := "CDJ-1101695378264092672"
|
||||
respBytes, err := deps.XingweiService.CallAPI(ctx, projectID, reqData)
|
||||
encryptedIDCard, err := deps.ZhichaService.Encrypt(paramsDto.IDCard)
|
||||
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)
|
||||
}
|
||||
|
||||
reqData := map[string]interface{}{
|
||||
"name": encryptedName,
|
||||
"idCard": encryptedIDCard,
|
||||
"authorized": "1",
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
respMap, ok := respData.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, errors.Join(processors.ErrSystem, errors.New("响应格式错误"))
|
||||
}
|
||||
|
||||
result := map[string]interface{}{
|
||||
"judicial_data": mapFLXG5A3BToJudicialData(respMap),
|
||||
}
|
||||
|
||||
respBytes, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
return respBytes, nil
|
||||
}
|
||||
|
||||
func mapFLXG5A3BToJudicialData(resp map[string]interface{}) map[string]interface{} {
|
||||
judicialData := map[string]interface{}{
|
||||
"consumptionRestrictionList": mapXgbzxrToConsumptionRestriction(asSlice(resp["xgbzxr"])),
|
||||
"breachCaseList": mapSxbzxrToBreachCaseList(asSlice(resp["sxbzxr"])),
|
||||
"lawsuitStat": normalizeLawsuitStat(asMap(resp["entout"])),
|
||||
}
|
||||
|
||||
return judicialData
|
||||
}
|
||||
|
||||
func mapXgbzxrToConsumptionRestriction(items []interface{}) []map[string]interface{} {
|
||||
result := make([]map[string]interface{}, 0, len(items))
|
||||
for _, item := range items {
|
||||
m := asMap(item)
|
||||
if len(m) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
result = append(result, map[string]interface{}{
|
||||
"caseNumber": m["ah"],
|
||||
"id": m["id"],
|
||||
"issueDate": m["fbrq"],
|
||||
"executiveCourt": m["zxfy"],
|
||||
"fileDate": m["larq"],
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func mapSxbzxrToBreachCaseList(items []interface{}) []map[string]interface{} {
|
||||
result := make([]map[string]interface{}, 0, len(items))
|
||||
for _, item := range items {
|
||||
m := asMap(item)
|
||||
if len(m) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
result = append(result, map[string]interface{}{
|
||||
"caseNumber": m["ah"],
|
||||
"issueDate": m["fbrq"],
|
||||
"id": m["id"],
|
||||
"fileDate": m["larq"],
|
||||
"fulfillStatus": m["lxqk"],
|
||||
"estimatedJudgementAmount": m["pjje_gj"],
|
||||
"province": m["sf"],
|
||||
"sex": m["xb"],
|
||||
"concreteDetails": m["xwqx"],
|
||||
"obligation": m["yw"],
|
||||
"executiveCourt": m["zxfy"],
|
||||
"enforcementBasisOrganization": m["zxyjdw"],
|
||||
"enforcementBasisNumber": m["zxyjwh"],
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func normalizeLawsuitStat(entout map[string]interface{}) map[string]interface{} {
|
||||
lawsuitStat := defaultLawsuitStat()
|
||||
for k, v := range entout {
|
||||
switch k {
|
||||
case "cases_tree":
|
||||
lawsuitStat[k] = normalizeCasesTree(asMap(v))
|
||||
case "count":
|
||||
lawsuitStat[k] = normalizeCount(asMap(v))
|
||||
case "preservation", "administrative", "civil", "implement", "criminal", "bankrupt":
|
||||
lawsuitStat[k] = normalizeCaseSection(asMap(v))
|
||||
default:
|
||||
lawsuitStat[k] = v
|
||||
}
|
||||
}
|
||||
return lawsuitStat
|
||||
}
|
||||
|
||||
func defaultLawsuitStat() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"crc": 0,
|
||||
"cases_tree": normalizeCasesTree(map[string]interface{}{}),
|
||||
"count": normalizeCount(map[string]interface{}{}),
|
||||
"preservation": normalizeCaseSection(map[string]interface{}{}),
|
||||
"administrative": normalizeCaseSection(map[string]interface{}{}),
|
||||
"civil": normalizeCaseSection(map[string]interface{}{}),
|
||||
"implement": normalizeCaseSection(map[string]interface{}{}),
|
||||
"criminal": normalizeCaseSection(map[string]interface{}{}),
|
||||
"bankrupt": normalizeCaseSection(map[string]interface{}{}),
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeCasesTree(src map[string]interface{}) map[string]interface{} {
|
||||
dst := map[string]interface{}{
|
||||
"administrative": []interface{}{},
|
||||
"criminal": []interface{}{},
|
||||
"civil": []interface{}{},
|
||||
}
|
||||
for _, key := range []string{"administrative", "criminal", "civil"} {
|
||||
if v, ok := src[key]; ok {
|
||||
dst[key] = asSlice(v)
|
||||
}
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
func normalizeCaseSection(src map[string]interface{}) map[string]interface{} {
|
||||
dst := map[string]interface{}{
|
||||
"cases": []interface{}{},
|
||||
"count": normalizeCount(map[string]interface{}{}),
|
||||
}
|
||||
if v, ok := src["cases"]; ok {
|
||||
dst["cases"] = asSlice(v)
|
||||
}
|
||||
if v, ok := src["count"]; ok {
|
||||
dst["count"] = normalizeCount(asMap(v))
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
func normalizeCount(src map[string]interface{}) map[string]interface{} {
|
||||
dst := map[string]interface{}{
|
||||
"money_yuangao": 0,
|
||||
"area_stat": "",
|
||||
"count_jie_beigao": 0,
|
||||
"count_total": 0,
|
||||
"money_wei_yuangao": 0,
|
||||
"count_wei_total": 0,
|
||||
"money_wei_beigao": 0,
|
||||
"count_other": 0,
|
||||
"money_beigao": 0,
|
||||
"count_yuangao": 0,
|
||||
"money_jie_other": 0,
|
||||
"money_total": 0,
|
||||
"money_wei_total": 0,
|
||||
"count_wei_yuangao": 0,
|
||||
"ay_stat": "",
|
||||
"count_beigao": 0,
|
||||
"money_jie_yuangao": 0,
|
||||
"jafs_stat": "",
|
||||
"money_jie_beigao": 0,
|
||||
"count_wei_beigao": 0,
|
||||
"count_jie_other": 0,
|
||||
"count_jie_total": 0,
|
||||
"count_wei_other": 0,
|
||||
"money_other": 0,
|
||||
"count_jie_yuangao": 0,
|
||||
"money_jie_total": 0,
|
||||
"money_wei_other": 0,
|
||||
"money_wei_percent": 0,
|
||||
"larq_stat": "",
|
||||
}
|
||||
for k, v := range src {
|
||||
dst[k] = v
|
||||
}
|
||||
return dst
|
||||
}
|
||||
|
||||
func asMap(v interface{}) map[string]interface{} {
|
||||
if v == nil {
|
||||
return map[string]interface{}{}
|
||||
}
|
||||
|
||||
if m, ok := v.(map[string]interface{}); ok {
|
||||
return m
|
||||
}
|
||||
|
||||
return map[string]interface{}{}
|
||||
}
|
||||
|
||||
func asSlice(v interface{}) []interface{} {
|
||||
if v == nil {
|
||||
return []interface{}{}
|
||||
}
|
||||
|
||||
if s, ok := v.([]interface{}); ok {
|
||||
return s
|
||||
}
|
||||
|
||||
return []interface{}{}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,9 @@ func ProcessFLXG9C1DRequest(ctx context.Context, params []byte, deps *processors
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
|
||||
if paramsDto.IDCard == "410482198504029333" {
|
||||
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)
|
||||
|
||||
@@ -20,7 +20,7 @@ func ProcessFLXGCA3DRequest(ctx context.Context, params []byte, deps *processors
|
||||
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" {
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" {
|
||||
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
|
||||
}
|
||||
encryptedName, err := deps.WestDexService.Encrypt(paramsDto.Name)
|
||||
|
||||
@@ -25,7 +25,7 @@ func ProcessFLXGDEA9Request(ctx context.Context, params []byte, deps *processors
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" {
|
||||
if paramsDto.IDCard == "350681198611130611" || paramsDto.IDCard == "370982199012037272" || paramsDto.IDCard == "622301200006250550" || paramsDto.IDCard == "320682198910134998" || paramsDto.IDCard == "640102198708020925" || paramsDto.IDCard == "420624197310234034" || paramsDto.IDCard == "350104198501184416" || paramsDto.IDCard == "410521198606018056" || paramsDto.IDCard == "410482198504029333" || paramsDto.IDCard == "370982199012037272" {
|
||||
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
|
||||
}
|
||||
encryptedIDCard, err := deps.ZhichaService.Encrypt(paramsDto.IDCard)
|
||||
|
||||
@@ -20,7 +20,9 @@ func ProcessFLXGK5D2Request(ctx context.Context, params []byte, deps *processors
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
|
||||
if paramsDto.IDCard == "410482198504029333" {
|
||||
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)
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
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/zhicha"
|
||||
)
|
||||
|
||||
// ProcessIVYZ2MN6Request IVYZ2MN6 API处理方法
|
||||
func ProcessIVYZ2MN6Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||
var paramsDto dto.IVYZ2MN6Req
|
||||
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.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, "ZCI1004", reqData)
|
||||
if err != nil {
|
||||
if errors.Is(err, zhicha.ErrDatasource) {
|
||||
return nil, errors.Join(processors.ErrDatasource, err)
|
||||
}
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
respBytes, err := json.Marshal(respData)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
return respBytes, nil
|
||||
}
|
||||
@@ -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,66 @@
|
||||
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/zhicha"
|
||||
)
|
||||
|
||||
// ProcessIVYZ5E22Request API处理方法 - 双人婚姻评估查询
|
||||
func ProcessIVYZ5E22Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||
var paramsDto dto.IVYZ5E22Req
|
||||
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)
|
||||
}
|
||||
|
||||
encryptedManName, err := deps.ZhichaService.Encrypt(paramsDto.ManName)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
encryptedManIDCard, err := deps.ZhichaService.Encrypt(paramsDto.ManIDCard)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
encryptedWomanName, err := deps.ZhichaService.Encrypt(paramsDto.WomanName)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
encryptedWomanIDCard, err := deps.ZhichaService.Encrypt(paramsDto.WomanIDCard)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
reqData := map[string]interface{}{
|
||||
"nameMan": encryptedManName,
|
||||
"idCardMan": encryptedManIDCard,
|
||||
"nameWoman": encryptedWomanName,
|
||||
"idCardWoman": encryptedWomanIDCard,
|
||||
"authorized": paramsDto.Authorized,
|
||||
}
|
||||
|
||||
respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI042", 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
|
||||
}
|
||||
@@ -49,6 +49,7 @@ func ProcessIVYZ81NCRequest(ctx context.Context, params []byte, deps *processors
|
||||
// 解析响应数据,期望格式为 {"state": "1"}
|
||||
var stateResp struct {
|
||||
State string `json:"state"`
|
||||
RegTime string `json:"regTime"`
|
||||
}
|
||||
|
||||
// 将 respData 转换为 JSON 字节再解析
|
||||
@@ -82,7 +83,7 @@ func ProcessIVYZ81NCRequest(ctx context.Context, params []byte, deps *processors
|
||||
result := map[string]interface{}{
|
||||
"code": "0",
|
||||
"data": map[string]interface{}{
|
||||
"op_date": "",
|
||||
"op_date": stateResp.RegTime,
|
||||
"op_type": opType,
|
||||
"op_type_desc": opTypeDesc,
|
||||
},
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -20,20 +20,17 @@ func ProcessIVYZN2P8Request(ctx context.Context, params []byte, deps *processors
|
||||
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
reqFormData := map[string]interface{}{
|
||||
"idcard": paramsDto.IDCard,
|
||||
|
||||
reqData := map[string]interface{}{
|
||||
"name": paramsDto.Name,
|
||||
"idcard": paramsDto.IDCard,
|
||||
}
|
||||
|
||||
// 以表单方式调用数脉 API;参数在 CallAPIForm 内转为 application/x-www-form-urlencoded
|
||||
apiPath := "/v4/id_card/check" // 接口路径,根据数脉文档填写(如 v4/xxx)
|
||||
apiPath := "/v4/id_card/check"
|
||||
|
||||
// 先尝试使用政务接口(app_id2 和 app_secret2)
|
||||
respBytes, err := deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData, true)
|
||||
if err != nil {
|
||||
// 使用实时接口(app_id 和 app_secret)重试
|
||||
respBytes, err = deps.ShumaiService.CallAPIForm(ctx, apiPath, reqFormData, false)
|
||||
// 如果重试后仍然失败,返回错误
|
||||
respBytes, err := deps.ShumaiService.CallAPIForm(ctx, apiPath, reqData, true)
|
||||
if err != nil {
|
||||
if errors.Is(err, shumai.ErrNotFound) {
|
||||
// 查无记录情况
|
||||
@@ -44,12 +41,81 @@ func ProcessIVYZN2P8Request(ctx context.Context, params []byte, deps *processors
|
||||
} 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
|
||||
}
|
||||
|
||||
// respBytes, err := deps.AlicloudService.CallAPI("api-mall/api/id_card/check", reqData)
|
||||
// if err != nil {
|
||||
// if errors.Is(err, alicloud.ErrDatasource) {
|
||||
// return nil, errors.Join(processors.ErrDatasource, err)
|
||||
// }
|
||||
// return nil, errors.Join(processors.ErrSystem, err)
|
||||
// }
|
||||
|
||||
// return respBytes, nil
|
||||
// // 对齐 yysybe08test 的原始响应结构,取 data 字段映射为 ivyzn2p8 返回
|
||||
// var aliyunData struct {
|
||||
// Code int `json:"code"`
|
||||
// Data struct {
|
||||
// Birthday string `json:"birthday"`
|
||||
// Result interface{} `json:"result"`
|
||||
// Address string `json:"address"`
|
||||
// OrderNo string `json:"orderNo"`
|
||||
// Sex string `json:"sex"`
|
||||
// Desc string `json:"desc"`
|
||||
// } `json:"data"`
|
||||
// Result interface{} `json:"result"`
|
||||
// Desc string `json:"desc"`
|
||||
// }
|
||||
// if err := json.Unmarshal(respBytes, &aliyunData); err != nil {
|
||||
// return nil, errors.Join(processors.ErrSystem, err)
|
||||
// }
|
||||
|
||||
// rawResult := aliyunData.Result
|
||||
// rawDesc := aliyunData.Desc
|
||||
// if aliyunData.Code == 200 {
|
||||
// rawResult = aliyunData.Data.Result
|
||||
// rawDesc = aliyunData.Data.Desc
|
||||
// }
|
||||
|
||||
// response := map[string]interface{}{
|
||||
// "result": normalizeResult(rawResult),
|
||||
// "order_no": aliyunData.Data.OrderNo,
|
||||
// "desc": rawDesc,
|
||||
// "sex": aliyunData.Data.Sex,
|
||||
// "birthday": aliyunData.Data.Birthday,
|
||||
// "address": aliyunData.Data.Address,
|
||||
// }
|
||||
// return json.Marshal(response)
|
||||
// }
|
||||
|
||||
// func normalizeResult(v interface{}) int {
|
||||
// switch r := v.(type) {
|
||||
// case float64:
|
||||
// return int(r)
|
||||
// case int:
|
||||
// return r
|
||||
// case int32:
|
||||
// return int(r)
|
||||
// case int64:
|
||||
// return int(r)
|
||||
// case json.Number:
|
||||
// n, err := r.Int64()
|
||||
// if err == nil {
|
||||
// return int(n)
|
||||
// }
|
||||
// case string:
|
||||
// s := strings.TrimSpace(r)
|
||||
// if s == "" {
|
||||
// return 1
|
||||
// }
|
||||
// n, err := strconv.Atoi(s)
|
||||
// if err == nil {
|
||||
// return n
|
||||
// }
|
||||
// }
|
||||
// // 默认按不一致处理
|
||||
// return 1
|
||||
// }
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
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/zhicha"
|
||||
)
|
||||
|
||||
// ProcessIVYZRAX1Request IVYZRAX1 API处理方法 - 融安信用分
|
||||
func ProcessIVYZRAX1Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||
var paramsDto dto.IVYZRAX1Req
|
||||
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.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)
|
||||
// }
|
||||
// encryptedMoblie, err := deps.ZhichaService.Encrypt(paramsDto.MobileNo)
|
||||
// if err != nil {
|
||||
// return nil, errors.Join(processors.ErrSystem, err)
|
||||
// }
|
||||
|
||||
md5Name := deps.ZhichaService.MD5(paramsDto.Name)
|
||||
md5IDCard := deps.ZhichaService.MD5(paramsDto.IDCard)
|
||||
md5Mobile := deps.ZhichaService.MD5(paramsDto.MobileNo)
|
||||
|
||||
reqData := map[string]interface{}{
|
||||
// "name": encryptedName,
|
||||
// "idCard": encryptedIDCard,
|
||||
// "phone": encryptedMoblie,
|
||||
"authorized": paramsDto.Authorized,
|
||||
"name": md5Name,
|
||||
"idCard": md5IDCard,
|
||||
"phone": md5Mobile,
|
||||
}
|
||||
|
||||
respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI084", 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
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
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/zhicha"
|
||||
)
|
||||
|
||||
// ProcessIVYZRAX2Request IVYZRAX2 API处理方法 - 融御反欺诈分
|
||||
func ProcessIVYZRAX2Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||
var paramsDto dto.IVYZRAX1Req
|
||||
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.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)
|
||||
// }
|
||||
// encryptedMoblie, err := deps.ZhichaService.Encrypt(paramsDto.MobileNo)
|
||||
// if err != nil {
|
||||
// return nil, errors.Join(processors.ErrSystem, err)
|
||||
// }
|
||||
|
||||
md5Name := deps.ZhichaService.MD5(paramsDto.Name)
|
||||
md5IDCard := deps.ZhichaService.MD5(paramsDto.IDCard)
|
||||
md5Mobile := deps.ZhichaService.MD5(paramsDto.MobileNo)
|
||||
|
||||
reqData := map[string]interface{}{
|
||||
// "name": encryptedName,
|
||||
// "idCard": encryptedIDCard,
|
||||
// "phone": encryptedMoblie,
|
||||
"authorized": paramsDto.Authorized,
|
||||
"name": md5Name,
|
||||
"idCard": md5IDCard,
|
||||
"phone": md5Mobile,
|
||||
}
|
||||
|
||||
respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI083", 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
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -4,13 +4,15 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
"tyapi-server/internal/domains/api/dto"
|
||||
"tyapi-server/internal/domains/api/services/processors"
|
||||
"tyapi-server/internal/infrastructure/external/xingwei"
|
||||
"tyapi-server/internal/infrastructure/external/zhicha"
|
||||
)
|
||||
|
||||
// ProcessJRZQ0L85Request JRZQ0L85 API处理方法 - xingwei service
|
||||
// ProcessJRZQ0L85Request JRZQ0L85 API处理方法 - 个人信用分
|
||||
func ProcessJRZQ0L85Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||
var paramsDto dto.JRZQ0L85Req
|
||||
if err := json.Unmarshal(params, ¶msDto); err != nil {
|
||||
@@ -21,27 +23,100 @@ func ProcessJRZQ0L85Request(ctx context.Context, params []byte, deps *processors
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
|
||||
// 构建请求数据,将项目规范的字段名转换为 XingweiService 需要的字段名
|
||||
reqData := map[string]interface{}{
|
||||
"name": paramsDto.Name,
|
||||
"idCardNum": paramsDto.IDCard,
|
||||
"phoneNumber": paramsDto.MobileNo,
|
||||
encryptedName, err := deps.ZhichaService.Encrypt(paramsDto.Name)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
// 调用行为数据API,使用指定的project_id
|
||||
projectID := "CDJ-1101695364016041984"
|
||||
respBytes, err := deps.XingweiService.CallAPI(ctx, projectID, reqData)
|
||||
encryptedIDCard, err := deps.ZhichaService.Encrypt(paramsDto.IDCard)
|
||||
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)
|
||||
}
|
||||
|
||||
encryptedMobileNo, err := deps.ZhichaService.Encrypt(paramsDto.MobileNo)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
reqData := map[string]interface{}{
|
||||
"name": encryptedName,
|
||||
"idCard": encryptedIDCard,
|
||||
"phone": encryptedMobileNo,
|
||||
"authorized": "1",
|
||||
}
|
||||
|
||||
respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI021", 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)
|
||||
}
|
||||
}
|
||||
|
||||
score := "-1"
|
||||
if m, ok := respData.(map[string]interface{}); ok {
|
||||
if raw, exists := m["xyp_cpl0081"]; exists {
|
||||
if v, ok := parseToFloat64(raw); ok {
|
||||
score = mapXypToGeneralScore(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result := map[string]interface{}{
|
||||
"score_120_General": score,
|
||||
}
|
||||
|
||||
respBytes, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
return respBytes, nil
|
||||
}
|
||||
|
||||
func parseToFloat64(v interface{}) (float64, bool) {
|
||||
switch value := v.(type) {
|
||||
case float64:
|
||||
return value, true
|
||||
case string:
|
||||
if value == "" {
|
||||
return 0, false
|
||||
}
|
||||
f, err := strconv.ParseFloat(value, 64)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
return f, true
|
||||
case json.Number:
|
||||
f, err := value.Float64()
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
return f, true
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
|
||||
func mapXypToGeneralScore(xyp float64) string {
|
||||
// xyp_cpl0081: 0~1,值越大风险越高;
|
||||
// score_120_General: 300~900,值越大信用越好。
|
||||
if xyp < 0 {
|
||||
xyp = 0
|
||||
}
|
||||
if xyp > 1 {
|
||||
xyp = 1
|
||||
}
|
||||
|
||||
score := 900 - xyp*600
|
||||
scoreInt := int(math.Round(score))
|
||||
if scoreInt < 300 {
|
||||
scoreInt = 300
|
||||
}
|
||||
if scoreInt > 900 {
|
||||
scoreInt = 900
|
||||
}
|
||||
|
||||
return strconv.Itoa(scoreInt)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,833 @@
|
||||
package jrzq
|
||||
|
||||
var jrzq6F2AVariableKeys = []string{
|
||||
"flag_applyloanstr",
|
||||
"als_d7_id_pdl_allnum",
|
||||
"als_d7_id_pdl_orgnum",
|
||||
"als_d7_id_caon_allnum",
|
||||
"als_d7_id_caon_orgnum",
|
||||
"als_d7_id_rel_allnum",
|
||||
"als_d7_id_rel_orgnum",
|
||||
"als_d7_id_caoff_allnum",
|
||||
"als_d7_id_caoff_orgnum",
|
||||
"als_d7_id_cooff_allnum",
|
||||
"als_d7_id_cooff_orgnum",
|
||||
"als_d7_id_af_allnum",
|
||||
"als_d7_id_af_orgnum",
|
||||
"als_d7_id_coon_allnum",
|
||||
"als_d7_id_coon_orgnum",
|
||||
"als_d7_id_oth_allnum",
|
||||
"als_d7_id_oth_orgnum",
|
||||
"als_d7_id_bank_selfnum",
|
||||
"als_d7_id_bank_allnum",
|
||||
"als_d7_id_bank_tra_allnum",
|
||||
"als_d7_id_bank_ret_allnum",
|
||||
"als_d7_id_bank_orgnum",
|
||||
"als_d7_id_bank_tra_orgnum",
|
||||
"als_d7_id_bank_ret_orgnum",
|
||||
"als_d7_id_bank_week_allnum",
|
||||
"als_d7_id_bank_week_orgnum",
|
||||
"als_d7_id_bank_night_allnum",
|
||||
"als_d7_id_bank_night_orgnum",
|
||||
"als_d7_id_nbank_selfnum",
|
||||
"als_d7_id_nbank_allnum",
|
||||
"als_d7_id_nbank_p2p_allnum",
|
||||
"als_d7_id_nbank_mc_allnum",
|
||||
"als_d7_id_nbank_ca_allnum",
|
||||
"als_d7_id_nbank_cf_allnum",
|
||||
"als_d7_id_nbank_com_allnum",
|
||||
"als_d7_id_nbank_oth_allnum",
|
||||
"als_d7_id_nbank_nsloan_allnum",
|
||||
"als_d7_id_nbank_autofin_allnum",
|
||||
"als_d7_id_nbank_sloan_allnum",
|
||||
"als_d7_id_nbank_cons_allnum",
|
||||
"als_d7_id_nbank_finlea_allnum",
|
||||
"als_d7_id_nbank_else_allnum",
|
||||
"als_d7_id_nbank_orgnum",
|
||||
"als_d7_id_nbank_p2p_orgnum",
|
||||
"als_d7_id_nbank_mc_orgnum",
|
||||
"als_d7_id_nbank_ca_orgnum",
|
||||
"als_d7_id_nbank_cf_orgnum",
|
||||
"als_d7_id_nbank_com_orgnum",
|
||||
"als_d7_id_nbank_oth_orgnum",
|
||||
"als_d7_id_nbank_nsloan_orgnum",
|
||||
"als_d7_id_nbank_autofin_orgnum",
|
||||
"als_d7_id_nbank_sloan_orgnum",
|
||||
"als_d7_id_nbank_cons_orgnum",
|
||||
"als_d7_id_nbank_finlea_orgnum",
|
||||
"als_d7_id_nbank_else_orgnum",
|
||||
"als_d7_id_nbank_week_allnum",
|
||||
"als_d7_id_nbank_week_orgnum",
|
||||
"als_d7_id_nbank_night_allnum",
|
||||
"als_d7_id_nbank_night_orgnum",
|
||||
"als_d7_cell_pdl_allnum",
|
||||
"als_d7_cell_pdl_orgnum",
|
||||
"als_d7_cell_caon_allnum",
|
||||
"als_d7_cell_caon_orgnum",
|
||||
"als_d7_cell_rel_allnum",
|
||||
"als_d7_cell_rel_orgnum",
|
||||
"als_d7_cell_caoff_allnum",
|
||||
"als_d7_cell_caoff_orgnum",
|
||||
"als_d7_cell_cooff_allnum",
|
||||
"als_d7_cell_cooff_orgnum",
|
||||
"als_d7_cell_af_allnum",
|
||||
"als_d7_cell_af_orgnum",
|
||||
"als_d7_cell_coon_allnum",
|
||||
"als_d7_cell_coon_orgnum",
|
||||
"als_d7_cell_oth_allnum",
|
||||
"als_d7_cell_oth_orgnum",
|
||||
"als_d7_cell_bank_selfnum",
|
||||
"als_d7_cell_bank_allnum",
|
||||
"als_d7_cell_bank_tra_allnum",
|
||||
"als_d7_cell_bank_ret_allnum",
|
||||
"als_d7_cell_bank_orgnum",
|
||||
"als_d7_cell_bank_tra_orgnum",
|
||||
"als_d7_cell_bank_ret_orgnum",
|
||||
"als_d7_cell_bank_week_allnum",
|
||||
"als_d7_cell_bank_week_orgnum",
|
||||
"als_d7_cell_bank_night_allnum",
|
||||
"als_d7_cell_bank_night_orgnum",
|
||||
"als_d7_cell_nbank_selfnum",
|
||||
"als_d7_cell_nbank_allnum",
|
||||
"als_d7_cell_nbank_p2p_allnum",
|
||||
"als_d7_cell_nbank_mc_allnum",
|
||||
"als_d7_cell_nbank_ca_allnum",
|
||||
"als_d7_cell_nbank_cf_allnum",
|
||||
"als_d7_cell_nbank_com_allnum",
|
||||
"als_d7_cell_nbank_oth_allnum",
|
||||
"als_d7_cell_nbank_nsloan_allnum",
|
||||
"als_d7_cell_nbank_autofin_allnum",
|
||||
"als_d7_cell_nbank_sloan_allnum",
|
||||
"als_d7_cell_nbank_cons_allnum",
|
||||
"als_d7_cell_nbank_finlea_allnum",
|
||||
"als_d7_cell_nbank_else_allnum",
|
||||
"als_d7_cell_nbank_orgnum",
|
||||
"als_d7_cell_nbank_p2p_orgnum",
|
||||
"als_d7_cell_nbank_mc_orgnum",
|
||||
"als_d7_cell_nbank_ca_orgnum",
|
||||
"als_d7_cell_nbank_cf_orgnum",
|
||||
"als_d7_cell_nbank_com_orgnum",
|
||||
"als_d7_cell_nbank_oth_orgnum",
|
||||
"als_d7_cell_nbank_nsloan_orgnum",
|
||||
"als_d7_cell_nbank_autofin_orgnum",
|
||||
"als_d7_cell_nbank_sloan_orgnum",
|
||||
"als_d7_cell_nbank_cons_orgnum",
|
||||
"als_d7_cell_nbank_finlea_orgnum",
|
||||
"als_d7_cell_nbank_else_orgnum",
|
||||
"als_d7_cell_nbank_week_allnum",
|
||||
"als_d7_cell_nbank_week_orgnum",
|
||||
"als_d7_cell_nbank_night_allnum",
|
||||
"als_d7_cell_nbank_night_orgnum",
|
||||
"als_d15_id_pdl_allnum",
|
||||
"als_d15_id_pdl_orgnum",
|
||||
"als_d15_id_caon_allnum",
|
||||
"als_d15_id_caon_orgnum",
|
||||
"als_d15_id_rel_allnum",
|
||||
"als_d15_id_rel_orgnum",
|
||||
"als_d15_id_caoff_allnum",
|
||||
"als_d15_id_caoff_orgnum",
|
||||
"als_d15_id_cooff_allnum",
|
||||
"als_d15_id_cooff_orgnum",
|
||||
"als_d15_id_af_allnum",
|
||||
"als_d15_id_af_orgnum",
|
||||
"als_d15_id_coon_allnum",
|
||||
"als_d15_id_coon_orgnum",
|
||||
"als_d15_id_oth_allnum",
|
||||
"als_d15_id_oth_orgnum",
|
||||
"als_d15_id_bank_selfnum",
|
||||
"als_d15_id_bank_allnum",
|
||||
"als_d15_id_bank_tra_allnum",
|
||||
"als_d15_id_bank_ret_allnum",
|
||||
"als_d15_id_bank_orgnum",
|
||||
"als_d15_id_bank_tra_orgnum",
|
||||
"als_d15_id_bank_ret_orgnum",
|
||||
"als_d15_id_bank_week_allnum",
|
||||
"als_d15_id_bank_week_orgnum",
|
||||
"als_d15_id_bank_night_allnum",
|
||||
"als_d15_id_bank_night_orgnum",
|
||||
"als_d15_id_nbank_selfnum",
|
||||
"als_d15_id_nbank_allnum",
|
||||
"als_d15_id_nbank_p2p_allnum",
|
||||
"als_d15_id_nbank_mc_allnum",
|
||||
"als_d15_id_nbank_ca_allnum",
|
||||
"als_d15_id_nbank_cf_allnum",
|
||||
"als_d15_id_nbank_com_allnum",
|
||||
"als_d15_id_nbank_oth_allnum",
|
||||
"als_d15_id_nbank_nsloan_allnum",
|
||||
"als_d15_id_nbank_autofin_allnum",
|
||||
"als_d15_id_nbank_sloan_allnum",
|
||||
"als_d15_id_nbank_cons_allnum",
|
||||
"als_d15_id_nbank_finlea_allnum",
|
||||
"als_d15_id_nbank_else_allnum",
|
||||
"als_d15_id_nbank_orgnum",
|
||||
"als_d15_id_nbank_p2p_orgnum",
|
||||
"als_d15_id_nbank_mc_orgnum",
|
||||
"als_d15_id_nbank_ca_orgnum",
|
||||
"als_d15_id_nbank_cf_orgnum",
|
||||
"als_d15_id_nbank_com_orgnum",
|
||||
"als_d15_id_nbank_oth_orgnum",
|
||||
"als_d15_id_nbank_nsloan_orgnum",
|
||||
"als_d15_id_nbank_autofin_orgnum",
|
||||
"als_d15_id_nbank_sloan_orgnum",
|
||||
"als_d15_id_nbank_cons_orgnum",
|
||||
"als_d15_id_nbank_finlea_orgnum",
|
||||
"als_d15_id_nbank_else_orgnum",
|
||||
"als_d15_id_nbank_week_allnum",
|
||||
"als_d15_id_nbank_week_orgnum",
|
||||
"als_d15_id_nbank_night_allnum",
|
||||
"als_d15_id_nbank_night_orgnum",
|
||||
"als_d15_cell_pdl_allnum",
|
||||
"als_d15_cell_pdl_orgnum",
|
||||
"als_d15_cell_caon_allnum",
|
||||
"als_d15_cell_caon_orgnum",
|
||||
"als_d15_cell_rel_allnum",
|
||||
"als_d15_cell_rel_orgnum",
|
||||
"als_d15_cell_caoff_allnum",
|
||||
"als_d15_cell_caoff_orgnum",
|
||||
"als_d15_cell_cooff_allnum",
|
||||
"als_d15_cell_cooff_orgnum",
|
||||
"als_d15_cell_af_allnum",
|
||||
"als_d15_cell_af_orgnum",
|
||||
"als_d15_cell_coon_allnum",
|
||||
"als_d15_cell_coon_orgnum",
|
||||
"als_d15_cell_oth_allnum",
|
||||
"als_d15_cell_oth_orgnum",
|
||||
"als_d15_cell_bank_selfnum",
|
||||
"als_d15_cell_bank_allnum",
|
||||
"als_d15_cell_bank_tra_allnum",
|
||||
"als_d15_cell_bank_ret_allnum",
|
||||
"als_d15_cell_bank_orgnum",
|
||||
"als_d15_cell_bank_tra_orgnum",
|
||||
"als_d15_cell_bank_ret_orgnum",
|
||||
"als_d15_cell_bank_week_allnum",
|
||||
"als_d15_cell_bank_week_orgnum",
|
||||
"als_d15_cell_bank_night_allnum",
|
||||
"als_d15_cell_bank_night_orgnum",
|
||||
"als_d15_cell_nbank_selfnum",
|
||||
"als_d15_cell_nbank_allnum",
|
||||
"als_d15_cell_nbank_p2p_allnum",
|
||||
"als_d15_cell_nbank_mc_allnum",
|
||||
"als_d15_cell_nbank_ca_allnum",
|
||||
"als_d15_cell_nbank_cf_allnum",
|
||||
"als_d15_cell_nbank_com_allnum",
|
||||
"als_d15_cell_nbank_oth_allnum",
|
||||
"als_d15_cell_nbank_nsloan_allnum",
|
||||
"als_d15_cell_nbank_autofin_allnum",
|
||||
"als_d15_cell_nbank_sloan_allnum",
|
||||
"als_d15_cell_nbank_cons_allnum",
|
||||
"als_d15_cell_nbank_finlea_allnum",
|
||||
"als_d15_cell_nbank_else_allnum",
|
||||
"als_d15_cell_nbank_orgnum",
|
||||
"als_d15_cell_nbank_p2p_orgnum",
|
||||
"als_d15_cell_nbank_mc_orgnum",
|
||||
"als_d15_cell_nbank_ca_orgnum",
|
||||
"als_d15_cell_nbank_cf_orgnum",
|
||||
"als_d15_cell_nbank_com_orgnum",
|
||||
"als_d15_cell_nbank_oth_orgnum",
|
||||
"als_d15_cell_nbank_nsloan_orgnum",
|
||||
"als_d15_cell_nbank_autofin_orgnum",
|
||||
"als_d15_cell_nbank_sloan_orgnum",
|
||||
"als_d15_cell_nbank_cons_orgnum",
|
||||
"als_d15_cell_nbank_finlea_orgnum",
|
||||
"als_d15_cell_nbank_else_orgnum",
|
||||
"als_d15_cell_nbank_week_allnum",
|
||||
"als_d15_cell_nbank_week_orgnum",
|
||||
"als_d15_cell_nbank_night_allnum",
|
||||
"als_d15_cell_nbank_night_orgnum",
|
||||
"als_m1_id_pdl_allnum",
|
||||
"als_m1_id_pdl_orgnum",
|
||||
"als_m1_id_caon_allnum",
|
||||
"als_m1_id_caon_orgnum",
|
||||
"als_m1_id_rel_allnum",
|
||||
"als_m1_id_rel_orgnum",
|
||||
"als_m1_id_caoff_allnum",
|
||||
"als_m1_id_caoff_orgnum",
|
||||
"als_m1_id_cooff_allnum",
|
||||
"als_m1_id_cooff_orgnum",
|
||||
"als_m1_id_af_allnum",
|
||||
"als_m1_id_af_orgnum",
|
||||
"als_m1_id_coon_allnum",
|
||||
"als_m1_id_coon_orgnum",
|
||||
"als_m1_id_oth_allnum",
|
||||
"als_m1_id_oth_orgnum",
|
||||
"als_m1_id_bank_selfnum",
|
||||
"als_m1_id_bank_allnum",
|
||||
"als_m1_id_bank_tra_allnum",
|
||||
"als_m1_id_bank_ret_allnum",
|
||||
"als_m1_id_bank_orgnum",
|
||||
"als_m1_id_bank_tra_orgnum",
|
||||
"als_m1_id_bank_ret_orgnum",
|
||||
"als_m1_id_bank_week_allnum",
|
||||
"als_m1_id_bank_week_orgnum",
|
||||
"als_m1_id_bank_night_allnum",
|
||||
"als_m1_id_bank_night_orgnum",
|
||||
"als_m1_id_nbank_selfnum",
|
||||
"als_m1_id_nbank_allnum",
|
||||
"als_m1_id_nbank_p2p_allnum",
|
||||
"als_m1_id_nbank_mc_allnum",
|
||||
"als_m1_id_nbank_ca_allnum",
|
||||
"als_m1_id_nbank_cf_allnum",
|
||||
"als_m1_id_nbank_com_allnum",
|
||||
"als_m1_id_nbank_oth_allnum",
|
||||
"als_m1_id_nbank_nsloan_allnum",
|
||||
"als_m1_id_nbank_autofin_allnum",
|
||||
"als_m1_id_nbank_sloan_allnum",
|
||||
"als_m1_id_nbank_cons_allnum",
|
||||
"als_m1_id_nbank_finlea_allnum",
|
||||
"als_m1_id_nbank_else_allnum",
|
||||
"als_m1_id_nbank_orgnum",
|
||||
"als_m1_id_nbank_p2p_orgnum",
|
||||
"als_m1_id_nbank_mc_orgnum",
|
||||
"als_m1_id_nbank_ca_orgnum",
|
||||
"als_m1_id_nbank_cf_orgnum",
|
||||
"als_m1_id_nbank_com_orgnum",
|
||||
"als_m1_id_nbank_oth_orgnum",
|
||||
"als_m1_id_nbank_nsloan_orgnum",
|
||||
"als_m1_id_nbank_autofin_orgnum",
|
||||
"als_m1_id_nbank_sloan_orgnum",
|
||||
"als_m1_id_nbank_cons_orgnum",
|
||||
"als_m1_id_nbank_finlea_orgnum",
|
||||
"als_m1_id_nbank_else_orgnum",
|
||||
"als_m1_id_nbank_week_allnum",
|
||||
"als_m1_id_nbank_week_orgnum",
|
||||
"als_m1_id_nbank_night_allnum",
|
||||
"als_m1_id_nbank_night_orgnum",
|
||||
"als_m1_cell_pdl_allnum",
|
||||
"als_m1_cell_pdl_orgnum",
|
||||
"als_m1_cell_caon_allnum",
|
||||
"als_m1_cell_caon_orgnum",
|
||||
"als_m1_cell_rel_allnum",
|
||||
"als_m1_cell_rel_orgnum",
|
||||
"als_m1_cell_caoff_allnum",
|
||||
"als_m1_cell_caoff_orgnum",
|
||||
"als_m1_cell_cooff_allnum",
|
||||
"als_m1_cell_cooff_orgnum",
|
||||
"als_m1_cell_af_allnum",
|
||||
"als_m1_cell_af_orgnum",
|
||||
"als_m1_cell_coon_allnum",
|
||||
"als_m1_cell_coon_orgnum",
|
||||
"als_m1_cell_oth_allnum",
|
||||
"als_m1_cell_oth_orgnum",
|
||||
"als_m1_cell_bank_selfnum",
|
||||
"als_m1_cell_bank_allnum",
|
||||
"als_m1_cell_bank_tra_allnum",
|
||||
"als_m1_cell_bank_ret_allnum",
|
||||
"als_m1_cell_bank_orgnum",
|
||||
"als_m1_cell_bank_tra_orgnum",
|
||||
"als_m1_cell_bank_ret_orgnum",
|
||||
"als_m1_cell_bank_week_allnum",
|
||||
"als_m1_cell_bank_week_orgnum",
|
||||
"als_m1_cell_bank_night_allnum",
|
||||
"als_m1_cell_bank_night_orgnum",
|
||||
"als_m1_cell_nbank_selfnum",
|
||||
"als_m1_cell_nbank_allnum",
|
||||
"als_m1_cell_nbank_p2p_allnum",
|
||||
"als_m1_cell_nbank_mc_allnum",
|
||||
"als_m1_cell_nbank_ca_allnum",
|
||||
"als_m1_cell_nbank_cf_allnum",
|
||||
"als_m1_cell_nbank_com_allnum",
|
||||
"als_m1_cell_nbank_oth_allnum",
|
||||
"als_m1_cell_nbank_nsloan_allnum",
|
||||
"als_m1_cell_nbank_autofin_allnum",
|
||||
"als_m1_cell_nbank_sloan_allnum",
|
||||
"als_m1_cell_nbank_cons_allnum",
|
||||
"als_m1_cell_nbank_finlea_allnum",
|
||||
"als_m1_cell_nbank_else_allnum",
|
||||
"als_m1_cell_nbank_orgnum",
|
||||
"als_m1_cell_nbank_p2p_orgnum",
|
||||
"als_m1_cell_nbank_mc_orgnum",
|
||||
"als_m1_cell_nbank_ca_orgnum",
|
||||
"als_m1_cell_nbank_cf_orgnum",
|
||||
"als_m1_cell_nbank_com_orgnum",
|
||||
"als_m1_cell_nbank_oth_orgnum",
|
||||
"als_m1_cell_nbank_nsloan_orgnum",
|
||||
"als_m1_cell_nbank_autofin_orgnum",
|
||||
"als_m1_cell_nbank_sloan_orgnum",
|
||||
"als_m1_cell_nbank_cons_orgnum",
|
||||
"als_m1_cell_nbank_finlea_orgnum",
|
||||
"als_m1_cell_nbank_else_orgnum",
|
||||
"als_m1_cell_nbank_week_allnum",
|
||||
"als_m1_cell_nbank_week_orgnum",
|
||||
"als_m1_cell_nbank_night_allnum",
|
||||
"als_m1_cell_nbank_night_orgnum",
|
||||
"als_m3_id_max_inteday",
|
||||
"als_m3_id_min_inteday",
|
||||
"als_m3_id_tot_mons",
|
||||
"als_m3_id_avg_monnum",
|
||||
"als_m3_id_max_monnum",
|
||||
"als_m3_id_min_monnum",
|
||||
"als_m3_id_pdl_allnum",
|
||||
"als_m3_id_pdl_orgnum",
|
||||
"als_m3_id_caon_allnum",
|
||||
"als_m3_id_caon_orgnum",
|
||||
"als_m3_id_rel_allnum",
|
||||
"als_m3_id_rel_orgnum",
|
||||
"als_m3_id_caoff_allnum",
|
||||
"als_m3_id_caoff_orgnum",
|
||||
"als_m3_id_cooff_allnum",
|
||||
"als_m3_id_cooff_orgnum",
|
||||
"als_m3_id_af_allnum",
|
||||
"als_m3_id_af_orgnum",
|
||||
"als_m3_id_coon_allnum",
|
||||
"als_m3_id_coon_orgnum",
|
||||
"als_m3_id_oth_allnum",
|
||||
"als_m3_id_oth_orgnum",
|
||||
"als_m3_id_bank_selfnum",
|
||||
"als_m3_id_bank_allnum",
|
||||
"als_m3_id_bank_tra_allnum",
|
||||
"als_m3_id_bank_ret_allnum",
|
||||
"als_m3_id_bank_orgnum",
|
||||
"als_m3_id_bank_tra_orgnum",
|
||||
"als_m3_id_bank_ret_orgnum",
|
||||
"als_m3_id_bank_tot_mons",
|
||||
"als_m3_id_bank_avg_monnum",
|
||||
"als_m3_id_bank_max_monnum",
|
||||
"als_m3_id_bank_min_monnum",
|
||||
"als_m3_id_bank_max_inteday",
|
||||
"als_m3_id_bank_min_inteday",
|
||||
"als_m3_id_bank_week_allnum",
|
||||
"als_m3_id_bank_week_orgnum",
|
||||
"als_m3_id_bank_night_allnum",
|
||||
"als_m3_id_bank_night_orgnum",
|
||||
"als_m3_id_nbank_selfnum",
|
||||
"als_m3_id_nbank_allnum",
|
||||
"als_m3_id_nbank_p2p_allnum",
|
||||
"als_m3_id_nbank_mc_allnum",
|
||||
"als_m3_id_nbank_ca_allnum",
|
||||
"als_m3_id_nbank_cf_allnum",
|
||||
"als_m3_id_nbank_com_allnum",
|
||||
"als_m3_id_nbank_oth_allnum",
|
||||
"als_m3_id_nbank_nsloan_allnum",
|
||||
"als_m3_id_nbank_autofin_allnum",
|
||||
"als_m3_id_nbank_sloan_allnum",
|
||||
"als_m3_id_nbank_cons_allnum",
|
||||
"als_m3_id_nbank_finlea_allnum",
|
||||
"als_m3_id_nbank_else_allnum",
|
||||
"als_m3_id_nbank_orgnum",
|
||||
"als_m3_id_nbank_p2p_orgnum",
|
||||
"als_m3_id_nbank_mc_orgnum",
|
||||
"als_m3_id_nbank_ca_orgnum",
|
||||
"als_m3_id_nbank_cf_orgnum",
|
||||
"als_m3_id_nbank_com_orgnum",
|
||||
"als_m3_id_nbank_oth_orgnum",
|
||||
"als_m3_id_nbank_nsloan_orgnum",
|
||||
"als_m3_id_nbank_autofin_orgnum",
|
||||
"als_m3_id_nbank_sloan_orgnum",
|
||||
"als_m3_id_nbank_cons_orgnum",
|
||||
"als_m3_id_nbank_finlea_orgnum",
|
||||
"als_m3_id_nbank_else_orgnum",
|
||||
"als_m3_id_nbank_tot_mons",
|
||||
"als_m3_id_nbank_avg_monnum",
|
||||
"als_m3_id_nbank_max_monnum",
|
||||
"als_m3_id_nbank_min_monnum",
|
||||
"als_m3_id_nbank_max_inteday",
|
||||
"als_m3_id_nbank_min_inteday",
|
||||
"als_m3_id_nbank_week_allnum",
|
||||
"als_m3_id_nbank_week_orgnum",
|
||||
"als_m3_id_nbank_night_allnum",
|
||||
"als_m3_id_nbank_night_orgnum",
|
||||
"als_m3_cell_max_inteday",
|
||||
"als_m3_cell_min_inteday",
|
||||
"als_m3_cell_tot_mons",
|
||||
"als_m3_cell_avg_monnum",
|
||||
"als_m3_cell_max_monnum",
|
||||
"als_m3_cell_min_monnum",
|
||||
"als_m3_cell_pdl_allnum",
|
||||
"als_m3_cell_pdl_orgnum",
|
||||
"als_m3_cell_caon_allnum",
|
||||
"als_m3_cell_caon_orgnum",
|
||||
"als_m3_cell_rel_allnum",
|
||||
"als_m3_cell_rel_orgnum",
|
||||
"als_m3_cell_caoff_allnum",
|
||||
"als_m3_cell_caoff_orgnum",
|
||||
"als_m3_cell_cooff_allnum",
|
||||
"als_m3_cell_cooff_orgnum",
|
||||
"als_m3_cell_af_allnum",
|
||||
"als_m3_cell_af_orgnum",
|
||||
"als_m3_cell_coon_allnum",
|
||||
"als_m3_cell_coon_orgnum",
|
||||
"als_m3_cell_oth_allnum",
|
||||
"als_m3_cell_oth_orgnum",
|
||||
"als_m3_cell_bank_selfnum",
|
||||
"als_m3_cell_bank_allnum",
|
||||
"als_m3_cell_bank_tra_allnum",
|
||||
"als_m3_cell_bank_ret_allnum",
|
||||
"als_m3_cell_bank_orgnum",
|
||||
"als_m3_cell_bank_tra_orgnum",
|
||||
"als_m3_cell_bank_ret_orgnum",
|
||||
"als_m3_cell_bank_tot_mons",
|
||||
"als_m3_cell_bank_avg_monnum",
|
||||
"als_m3_cell_bank_max_monnum",
|
||||
"als_m3_cell_bank_min_monnum",
|
||||
"als_m3_cell_bank_max_inteday",
|
||||
"als_m3_cell_bank_min_inteday",
|
||||
"als_m3_cell_bank_week_allnum",
|
||||
"als_m3_cell_bank_week_orgnum",
|
||||
"als_m3_cell_bank_night_allnum",
|
||||
"als_m3_cell_bank_night_orgnum",
|
||||
"als_m3_cell_nbank_selfnum",
|
||||
"als_m3_cell_nbank_allnum",
|
||||
"als_m3_cell_nbank_p2p_allnum",
|
||||
"als_m3_cell_nbank_mc_allnum",
|
||||
"als_m3_cell_nbank_ca_allnum",
|
||||
"als_m3_cell_nbank_cf_allnum",
|
||||
"als_m3_cell_nbank_com_allnum",
|
||||
"als_m3_cell_nbank_oth_allnum",
|
||||
"als_m3_cell_nbank_nsloan_allnum",
|
||||
"als_m3_cell_nbank_autofin_allnum",
|
||||
"als_m3_cell_nbank_sloan_allnum",
|
||||
"als_m3_cell_nbank_cons_allnum",
|
||||
"als_m3_cell_nbank_finlea_allnum",
|
||||
"als_m3_cell_nbank_else_allnum",
|
||||
"als_m3_cell_nbank_orgnum",
|
||||
"als_m3_cell_nbank_p2p_orgnum",
|
||||
"als_m3_cell_nbank_mc_orgnum",
|
||||
"als_m3_cell_nbank_ca_orgnum",
|
||||
"als_m3_cell_nbank_cf_orgnum",
|
||||
"als_m3_cell_nbank_com_orgnum",
|
||||
"als_m3_cell_nbank_oth_orgnum",
|
||||
"als_m3_cell_nbank_nsloan_orgnum",
|
||||
"als_m3_cell_nbank_autofin_orgnum",
|
||||
"als_m3_cell_nbank_sloan_orgnum",
|
||||
"als_m3_cell_nbank_cons_orgnum",
|
||||
"als_m3_cell_nbank_finlea_orgnum",
|
||||
"als_m3_cell_nbank_else_orgnum",
|
||||
"als_m3_cell_nbank_tot_mons",
|
||||
"als_m3_cell_nbank_avg_monnum",
|
||||
"als_m3_cell_nbank_max_monnum",
|
||||
"als_m3_cell_nbank_min_monnum",
|
||||
"als_m3_cell_nbank_max_inteday",
|
||||
"als_m3_cell_nbank_min_inteday",
|
||||
"als_m3_cell_nbank_week_allnum",
|
||||
"als_m3_cell_nbank_week_orgnum",
|
||||
"als_m3_cell_nbank_night_allnum",
|
||||
"als_m3_cell_nbank_night_orgnum",
|
||||
"als_m6_id_max_inteday",
|
||||
"als_m6_id_min_inteday",
|
||||
"als_m6_id_tot_mons",
|
||||
"als_m6_id_avg_monnum",
|
||||
"als_m6_id_max_monnum",
|
||||
"als_m6_id_min_monnum",
|
||||
"als_m6_id_pdl_allnum",
|
||||
"als_m6_id_pdl_orgnum",
|
||||
"als_m6_id_caon_allnum",
|
||||
"als_m6_id_caon_orgnum",
|
||||
"als_m6_id_rel_allnum",
|
||||
"als_m6_id_rel_orgnum",
|
||||
"als_m6_id_caoff_allnum",
|
||||
"als_m6_id_caoff_orgnum",
|
||||
"als_m6_id_cooff_allnum",
|
||||
"als_m6_id_cooff_orgnum",
|
||||
"als_m6_id_af_allnum",
|
||||
"als_m6_id_af_orgnum",
|
||||
"als_m6_id_coon_allnum",
|
||||
"als_m6_id_coon_orgnum",
|
||||
"als_m6_id_oth_allnum",
|
||||
"als_m6_id_oth_orgnum",
|
||||
"als_m6_id_bank_selfnum",
|
||||
"als_m6_id_bank_allnum",
|
||||
"als_m6_id_bank_tra_allnum",
|
||||
"als_m6_id_bank_ret_allnum",
|
||||
"als_m6_id_bank_orgnum",
|
||||
"als_m6_id_bank_tra_orgnum",
|
||||
"als_m6_id_bank_ret_orgnum",
|
||||
"als_m6_id_bank_tot_mons",
|
||||
"als_m6_id_bank_avg_monnum",
|
||||
"als_m6_id_bank_max_monnum",
|
||||
"als_m6_id_bank_min_monnum",
|
||||
"als_m6_id_bank_max_inteday",
|
||||
"als_m6_id_bank_min_inteday",
|
||||
"als_m6_id_bank_week_allnum",
|
||||
"als_m6_id_bank_week_orgnum",
|
||||
"als_m6_id_bank_night_allnum",
|
||||
"als_m6_id_bank_night_orgnum",
|
||||
"als_m6_id_nbank_selfnum",
|
||||
"als_m6_id_nbank_allnum",
|
||||
"als_m6_id_nbank_p2p_allnum",
|
||||
"als_m6_id_nbank_mc_allnum",
|
||||
"als_m6_id_nbank_ca_allnum",
|
||||
"als_m6_id_nbank_cf_allnum",
|
||||
"als_m6_id_nbank_com_allnum",
|
||||
"als_m6_id_nbank_oth_allnum",
|
||||
"als_m6_id_nbank_nsloan_allnum",
|
||||
"als_m6_id_nbank_autofin_allnum",
|
||||
"als_m6_id_nbank_sloan_allnum",
|
||||
"als_m6_id_nbank_cons_allnum",
|
||||
"als_m6_id_nbank_finlea_allnum",
|
||||
"als_m6_id_nbank_else_allnum",
|
||||
"als_m6_id_nbank_orgnum",
|
||||
"als_m6_id_nbank_p2p_orgnum",
|
||||
"als_m6_id_nbank_mc_orgnum",
|
||||
"als_m6_id_nbank_ca_orgnum",
|
||||
"als_m6_id_nbank_cf_orgnum",
|
||||
"als_m6_id_nbank_com_orgnum",
|
||||
"als_m6_id_nbank_oth_orgnum",
|
||||
"als_m6_id_nbank_nsloan_orgnum",
|
||||
"als_m6_id_nbank_autofin_orgnum",
|
||||
"als_m6_id_nbank_sloan_orgnum",
|
||||
"als_m6_id_nbank_cons_orgnum",
|
||||
"als_m6_id_nbank_finlea_orgnum",
|
||||
"als_m6_id_nbank_else_orgnum",
|
||||
"als_m6_id_nbank_tot_mons",
|
||||
"als_m6_id_nbank_avg_monnum",
|
||||
"als_m6_id_nbank_max_monnum",
|
||||
"als_m6_id_nbank_min_monnum",
|
||||
"als_m6_id_nbank_max_inteday",
|
||||
"als_m6_id_nbank_min_inteday",
|
||||
"als_m6_id_nbank_week_allnum",
|
||||
"als_m6_id_nbank_week_orgnum",
|
||||
"als_m6_id_nbank_night_allnum",
|
||||
"als_m6_id_nbank_night_orgnum",
|
||||
"als_m6_cell_max_inteday",
|
||||
"als_m6_cell_min_inteday",
|
||||
"als_m6_cell_tot_mons",
|
||||
"als_m6_cell_avg_monnum",
|
||||
"als_m6_cell_max_monnum",
|
||||
"als_m6_cell_min_monnum",
|
||||
"als_m6_cell_pdl_allnum",
|
||||
"als_m6_cell_pdl_orgnum",
|
||||
"als_m6_cell_caon_allnum",
|
||||
"als_m6_cell_caon_orgnum",
|
||||
"als_m6_cell_rel_allnum",
|
||||
"als_m6_cell_rel_orgnum",
|
||||
"als_m6_cell_caoff_allnum",
|
||||
"als_m6_cell_caoff_orgnum",
|
||||
"als_m6_cell_cooff_allnum",
|
||||
"als_m6_cell_cooff_orgnum",
|
||||
"als_m6_cell_af_allnum",
|
||||
"als_m6_cell_af_orgnum",
|
||||
"als_m6_cell_coon_allnum",
|
||||
"als_m6_cell_coon_orgnum",
|
||||
"als_m6_cell_oth_allnum",
|
||||
"als_m6_cell_oth_orgnum",
|
||||
"als_m6_cell_bank_selfnum",
|
||||
"als_m6_cell_bank_allnum",
|
||||
"als_m6_cell_bank_tra_allnum",
|
||||
"als_m6_cell_bank_ret_allnum",
|
||||
"als_m6_cell_bank_orgnum",
|
||||
"als_m6_cell_bank_tra_orgnum",
|
||||
"als_m6_cell_bank_ret_orgnum",
|
||||
"als_m6_cell_bank_tot_mons",
|
||||
"als_m6_cell_bank_avg_monnum",
|
||||
"als_m6_cell_bank_max_monnum",
|
||||
"als_m6_cell_bank_min_monnum",
|
||||
"als_m6_cell_bank_max_inteday",
|
||||
"als_m6_cell_bank_min_inteday",
|
||||
"als_m6_cell_bank_week_allnum",
|
||||
"als_m6_cell_bank_week_orgnum",
|
||||
"als_m6_cell_bank_night_allnum",
|
||||
"als_m6_cell_bank_night_orgnum",
|
||||
"als_m6_cell_nbank_selfnum",
|
||||
"als_m6_cell_nbank_allnum",
|
||||
"als_m6_cell_nbank_p2p_allnum",
|
||||
"als_m6_cell_nbank_mc_allnum",
|
||||
"als_m6_cell_nbank_ca_allnum",
|
||||
"als_m6_cell_nbank_cf_allnum",
|
||||
"als_m6_cell_nbank_com_allnum",
|
||||
"als_m6_cell_nbank_oth_allnum",
|
||||
"als_m6_cell_nbank_nsloan_allnum",
|
||||
"als_m6_cell_nbank_autofin_allnum",
|
||||
"als_m6_cell_nbank_sloan_allnum",
|
||||
"als_m6_cell_nbank_cons_allnum",
|
||||
"als_m6_cell_nbank_finlea_allnum",
|
||||
"als_m6_cell_nbank_else_allnum",
|
||||
"als_m6_cell_nbank_orgnum",
|
||||
"als_m6_cell_nbank_p2p_orgnum",
|
||||
"als_m6_cell_nbank_mc_orgnum",
|
||||
"als_m6_cell_nbank_ca_orgnum",
|
||||
"als_m6_cell_nbank_cf_orgnum",
|
||||
"als_m6_cell_nbank_com_orgnum",
|
||||
"als_m6_cell_nbank_oth_orgnum",
|
||||
"als_m6_cell_nbank_nsloan_orgnum",
|
||||
"als_m6_cell_nbank_autofin_orgnum",
|
||||
"als_m6_cell_nbank_sloan_orgnum",
|
||||
"als_m6_cell_nbank_cons_orgnum",
|
||||
"als_m6_cell_nbank_finlea_orgnum",
|
||||
"als_m6_cell_nbank_else_orgnum",
|
||||
"als_m6_cell_nbank_tot_mons",
|
||||
"als_m6_cell_nbank_avg_monnum",
|
||||
"als_m6_cell_nbank_max_monnum",
|
||||
"als_m6_cell_nbank_min_monnum",
|
||||
"als_m6_cell_nbank_max_inteday",
|
||||
"als_m6_cell_nbank_min_inteday",
|
||||
"als_m6_cell_nbank_week_allnum",
|
||||
"als_m6_cell_nbank_week_orgnum",
|
||||
"als_m6_cell_nbank_night_allnum",
|
||||
"als_m6_cell_nbank_night_orgnum",
|
||||
"als_m12_id_max_inteday",
|
||||
"als_m12_id_min_inteday",
|
||||
"als_m12_id_tot_mons",
|
||||
"als_m12_id_avg_monnum",
|
||||
"als_m12_id_max_monnum",
|
||||
"als_m12_id_min_monnum",
|
||||
"als_m12_id_pdl_allnum",
|
||||
"als_m12_id_pdl_orgnum",
|
||||
"als_m12_id_caon_allnum",
|
||||
"als_m12_id_caon_orgnum",
|
||||
"als_m12_id_rel_allnum",
|
||||
"als_m12_id_rel_orgnum",
|
||||
"als_m12_id_caoff_allnum",
|
||||
"als_m12_id_caoff_orgnum",
|
||||
"als_m12_id_cooff_allnum",
|
||||
"als_m12_id_cooff_orgnum",
|
||||
"als_m12_id_af_allnum",
|
||||
"als_m12_id_af_orgnum",
|
||||
"als_m12_id_coon_allnum",
|
||||
"als_m12_id_coon_orgnum",
|
||||
"als_m12_id_oth_allnum",
|
||||
"als_m12_id_oth_orgnum",
|
||||
"als_m12_id_bank_selfnum",
|
||||
"als_m12_id_bank_allnum",
|
||||
"als_m12_id_bank_tra_allnum",
|
||||
"als_m12_id_bank_ret_allnum",
|
||||
"als_m12_id_bank_orgnum",
|
||||
"als_m12_id_bank_tra_orgnum",
|
||||
"als_m12_id_bank_ret_orgnum",
|
||||
"als_m12_id_bank_tot_mons",
|
||||
"als_m12_id_bank_avg_monnum",
|
||||
"als_m12_id_bank_max_monnum",
|
||||
"als_m12_id_bank_min_monnum",
|
||||
"als_m12_id_bank_max_inteday",
|
||||
"als_m12_id_bank_min_inteday",
|
||||
"als_m12_id_bank_week_allnum",
|
||||
"als_m12_id_bank_week_orgnum",
|
||||
"als_m12_id_bank_night_allnum",
|
||||
"als_m12_id_bank_night_orgnum",
|
||||
"als_m12_id_nbank_selfnum",
|
||||
"als_m12_id_nbank_allnum",
|
||||
"als_m12_id_nbank_p2p_allnum",
|
||||
"als_m12_id_nbank_mc_allnum",
|
||||
"als_m12_id_nbank_ca_allnum",
|
||||
"als_m12_id_nbank_cf_allnum",
|
||||
"als_m12_id_nbank_com_allnum",
|
||||
"als_m12_id_nbank_oth_allnum",
|
||||
"als_m12_id_nbank_nsloan_allnum",
|
||||
"als_m12_id_nbank_autofin_allnum",
|
||||
"als_m12_id_nbank_sloan_allnum",
|
||||
"als_m12_id_nbank_cons_allnum",
|
||||
"als_m12_id_nbank_finlea_allnum",
|
||||
"als_m12_id_nbank_else_allnum",
|
||||
"als_m12_id_nbank_orgnum",
|
||||
"als_m12_id_nbank_p2p_orgnum",
|
||||
"als_m12_id_nbank_mc_orgnum",
|
||||
"als_m12_id_nbank_ca_orgnum",
|
||||
"als_m12_id_nbank_cf_orgnum",
|
||||
"als_m12_id_nbank_com_orgnum",
|
||||
"als_m12_id_nbank_oth_orgnum",
|
||||
"als_m12_id_nbank_nsloan_orgnum",
|
||||
"als_m12_id_nbank_autofin_orgnum",
|
||||
"als_m12_id_nbank_sloan_orgnum",
|
||||
"als_m12_id_nbank_cons_orgnum",
|
||||
"als_m12_id_nbank_finlea_orgnum",
|
||||
"als_m12_id_nbank_else_orgnum",
|
||||
"als_m12_id_nbank_tot_mons",
|
||||
"als_m12_id_nbank_avg_monnum",
|
||||
"als_m12_id_nbank_max_monnum",
|
||||
"als_m12_id_nbank_min_monnum",
|
||||
"als_m12_id_nbank_max_inteday",
|
||||
"als_m12_id_nbank_min_inteday",
|
||||
"als_m12_id_nbank_week_allnum",
|
||||
"als_m12_id_nbank_week_orgnum",
|
||||
"als_m12_id_nbank_night_allnum",
|
||||
"als_m12_id_nbank_night_orgnum",
|
||||
"als_m12_cell_max_inteday",
|
||||
"als_m12_cell_min_inteday",
|
||||
"als_m12_cell_tot_mons",
|
||||
"als_m12_cell_avg_monnum",
|
||||
"als_m12_cell_max_monnum",
|
||||
"als_m12_cell_min_monnum",
|
||||
"als_m12_cell_pdl_allnum",
|
||||
"als_m12_cell_pdl_orgnum",
|
||||
"als_m12_cell_caon_allnum",
|
||||
"als_m12_cell_caon_orgnum",
|
||||
"als_m12_cell_rel_allnum",
|
||||
"als_m12_cell_rel_orgnum",
|
||||
"als_m12_cell_caoff_allnum",
|
||||
"als_m12_cell_caoff_orgnum",
|
||||
"als_m12_cell_cooff_allnum",
|
||||
"als_m12_cell_cooff_orgnum",
|
||||
"als_m12_cell_af_allnum",
|
||||
"als_m12_cell_af_orgnum",
|
||||
"als_m12_cell_coon_allnum",
|
||||
"als_m12_cell_coon_orgnum",
|
||||
"als_m12_cell_oth_allnum",
|
||||
"als_m12_cell_oth_orgnum",
|
||||
"als_m12_cell_bank_selfnum",
|
||||
"als_m12_cell_bank_allnum",
|
||||
"als_m12_cell_bank_tra_allnum",
|
||||
"als_m12_cell_bank_ret_allnum",
|
||||
"als_m12_cell_bank_orgnum",
|
||||
"als_m12_cell_bank_tra_orgnum",
|
||||
"als_m12_cell_bank_ret_orgnum",
|
||||
"als_m12_cell_bank_tot_mons",
|
||||
"als_m12_cell_bank_avg_monnum",
|
||||
"als_m12_cell_bank_max_monnum",
|
||||
"als_m12_cell_bank_min_monnum",
|
||||
"als_m12_cell_bank_max_inteday",
|
||||
"als_m12_cell_bank_min_inteday",
|
||||
"als_m12_cell_bank_week_allnum",
|
||||
"als_m12_cell_bank_week_orgnum",
|
||||
"als_m12_cell_bank_night_allnum",
|
||||
"als_m12_cell_bank_night_orgnum",
|
||||
"als_m12_cell_nbank_selfnum",
|
||||
"als_m12_cell_nbank_allnum",
|
||||
"als_m12_cell_nbank_p2p_allnum",
|
||||
"als_m12_cell_nbank_mc_allnum",
|
||||
"als_m12_cell_nbank_ca_allnum",
|
||||
"als_m12_cell_nbank_cf_allnum",
|
||||
"als_m12_cell_nbank_com_allnum",
|
||||
"als_m12_cell_nbank_oth_allnum",
|
||||
"als_m12_cell_nbank_nsloan_allnum",
|
||||
"als_m12_cell_nbank_autofin_allnum",
|
||||
"als_m12_cell_nbank_sloan_allnum",
|
||||
"als_m12_cell_nbank_cons_allnum",
|
||||
"als_m12_cell_nbank_finlea_allnum",
|
||||
"als_m12_cell_nbank_else_allnum",
|
||||
"als_m12_cell_nbank_orgnum",
|
||||
"als_m12_cell_nbank_p2p_orgnum",
|
||||
"als_m12_cell_nbank_mc_orgnum",
|
||||
"als_m12_cell_nbank_ca_orgnum",
|
||||
"als_m12_cell_nbank_cf_orgnum",
|
||||
"als_m12_cell_nbank_com_orgnum",
|
||||
"als_m12_cell_nbank_oth_orgnum",
|
||||
"als_m12_cell_nbank_nsloan_orgnum",
|
||||
"als_m12_cell_nbank_autofin_orgnum",
|
||||
"als_m12_cell_nbank_sloan_orgnum",
|
||||
"als_m12_cell_nbank_cons_orgnum",
|
||||
"als_m12_cell_nbank_finlea_orgnum",
|
||||
"als_m12_cell_nbank_else_orgnum",
|
||||
"als_m12_cell_nbank_tot_mons",
|
||||
"als_m12_cell_nbank_avg_monnum",
|
||||
"als_m12_cell_nbank_max_monnum",
|
||||
"als_m12_cell_nbank_min_monnum",
|
||||
"als_m12_cell_nbank_max_inteday",
|
||||
"als_m12_cell_nbank_min_inteday",
|
||||
"als_m12_cell_nbank_week_allnum",
|
||||
"als_m12_cell_nbank_week_orgnum",
|
||||
"als_m12_cell_nbank_night_allnum",
|
||||
"als_m12_cell_nbank_night_orgnum",
|
||||
"als_fst_id_bank_inteday",
|
||||
"als_fst_id_nbank_inteday",
|
||||
"als_fst_cell_bank_inteday",
|
||||
"als_fst_cell_nbank_inteday",
|
||||
"als_lst_id_bank_inteday",
|
||||
"als_lst_id_bank_consnum",
|
||||
"als_lst_id_bank_csinteday",
|
||||
"als_lst_id_nbank_inteday",
|
||||
"als_lst_id_nbank_consnum",
|
||||
"als_lst_id_nbank_csinteday",
|
||||
"als_lst_cell_bank_inteday",
|
||||
"als_lst_cell_bank_consnum",
|
||||
"als_lst_cell_bank_csinteday",
|
||||
"als_lst_cell_nbank_inteday",
|
||||
"als_lst_cell_nbank_consnum",
|
||||
"als_lst_cell_nbank_csinteday",
|
||||
}
|
||||
|
||||
var jrzq6F2AKeySet = func() map[string]struct{} {
|
||||
m := make(map[string]struct{}, len(jrzq6F2AVariableKeys))
|
||||
for _, key := range jrzq6F2AVariableKeys {
|
||||
m[key] = struct{}{}
|
||||
}
|
||||
return m
|
||||
}()
|
||||
@@ -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/xingwei"
|
||||
"tyapi-server/internal/infrastructure/external/zhicha"
|
||||
)
|
||||
|
||||
// ProcessJRZQ6F2ARequest JRZQ6F2A API处理方法 - 借贷申请记录
|
||||
@@ -21,27 +22,196 @@ func ProcessJRZQ6F2ARequest(ctx context.Context, params []byte, deps *processors
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
|
||||
// 构建请求数据,将项目规范的字段名转换为 XingweiService 需要的字段名
|
||||
reqData := map[string]interface{}{
|
||||
"name": paramsDto.Name,
|
||||
"idCardNum": paramsDto.IDCard,
|
||||
"phoneNumber": paramsDto.MobileNo,
|
||||
encryptedName, err := deps.ZhichaService.Encrypt(paramsDto.Name)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
// 调用行为数据API,使用指定的project_id
|
||||
projectID := "CDJ-1101695369065984000"
|
||||
respBytes, err := deps.XingweiService.CallAPI(ctx, projectID, reqData)
|
||||
encryptedIDCard, err := deps.ZhichaService.Encrypt(paramsDto.IDCard)
|
||||
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)
|
||||
}
|
||||
|
||||
encryptedMobileNo, err := deps.ZhichaService.Encrypt(paramsDto.MobileNo)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
reqData := map[string]interface{}{
|
||||
"name": encryptedName,
|
||||
"idCard": encryptedIDCard,
|
||||
"phone": encryptedMobileNo,
|
||||
"authorized": "1",
|
||||
}
|
||||
|
||||
respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI017", 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)
|
||||
}
|
||||
}
|
||||
|
||||
respMap, ok := respData.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, errors.Join(processors.ErrSystem, errors.New("响应格式错误"))
|
||||
}
|
||||
|
||||
result := mapJRZQ3C7BToJRZQ6F2A(respMap)
|
||||
|
||||
respBytes, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
return respBytes, nil
|
||||
}
|
||||
|
||||
func mapJRZQ3C7BToJRZQ6F2A(src map[string]interface{}) map[string]interface{} {
|
||||
variableValue := buildDefaultVariableValue()
|
||||
|
||||
// 如果源已经是平铺字段,优先直接覆盖,兼容不同返回形态。
|
||||
copyDirectFlattenFields(variableValue, src)
|
||||
|
||||
periods := []string{"d7", "d15", "m1", "m3", "m6", "m12"}
|
||||
for _, period := range periods {
|
||||
periodData := asMap(src[period])
|
||||
if len(periodData) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, scope := range []string{"id", "cell"} {
|
||||
scopeData := asMap(periodData[scope])
|
||||
if len(scopeData) == 0 {
|
||||
continue
|
||||
}
|
||||
flattenPeriodScope(variableValue, period, scope, scopeData)
|
||||
}
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"risk_screen_v2": map[string]interface{}{
|
||||
"fulinHitFlag": 1,
|
||||
"models": []interface{}{},
|
||||
"variables": []interface{}{map[string]interface{}{"variableName": "bairong_applyloan_extend", "variableValue": variableValue}},
|
||||
"code": "OK",
|
||||
"decision": "accept",
|
||||
"propertyValidations": []interface{}{},
|
||||
"strategies": []interface{}{},
|
||||
"scenes": []interface{}{},
|
||||
"validateInfo": map[string]interface{}{"productCodes": []interface{}{}},
|
||||
"id": "",
|
||||
"message": "业务处理成功!",
|
||||
"knowledge": map[string]interface{}{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func flattenPeriodScope(target map[string]interface{}, period, scope string, scopeData map[string]interface{}) {
|
||||
basePrefix := "als_" + period + "_" + scope + "_"
|
||||
|
||||
// 先处理 scope 级基础字段(例如 tot_mons/max_monnum/min_monnum/avg_monnum)
|
||||
copyScalarFields(target, basePrefix, scopeData)
|
||||
|
||||
for key, raw := range scopeData {
|
||||
child := asMap(raw)
|
||||
if len(child) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
sectionPrefix := basePrefix + key + "_"
|
||||
copyScalarFields(target, sectionPrefix, child)
|
||||
|
||||
// 对周末字段做兼容命名映射
|
||||
copyAliasIfPresent(target, sectionPrefix, child, "weekend_allnum", "week_allnum")
|
||||
copyAliasIfPresent(target, sectionPrefix, child, "weekend_orgnum", "week_orgnum")
|
||||
|
||||
// 对 top_* 与 *_d 字段做兜底映射,尽可能补齐常用 allnum/orgnum
|
||||
if _, ok := target[sectionPrefix+"allnum"]; !ok {
|
||||
copyAliasIfPresent(target, sectionPrefix, child, "top_allnum", "allnum")
|
||||
copyAliasIfPresent(target, sectionPrefix, child, "allnum_d", "allnum")
|
||||
}
|
||||
if _, ok := target[sectionPrefix+"orgnum"]; !ok {
|
||||
copyAliasIfPresent(target, sectionPrefix, child, "top_orgnum", "orgnum")
|
||||
copyAliasIfPresent(target, sectionPrefix, child, "orgnum_d", "orgnum")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func copyScalarFields(target map[string]interface{}, prefix string, src map[string]interface{}) {
|
||||
for k, v := range src {
|
||||
if isScalar(v) {
|
||||
setVariableField(target, prefix+normalizeMetricName(k), v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func copyAliasIfPresent(target map[string]interface{}, prefix string, src map[string]interface{}, from, to string) {
|
||||
if v, ok := src[from]; ok && isScalar(v) {
|
||||
setVariableField(target, prefix+to, v)
|
||||
}
|
||||
}
|
||||
|
||||
func copyDirectFlattenFields(target map[string]interface{}, src map[string]interface{}) {
|
||||
for k, v := range src {
|
||||
if !isScalar(v) {
|
||||
continue
|
||||
}
|
||||
// 允许直接覆盖文档字段以及兼容字段
|
||||
setVariableField(target, k, v)
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeMetricName(name string) string {
|
||||
switch name {
|
||||
case "weekend_allnum":
|
||||
return "week_allnum"
|
||||
case "weekend_orgnum":
|
||||
return "week_orgnum"
|
||||
default:
|
||||
return strings.TrimSpace(name)
|
||||
}
|
||||
}
|
||||
|
||||
func isScalar(v interface{}) bool {
|
||||
switch v.(type) {
|
||||
case nil:
|
||||
return false
|
||||
case string, bool, float64, int, int32, int64, uint, uint32, uint64:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func asMap(v interface{}) map[string]interface{} {
|
||||
if v == nil {
|
||||
return map[string]interface{}{}
|
||||
}
|
||||
if m, ok := v.(map[string]interface{}); ok {
|
||||
return m
|
||||
}
|
||||
return map[string]interface{}{}
|
||||
}
|
||||
|
||||
func buildDefaultVariableValue() map[string]interface{} {
|
||||
m := make(map[string]interface{}, len(jrzq6F2AVariableKeys)+3)
|
||||
for _, key := range jrzq6F2AVariableKeys {
|
||||
m[key] = ""
|
||||
}
|
||||
|
||||
// 兼容历史示例中出现的附加字段
|
||||
m["als_Flag_applyloanstr"] = "1"
|
||||
m["code"] = "00"
|
||||
m["swift_number"] = ""
|
||||
m["flag_applyloanstr"] = "1"
|
||||
return m
|
||||
}
|
||||
|
||||
func setVariableField(target map[string]interface{}, key string, value interface{}) {
|
||||
_, inDoc := jrzq6F2AKeySet[key]
|
||||
if inDoc || key == "als_Flag_applyloanstr" || key == "code" || key == "swift_number" {
|
||||
target[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,10 +4,13 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"math"
|
||||
"strconv"
|
||||
"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/zhicha"
|
||||
)
|
||||
|
||||
// ProcessJRZQ8B3CRequest JRZQ8B3C API处理方法 - 个人消费能力等级
|
||||
@@ -21,27 +24,173 @@ func ProcessJRZQ8B3CRequest(ctx context.Context, params []byte, deps *processors
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
|
||||
// 构建请求数据,将项目规范的字段名转换为 XingweiService 需要的字段名
|
||||
reqData := map[string]interface{}{
|
||||
"name": paramsDto.Name,
|
||||
"idCardNum": paramsDto.IDCard,
|
||||
"phoneNumber": paramsDto.MobileNo,
|
||||
encryptedName, err := deps.ZhichaService.Encrypt(paramsDto.Name)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
// 调用行为数据API,使用指定的project_id
|
||||
projectID := "CDJ-1101695392528920576"
|
||||
respBytes, err := deps.XingweiService.CallAPI(ctx, projectID, reqData)
|
||||
encryptedIDCard, err := deps.ZhichaService.Encrypt(paramsDto.IDCard)
|
||||
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)
|
||||
}
|
||||
|
||||
encryptedMobileNo, err := deps.ZhichaService.Encrypt(paramsDto.MobileNo)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
reqData := map[string]interface{}{
|
||||
"name": encryptedName,
|
||||
"idCard": encryptedIDCard,
|
||||
"phone": encryptedMobileNo,
|
||||
"authorized": "1",
|
||||
}
|
||||
|
||||
respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI034", 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)
|
||||
}
|
||||
}
|
||||
|
||||
personIncomeIndex := "-1"
|
||||
if m, ok := respData.(map[string]interface{}); ok {
|
||||
personIncomeIndex = mapTap010ToIncomeIndex(m["tap010"], paramsDto.IDCard)
|
||||
}
|
||||
|
||||
respPayload := map[string]interface{}{
|
||||
"personincome_index_2.0": personIncomeIndex,
|
||||
}
|
||||
|
||||
respBytes, err := json.Marshal(respPayload)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
return respBytes, nil
|
||||
}
|
||||
|
||||
type incomeTier struct {
|
||||
Score int
|
||||
Low float64
|
||||
High float64 // 上界闭区间;math.Inf(1) 表示正无穷
|
||||
}
|
||||
|
||||
var incomeTiers = []incomeTier{
|
||||
{Score: 100, Low: 1000, High: 2000},
|
||||
{Score: 200, Low: 2000, High: 4000},
|
||||
{Score: 300, Low: 4000, High: 6000},
|
||||
{Score: 400, Low: 6000, High: 8000},
|
||||
{Score: 500, Low: 8000, High: 10000},
|
||||
{Score: 600, Low: 10000, High: 12000},
|
||||
{Score: 700, Low: 12000, High: 15000},
|
||||
{Score: 800, Low: 15000, High: 20000},
|
||||
{Score: 900, Low: 20000, High: 25000},
|
||||
{Score: 1000, Low: 25000, High: math.Inf(1)},
|
||||
}
|
||||
|
||||
func mapTap010ToIncomeIndex(rawTap010 interface{}, idCard string) string {
|
||||
tap010, ok := parseTap010Level(rawTap010)
|
||||
if !ok {
|
||||
return "-1"
|
||||
}
|
||||
|
||||
mappedLow, mappedHigh := expandTap010Range(tap010)
|
||||
candidateScores := intersectedTierScores(mappedLow, mappedHigh)
|
||||
if len(candidateScores) == 0 {
|
||||
return "-1"
|
||||
}
|
||||
|
||||
seed := stableSeedFromIDCard(idCard)
|
||||
score := candidateScores[seed%len(candidateScores)]
|
||||
return strconv.Itoa(score)
|
||||
}
|
||||
|
||||
func parseTap010Level(v interface{}) (int, bool) {
|
||||
switch value := v.(type) {
|
||||
case string:
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
return 0, false
|
||||
}
|
||||
n, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return 0, false
|
||||
}
|
||||
if n < 1 || n > 4 {
|
||||
return 0, false
|
||||
}
|
||||
return n, true
|
||||
case float64:
|
||||
n := int(value)
|
||||
if value != float64(n) || n < 1 || n > 4 {
|
||||
return 0, false
|
||||
}
|
||||
return n, true
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
|
||||
func expandTap010Range(level int) (float64, float64) {
|
||||
// tap010 原区间:
|
||||
// 1:(0,500) 2:[500,1000) 3:[1000,3000) 4:[3000,+inf)
|
||||
// 按比例放大 9 倍映射到收入尺度,满足示例: (0,500)->(0,4500)
|
||||
switch level {
|
||||
case 1:
|
||||
return 0, 4500
|
||||
case 2:
|
||||
return 4500, 9000
|
||||
case 3:
|
||||
return 9000, 27000
|
||||
case 4:
|
||||
return 27000, math.Inf(1)
|
||||
default:
|
||||
return 0, 0
|
||||
}
|
||||
}
|
||||
|
||||
func intersectedTierScores(low, high float64) []int {
|
||||
scores := make([]int, 0, len(incomeTiers))
|
||||
for _, t := range incomeTiers {
|
||||
if isRangeIntersect(low, high, t.Low, t.High) {
|
||||
scores = append(scores, t.Score)
|
||||
}
|
||||
}
|
||||
return scores
|
||||
}
|
||||
|
||||
func isRangeIntersect(aLow, aHigh, bLow, bHigh float64) bool {
|
||||
return aLow <= bHigh && bLow <= aHigh
|
||||
}
|
||||
|
||||
func stableSeedFromIDCard(idCard string) int {
|
||||
if len(idCard) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
runes := []rune(idCard)
|
||||
start := len(runes) - 4
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
|
||||
seed := 0
|
||||
for _, r := range runes[start:] {
|
||||
switch {
|
||||
case r >= '0' && r <= '9':
|
||||
seed = seed*11 + int(r-'0')
|
||||
case r == 'X' || r == 'x':
|
||||
seed = seed*11 + 10
|
||||
default:
|
||||
seed = seed*11 + int(r)%11
|
||||
}
|
||||
}
|
||||
|
||||
if seed < 0 {
|
||||
return -seed
|
||||
}
|
||||
return seed
|
||||
}
|
||||
|
||||
@@ -47,8 +47,6 @@ func ProcessJRZQO7L1Request(ctx context.Context, params []byte, deps *processors
|
||||
"city": null,
|
||||
}
|
||||
|
||||
// 使用 WithSkipCode201Check 不跳过 201 错误检查,当 Code == "201" 时返回错误
|
||||
// ctx = zhicha.WithSkipCode201Check(ctx)
|
||||
respData, err := deps.ZhichaService.CallAPI(ctx, "ZCI080", reqData)
|
||||
if err != nil {
|
||||
if errors.Is(err, zhicha.ErrDatasource) {
|
||||
|
||||
@@ -75,6 +75,5 @@ func ProcessQCXGGB2QRequest(ctx context.Context, params []byte, deps *processors
|
||||
}
|
||||
}
|
||||
|
||||
// 极光服务已经返回了 data 字段的 JSON,直接返回即可
|
||||
return respBytes, nil
|
||||
}
|
||||
|
||||
@@ -31,6 +31,9 @@ func ProcessQYGL2S0WRequest(ctx context.Context, params []byte, deps *processors
|
||||
fmt.Print("个人身份证件号不能为空")
|
||||
return nil, fmt.Errorf("%s: %w", processors.ErrInvalidParam, errors.New("当失信被执行人类型为个人时,身份证件号不能为空"))
|
||||
}
|
||||
if paramsDto.IDCard == "410482198504029333" {
|
||||
return nil, errors.Join(processors.ErrNotFound, errors.New("查询为空"))
|
||||
}
|
||||
} else if paramsDto.Type == "ent" {
|
||||
// 企业查询:name 和 entMark 两者必填其一
|
||||
nameValue = paramsDto.EntName
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -17,9 +17,9 @@ import (
|
||||
"tyapi-server/internal/domains/api/services/processors"
|
||||
)
|
||||
|
||||
// ProcessQYGLJ1U9Request 企业全景报告处理器:并发调用企业全量(QYGLUY3S)、股权全景(QYGLJ0Q1)、司法涉诉(QYGL5S1I),
|
||||
// 以及企业年报(QYGLDJ12)、税收违法(QYGL8848)、欠税公告(QYGL7D9A);
|
||||
// 前三者走 buildReport / map*;后三者在 buildReport 中转为 annualReports、taxViolations、ownTaxNotices 供页面展示。
|
||||
// ProcessQYGLJ1U9Request 企业全景报告处理器:并发调用企业全量(QYGLUY3S)、股权全景(QYGLJ0Q1)、司法涉诉(QYGL5S1I)、
|
||||
// 企业年报(QYGLDJ12)、税收违法(QYGL8848)、欠税公告(QYGL7D9A)。
|
||||
// 单路失败、查无、解析失败时该路按空数据处理并继续合并;仅当合并后的报告仍无任何可展示的企业要素时返回查询为空。
|
||||
func ProcessQYGLJ1U9Request(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
|
||||
// 复用 QYGLUY3S 的入参结构:企业名称/注册号/统一社会信用代码
|
||||
var p dto.QYGLJ1U9Req
|
||||
@@ -30,7 +30,7 @@ func ProcessQYGLJ1U9Request(ctx context.Context, params []byte, deps *processors
|
||||
return nil, errors.Join(processors.ErrInvalidParam, err)
|
||||
}
|
||||
|
||||
// 并发调用三个已有处理器
|
||||
// 并发调用六个子处理器;单路失败或无数据时降级为空结果,仅当合并后仍无任何企业要素时返回查询为空
|
||||
type apiResult struct {
|
||||
key string
|
||||
data map[string]interface{}
|
||||
@@ -108,12 +108,15 @@ func ProcessQYGLJ1U9Request(ctx context.Context, params []byte, deps *processors
|
||||
wg.Wait()
|
||||
close(resultsCh)
|
||||
|
||||
var jiguang, judicial, equity map[string]interface{}
|
||||
var annualReport, taxViolation, taxArrears map[string]interface{}
|
||||
jiguang := map[string]interface{}{}
|
||||
judicial := map[string]interface{}{}
|
||||
equity := map[string]interface{}{}
|
||||
annualReport := map[string]interface{}{}
|
||||
taxViolation := map[string]interface{}{}
|
||||
taxArrears := map[string]interface{}{}
|
||||
for r := range resultsCh {
|
||||
if r.err != nil {
|
||||
// 任一关键数据源异常,则返回系统错误(也可以根据需求做降级)
|
||||
return nil, errors.Join(processors.ErrSystem, fmt.Errorf("%s 调用失败: %w", r.key, r.err))
|
||||
if r.err != nil || r.data == nil {
|
||||
continue
|
||||
}
|
||||
switch r.key {
|
||||
case "jiguangFull":
|
||||
@@ -130,27 +133,12 @@ func ProcessQYGLJ1U9Request(ctx context.Context, params []byte, deps *processors
|
||||
taxArrears = r.data
|
||||
}
|
||||
}
|
||||
if jiguang == nil {
|
||||
jiguang = map[string]interface{}{}
|
||||
}
|
||||
if judicial == nil {
|
||||
judicial = map[string]interface{}{}
|
||||
}
|
||||
if equity == nil {
|
||||
equity = map[string]interface{}{}
|
||||
}
|
||||
if annualReport == nil {
|
||||
annualReport = map[string]interface{}{}
|
||||
}
|
||||
if taxViolation == nil {
|
||||
taxViolation = map[string]interface{}{}
|
||||
}
|
||||
if taxArrears == nil {
|
||||
taxArrears = map[string]interface{}{}
|
||||
}
|
||||
|
||||
// 复用构建逻辑生成企业报告结构(含年报 / 税收违法 / 欠税公告的转化结果)
|
||||
report := buildReport(jiguang, judicial, equity, annualReport, taxViolation, taxArrears)
|
||||
if !qyglJ1U9ReportHasSubstantiveData(report) {
|
||||
return nil, errors.Join(processors.ErrNotFound, errors.New("未查询到可用于生成报告的企业数据"))
|
||||
}
|
||||
|
||||
// 为报告生成唯一编号并缓存,供后续通过编号查看
|
||||
reportID := saveQYGLReport(report)
|
||||
|
||||
@@ -58,9 +58,654 @@ func buildReport(jiguang, judicial, equity, annualRaw, taxViolationRaw, taxArrea
|
||||
report["annualReports"] = annualReports
|
||||
report["taxViolations"] = mapTaxViolations(taxViolationRaw)
|
||||
report["ownTaxNotices"] = mapOwnTaxNotices(taxArrearsRaw)
|
||||
applyQYGLJ1U9ReportFieldDefaults(report)
|
||||
return report
|
||||
}
|
||||
|
||||
// applyQYGLJ1U9ReportFieldDefaults 在子数据源缺失时仍保证报告字段齐全:字符串 ""、数值 0、数组 []、对象按约定填空结构(不向客户暴露「缺键」)。
|
||||
func applyQYGLJ1U9ReportFieldDefaults(report map[string]interface{}) {
|
||||
if report == nil {
|
||||
return
|
||||
}
|
||||
// 顶层字符串
|
||||
report["entName"] = str(report["entName"])
|
||||
report["creditCode"] = str(report["creditCode"])
|
||||
report["reportTime"] = str(report["reportTime"])
|
||||
|
||||
report["basic"] = mergeBasicDefaults(report["basic"])
|
||||
b, _ := report["basic"].(map[string]interface{})
|
||||
if str(report["entName"]) == "" {
|
||||
report["entName"] = str(b["entName"])
|
||||
}
|
||||
if str(report["creditCode"]) == "" {
|
||||
report["creditCode"] = str(b["creditCode"])
|
||||
}
|
||||
|
||||
report["branches"] = ensureSlice(report["branches"])
|
||||
report["guarantees"] = ensureSlice(report["guarantees"])
|
||||
report["inspections"] = ensureSlice(report["inspections"])
|
||||
report["timeline"] = ensureSlice(report["timeline"])
|
||||
report["beneficiaries"] = ensureSlice(report["beneficiaries"])
|
||||
report["annualReports"] = ensureSlice(report["annualReports"])
|
||||
if _, ok := report["basicList"]; !ok {
|
||||
report["basicList"] = []interface{}{}
|
||||
} else {
|
||||
report["basicList"] = ensureSlice(report["basicList"])
|
||||
}
|
||||
|
||||
report["shareholding"] = mergeShareholdingDefaults(report["shareholding"])
|
||||
report["controller"] = mergeControllerDefaults(report["controller"])
|
||||
report["investments"] = mergeInvestmentsDefaults(report["investments"])
|
||||
report["management"] = mergeManagementDefaults(report["management"])
|
||||
report["assets"] = mergeAssetsDefaults(report["assets"])
|
||||
report["licenses"] = mergeLicensesDefaults(report["licenses"])
|
||||
report["activities"] = mergeActivitiesDefaults(report["activities"])
|
||||
report["risks"] = mergeRisksDefaults(report["risks"])
|
||||
report["listed"] = mergeListedDefaults(report["listed"])
|
||||
|
||||
report["taxViolations"] = mergeTaxViolationsDefaults(report["taxViolations"])
|
||||
report["ownTaxNotices"] = mergeOwnTaxNoticesDefaults(report["ownTaxNotices"])
|
||||
|
||||
if ro, ok := report["riskOverview"].(map[string]interface{}); ok {
|
||||
report["riskOverview"] = mergeRiskOverviewDefaults(ro)
|
||||
} else {
|
||||
report["riskOverview"] = mergeRiskOverviewDefaults(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func mergeBasicDefaults(v interface{}) map[string]interface{} {
|
||||
out, _ := v.(map[string]interface{})
|
||||
if out == nil {
|
||||
out = map[string]interface{}{}
|
||||
}
|
||||
skel := map[string]interface{}{
|
||||
"entName": "",
|
||||
"creditCode": "",
|
||||
"regNo": "",
|
||||
"orgCode": "",
|
||||
"entType": "",
|
||||
"entTypeCode": "",
|
||||
"entityTypeCode": "",
|
||||
"establishDate": "",
|
||||
"registeredCapital": float64(0),
|
||||
"regCapCurrency": "",
|
||||
"regCapCurrencyCode": "",
|
||||
"regOrg": "",
|
||||
"regOrgCode": "",
|
||||
"regProvince": "",
|
||||
"provinceCode": "",
|
||||
"regCity": "",
|
||||
"regCityCode": "",
|
||||
"regDistrict": "",
|
||||
"districtCode": "",
|
||||
"address": "",
|
||||
"postalCode": "",
|
||||
"legalRepresentative": "",
|
||||
"compositionForm": "",
|
||||
"approvedBusinessItem": "",
|
||||
"status": "",
|
||||
"statusCode": "",
|
||||
"operationPeriodFrom": "",
|
||||
"operationPeriodTo": "",
|
||||
"approveDate": "",
|
||||
"cancelDate": "",
|
||||
"revokeDate": "",
|
||||
"cancelReason": "",
|
||||
"revokeReason": "",
|
||||
"businessScope": "",
|
||||
"lastAnnuReportYear": "",
|
||||
"oldNames": []interface{}{},
|
||||
}
|
||||
for k, def := range skel {
|
||||
if _, ok := out[k]; !ok {
|
||||
out[k] = def
|
||||
}
|
||||
}
|
||||
if out["oldNames"] == nil {
|
||||
out["oldNames"] = []interface{}{}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func ensureSlice(v interface{}) []interface{} {
|
||||
if v == nil {
|
||||
return []interface{}{}
|
||||
}
|
||||
arr, ok := v.([]interface{})
|
||||
if !ok {
|
||||
return []interface{}{}
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
func mergeShareholdingDefaults(v interface{}) map[string]interface{} {
|
||||
out, _ := v.(map[string]interface{})
|
||||
if out == nil {
|
||||
out = map[string]interface{}{}
|
||||
}
|
||||
skel := map[string]interface{}{
|
||||
"shareholders": []interface{}{},
|
||||
"equityChanges": []interface{}{},
|
||||
"equityPledges": []interface{}{},
|
||||
"paidInDetails": []interface{}{},
|
||||
"subscribedCapitalDetails": []interface{}{},
|
||||
"hasEquityPledges": false,
|
||||
"shareholderCount": 0,
|
||||
"registeredCapital": float64(0),
|
||||
"currency": "",
|
||||
"topHolderName": "",
|
||||
"topHolderPercent": float64(0),
|
||||
"top5TotalPercent": float64(0),
|
||||
}
|
||||
for k, def := range skel {
|
||||
if _, ok := out[k]; !ok {
|
||||
out[k] = def
|
||||
}
|
||||
}
|
||||
for _, k := range []string{"shareholders", "equityChanges", "equityPledges", "paidInDetails", "subscribedCapitalDetails"} {
|
||||
out[k] = ensureSlice(out[k])
|
||||
}
|
||||
if out["registeredCapital"] == nil {
|
||||
out["registeredCapital"] = float64(0)
|
||||
}
|
||||
if _, ok := out["hasEquityPledges"].(bool); !ok {
|
||||
out["hasEquityPledges"] = false
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func mergeControllerDefaults(v interface{}) map[string]interface{} {
|
||||
if m, ok := v.(map[string]interface{}); ok && m != nil {
|
||||
if m["path"] == nil {
|
||||
m["path"] = map[string]interface{}{
|
||||
"nodes": []interface{}{},
|
||||
"links": []interface{}{},
|
||||
}
|
||||
}
|
||||
if p, ok := m["path"].(map[string]interface{}); ok {
|
||||
if _, ok := p["nodes"]; !ok {
|
||||
p["nodes"] = []interface{}{}
|
||||
}
|
||||
if _, ok := p["links"]; !ok {
|
||||
p["links"] = []interface{}{}
|
||||
}
|
||||
}
|
||||
ensureStrKeys(m, []string{"id", "name", "type", "reason", "source"})
|
||||
if m["percent"] == nil {
|
||||
m["percent"] = float64(0)
|
||||
} else if _, ok := m["percent"].(float64); !ok {
|
||||
m["percent"] = num(m["percent"])
|
||||
}
|
||||
return m
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"id": "",
|
||||
"name": "",
|
||||
"type": "",
|
||||
"percent": float64(0),
|
||||
"path": map[string]interface{}{
|
||||
"nodes": []interface{}{},
|
||||
"links": []interface{}{},
|
||||
},
|
||||
"reason": "",
|
||||
"source": "",
|
||||
}
|
||||
}
|
||||
|
||||
func mergeInvestmentsDefaults(v interface{}) map[string]interface{} {
|
||||
out, _ := v.(map[string]interface{})
|
||||
if out == nil {
|
||||
out = map[string]interface{}{}
|
||||
}
|
||||
if _, ok := out["totalCount"]; !ok {
|
||||
out["totalCount"] = 0
|
||||
}
|
||||
if _, ok := out["totalAmount"]; !ok {
|
||||
out["totalAmount"] = float64(0)
|
||||
}
|
||||
if out["totalAmount"] == nil {
|
||||
out["totalAmount"] = float64(0)
|
||||
}
|
||||
out["list"] = ensureSlice(out["list"])
|
||||
out["legalRepresentativeInvestments"] = ensureSlice(out["legalRepresentativeInvestments"])
|
||||
return out
|
||||
}
|
||||
|
||||
func mergeManagementDefaults(v interface{}) map[string]interface{} {
|
||||
out, _ := v.(map[string]interface{})
|
||||
if out == nil {
|
||||
out = map[string]interface{}{}
|
||||
}
|
||||
if _, ok := out["executives"]; !ok {
|
||||
out["executives"] = []interface{}{}
|
||||
} else {
|
||||
out["executives"] = ensureSlice(out["executives"])
|
||||
}
|
||||
if _, ok := out["legalRepresentativeOtherPositions"]; !ok {
|
||||
out["legalRepresentativeOtherPositions"] = []interface{}{}
|
||||
} else {
|
||||
out["legalRepresentativeOtherPositions"] = ensureSlice(out["legalRepresentativeOtherPositions"])
|
||||
}
|
||||
if _, ok := out["employeeCount"]; !ok || out["employeeCount"] == nil {
|
||||
out["employeeCount"] = float64(0)
|
||||
}
|
||||
if _, ok := out["femaleEmployeeCount"]; !ok || out["femaleEmployeeCount"] == nil {
|
||||
out["femaleEmployeeCount"] = float64(0)
|
||||
}
|
||||
if out["socialSecurity"] == nil {
|
||||
out["socialSecurity"] = map[string]interface{}{}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func mergeAssetsDefaults(v interface{}) map[string]interface{} {
|
||||
out, _ := v.(map[string]interface{})
|
||||
if out == nil {
|
||||
out = map[string]interface{}{}
|
||||
}
|
||||
if _, ok := out["years"]; !ok {
|
||||
out["years"] = []interface{}{}
|
||||
} else {
|
||||
out["years"] = ensureSlice(out["years"])
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func mergeLicensesDefaults(v interface{}) map[string]interface{} {
|
||||
out, _ := v.(map[string]interface{})
|
||||
if out == nil {
|
||||
out = map[string]interface{}{}
|
||||
}
|
||||
for _, k := range []string{"permits", "permitChanges", "ipPledges", "otherLicenses"} {
|
||||
if _, ok := out[k]; !ok {
|
||||
out[k] = []interface{}{}
|
||||
} else {
|
||||
out[k] = ensureSlice(out[k])
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func mergeActivitiesDefaults(v interface{}) map[string]interface{} {
|
||||
out, _ := v.(map[string]interface{})
|
||||
if out == nil {
|
||||
out = map[string]interface{}{}
|
||||
}
|
||||
if _, ok := out["bids"]; !ok {
|
||||
out["bids"] = []interface{}{}
|
||||
} else {
|
||||
out["bids"] = ensureSlice(out["bids"])
|
||||
}
|
||||
if _, ok := out["websites"]; !ok {
|
||||
out["websites"] = []interface{}{}
|
||||
} else {
|
||||
out["websites"] = ensureSlice(out["websites"])
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func litigationTypeKeys() []string {
|
||||
return []string{
|
||||
"administrative", "implement", "preservation", "civil", "criminal",
|
||||
"bankrupt", "jurisdict", "compensate",
|
||||
}
|
||||
}
|
||||
|
||||
func defaultLitigationShell() map[string]interface{} {
|
||||
out := map[string]interface{}{"totalCases": 0}
|
||||
for _, k := range litigationTypeKeys() {
|
||||
out[k] = map[string]interface{}{
|
||||
"count": 0,
|
||||
"cases": []interface{}{},
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func mergeLitigationShape(v interface{}) map[string]interface{} {
|
||||
out := defaultLitigationShell()
|
||||
if v == nil {
|
||||
return out
|
||||
}
|
||||
m, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
return out
|
||||
}
|
||||
known := map[string]struct{}{}
|
||||
for _, k := range litigationTypeKeys() {
|
||||
known[k] = struct{}{}
|
||||
}
|
||||
for k, val := range m {
|
||||
if k == "totalCases" {
|
||||
out["totalCases"] = intFromAny(val)
|
||||
continue
|
||||
}
|
||||
if _, isCat := known[k]; isCat {
|
||||
sm, ok := val.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
out[k] = map[string]interface{}{
|
||||
"count": intFromAny(sm["count"]),
|
||||
"cases": ensureSlice(sm["cases"]),
|
||||
}
|
||||
continue
|
||||
}
|
||||
out[k] = val
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func defaultQuickCancelShell() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"entName": "",
|
||||
"creditCode": "",
|
||||
"regNo": "",
|
||||
"regOrg": "",
|
||||
"noticeFromDate": "",
|
||||
"noticeToDate": "",
|
||||
"cancelResult": "",
|
||||
"dissents": []interface{}{},
|
||||
}
|
||||
}
|
||||
|
||||
func defaultLiquidationShell() map[string]interface{} {
|
||||
return map[string]interface{}{
|
||||
"principal": "",
|
||||
"members": []interface{}{},
|
||||
}
|
||||
}
|
||||
|
||||
func mergeRisksDefaults(v interface{}) map[string]interface{} {
|
||||
out, _ := v.(map[string]interface{})
|
||||
if out == nil {
|
||||
out = map[string]interface{}{}
|
||||
}
|
||||
boolKeys := []string{
|
||||
"hasCourtJudgments", "hasJudicialAssists", "hasDishonestDebtors", "hasLimitHighDebtors",
|
||||
"hasAdminPenalty", "hasException", "hasSeriousIllegal", "hasTaxOwing", "hasSeriousTaxIllegal",
|
||||
"hasMortgage", "hasEquityPledges", "hasQuickCancel",
|
||||
}
|
||||
for _, k := range boolKeys {
|
||||
if _, ok := out[k]; !ok {
|
||||
out[k] = false
|
||||
}
|
||||
}
|
||||
if _, ok := out["riskLevel"]; !ok {
|
||||
out["riskLevel"] = "低"
|
||||
}
|
||||
if _, ok := out["riskScore"]; !ok {
|
||||
out["riskScore"] = 80
|
||||
}
|
||||
for _, k := range []string{"dishonestDebtorCount", "limitHighDebtorCount"} {
|
||||
if _, ok := out[k]; !ok {
|
||||
out[k] = 0
|
||||
}
|
||||
}
|
||||
for _, k := range []string{
|
||||
"courtJudgments", "judicialAssists", "dishonestDebtors", "limitHighDebtors",
|
||||
"adminPenalties", "adminPenaltyUpdates", "exceptions", "seriousIllegals", "mortgages",
|
||||
} {
|
||||
if _, ok := out[k]; !ok {
|
||||
out[k] = []interface{}{}
|
||||
} else {
|
||||
out[k] = ensureSlice(out[k])
|
||||
}
|
||||
}
|
||||
out["litigation"] = mergeLitigationShape(out["litigation"])
|
||||
if out["quickCancel"] == nil {
|
||||
out["quickCancel"] = defaultQuickCancelShell()
|
||||
} else if qm, ok := out["quickCancel"].(map[string]interface{}); ok {
|
||||
dc := defaultQuickCancelShell()
|
||||
for k, def := range dc {
|
||||
if _, ok := qm[k]; !ok {
|
||||
qm[k] = def
|
||||
}
|
||||
}
|
||||
if qm["dissents"] == nil {
|
||||
qm["dissents"] = []interface{}{}
|
||||
} else {
|
||||
qm["dissents"] = ensureSlice(qm["dissents"])
|
||||
}
|
||||
out["quickCancel"] = qm
|
||||
}
|
||||
if out["liquidation"] == nil {
|
||||
out["liquidation"] = defaultLiquidationShell()
|
||||
} else if lm, ok := out["liquidation"].(map[string]interface{}); ok {
|
||||
dc := defaultLiquidationShell()
|
||||
for k, def := range dc {
|
||||
if _, ok := lm[k]; !ok {
|
||||
lm[k] = def
|
||||
}
|
||||
}
|
||||
if lm["members"] == nil {
|
||||
lm["members"] = []interface{}{}
|
||||
} else {
|
||||
lm["members"] = ensureSlice(lm["members"])
|
||||
}
|
||||
out["liquidation"] = lm
|
||||
}
|
||||
tr, _ := out["taxRecords"].(map[string]interface{})
|
||||
if tr == nil {
|
||||
tr = map[string]interface{}{}
|
||||
}
|
||||
for _, k := range []string{"taxLevelAYears", "seriousTaxIllegal", "taxOwings"} {
|
||||
if _, ok := tr[k]; !ok {
|
||||
tr[k] = []interface{}{}
|
||||
} else {
|
||||
tr[k] = ensureSlice(tr[k])
|
||||
}
|
||||
}
|
||||
out["taxRecords"] = tr
|
||||
return out
|
||||
}
|
||||
|
||||
// normalizeListedStock 无股票结构时用 JSON null,避免前端把 {} 当成有值而 JSON.stringify 出 "{}"。
|
||||
func normalizeListedStock(v interface{}) interface{} {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
m, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
return v
|
||||
}
|
||||
if len(m) == 0 {
|
||||
return nil
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func mergeListedDefaults(v interface{}) map[string]interface{} {
|
||||
if m, ok := v.(map[string]interface{}); ok && m != nil {
|
||||
if _, ok := m["isListed"].(bool); !ok {
|
||||
m["isListed"] = false
|
||||
}
|
||||
co, _ := m["company"].(map[string]interface{})
|
||||
if co == nil {
|
||||
co = map[string]interface{}{}
|
||||
}
|
||||
for k, def := range map[string]interface{}{
|
||||
"bizScope": "", "creditCode": "", "regAddr": "", "regCapital": "",
|
||||
"orgCode": "", "cur": "", "curName": "",
|
||||
} {
|
||||
if _, ok := co[k]; !ok {
|
||||
co[k] = def
|
||||
}
|
||||
}
|
||||
m["company"] = co
|
||||
m["stock"] = normalizeListedStock(m["stock"])
|
||||
if _, ok := m["topShareholders"]; !ok {
|
||||
m["topShareholders"] = []interface{}{}
|
||||
} else {
|
||||
m["topShareholders"] = ensureSlice(m["topShareholders"])
|
||||
}
|
||||
if _, ok := m["listedManagers"]; !ok {
|
||||
m["listedManagers"] = []interface{}{}
|
||||
} else {
|
||||
m["listedManagers"] = ensureSlice(m["listedManagers"])
|
||||
}
|
||||
return m
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"isListed": false,
|
||||
"company": map[string]interface{}{
|
||||
"bizScope": "", "creditCode": "", "regAddr": "", "regCapital": "",
|
||||
"orgCode": "", "cur": "", "curName": "",
|
||||
},
|
||||
"stock": nil,
|
||||
"topShareholders": []interface{}{},
|
||||
"listedManagers": []interface{}{},
|
||||
}
|
||||
}
|
||||
|
||||
func mergeTaxViolationsDefaults(v interface{}) map[string]interface{} {
|
||||
out, _ := v.(map[string]interface{})
|
||||
if out == nil {
|
||||
out = map[string]interface{}{}
|
||||
}
|
||||
if _, ok := out["total"]; !ok {
|
||||
out["total"] = 0
|
||||
}
|
||||
if _, ok := out["items"]; !ok {
|
||||
out["items"] = []interface{}{}
|
||||
} else {
|
||||
out["items"] = ensureSlice(out["items"])
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func mergeOwnTaxNoticesDefaults(v interface{}) map[string]interface{} {
|
||||
return mergeTaxViolationsDefaults(v)
|
||||
}
|
||||
|
||||
func mergeRiskOverviewDefaults(v interface{}) map[string]interface{} {
|
||||
out, _ := v.(map[string]interface{})
|
||||
if out == nil {
|
||||
out = map[string]interface{}{}
|
||||
}
|
||||
if _, ok := out["riskLevel"]; !ok {
|
||||
out["riskLevel"] = "低"
|
||||
}
|
||||
if _, ok := out["riskScore"]; !ok {
|
||||
out["riskScore"] = 100
|
||||
}
|
||||
if _, ok := out["tags"]; !ok {
|
||||
out["tags"] = []interface{}{}
|
||||
} else {
|
||||
out["tags"] = ensureSlice(out["tags"])
|
||||
}
|
||||
if _, ok := out["items"]; !ok {
|
||||
out["items"] = []interface{}{}
|
||||
} else {
|
||||
out["items"] = ensureSlice(out["items"])
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func ensureStrKeys(m map[string]interface{}, keys []string) {
|
||||
if m == nil {
|
||||
return
|
||||
}
|
||||
for _, k := range keys {
|
||||
if _, ok := m[k]; !ok {
|
||||
m[k] = ""
|
||||
} else if m[k] == nil {
|
||||
m[k] = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// qyglJ1U9ReportHasSubstantiveData 判断合并后的报告是否至少含一项可展示的企业要素。
|
||||
// 当所有子数据源均失败或等价于无数据时返回 false,用于 QYGLJ1U9 整体返回「查询为空」。
|
||||
func qyglJ1U9ReportHasSubstantiveData(report map[string]interface{}) bool {
|
||||
if report == nil {
|
||||
return false
|
||||
}
|
||||
trim := func(v interface{}) string { return strings.TrimSpace(str(v)) }
|
||||
|
||||
basic, _ := report["basic"].(map[string]interface{})
|
||||
if basic != nil {
|
||||
if trim(basic["entName"]) != "" || trim(basic["creditCode"]) != "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if trim(report["entName"]) != "" || trim(report["creditCode"]) != "" {
|
||||
return true
|
||||
}
|
||||
if ar, ok := report["annualReports"].([]interface{}); ok && len(ar) > 0 {
|
||||
return true
|
||||
}
|
||||
if tv, ok := report["taxViolations"].(map[string]interface{}); ok {
|
||||
if intFromAny(tv["total"]) > 0 {
|
||||
return true
|
||||
}
|
||||
if items, ok := tv["items"].([]interface{}); ok && len(items) > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if ot, ok := report["ownTaxNotices"].(map[string]interface{}); ok {
|
||||
if intFromAny(ot["total"]) > 0 {
|
||||
return true
|
||||
}
|
||||
if items, ok := ot["items"].([]interface{}); ok && len(items) > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if br, ok := report["branches"].([]interface{}); ok && len(br) > 0 {
|
||||
return true
|
||||
}
|
||||
if bl, ok := report["basicList"].([]interface{}); ok && len(bl) > 0 {
|
||||
return true
|
||||
}
|
||||
if sh, ok := report["shareholding"].(map[string]interface{}); ok {
|
||||
if arr, ok := sh["shareholders"].([]interface{}); ok && len(arr) > 0 {
|
||||
return true
|
||||
}
|
||||
if intFromAny(sh["shareholderCount"]) > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if inv, ok := report["investments"].(map[string]interface{}); ok {
|
||||
if arr, ok := inv["list"].([]interface{}); ok && len(arr) > 0 {
|
||||
return true
|
||||
}
|
||||
if intFromAny(inv["totalCount"]) > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if ben, ok := report["beneficiaries"].([]interface{}); ok && len(ben) > 0 {
|
||||
return true
|
||||
}
|
||||
if tl, ok := report["timeline"].([]interface{}); ok && len(tl) > 0 {
|
||||
return true
|
||||
}
|
||||
if ctl, _ := report["controller"].(map[string]interface{}); ctl != nil && trim(ctl["name"]) != "" {
|
||||
return true
|
||||
}
|
||||
if risks, ok := report["risks"].(map[string]interface{}); ok {
|
||||
for _, key := range []string{
|
||||
"dishonestDebtors", "limitHighDebtors", "adminPenalties", "exceptions",
|
||||
"seriousIllegals", "mortgages", "courtJudgments", "judicialAssists",
|
||||
} {
|
||||
if arr, ok := risks[key].([]interface{}); ok && len(arr) > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if lit, ok := risks["litigation"].(map[string]interface{}); ok {
|
||||
for _, v := range lit {
|
||||
sub, ok := v.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if arr, ok := sub["cases"].([]interface{}); ok && len(arr) > 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// jiguangWithoutYearReportTables 浅拷贝全量 map,并去掉企业全量 V2 中与「公示年报」对应的 YEARREPORT* 键。
|
||||
// 在已接入 QYGLDJ12 且年报列表非空时使用,避免 build 与 HTML 中与「十六、企业年报」重复展示。
|
||||
func jiguangWithoutYearReportTables(jiguang map[string]interface{}) map[string]interface{} {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ func (s *EnterpriseInfoSubmitRecordService) ValidateWithWestdex(ctx context.Cont
|
||||
// 调用QYGL23T7处理器进行验证
|
||||
responseBytes, err := qygl.ProcessQYGL23T7Request(ctx, paramsBytes, deps)
|
||||
if err != nil {
|
||||
// 检查是否是数据源错误
|
||||
// 检查是否是数据源错误企业信息不一致
|
||||
if errors.Is(err, processors.ErrDatasource) {
|
||||
return fmt.Errorf("数据源异常: %w", err)
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ var (
|
||||
ErrSuccess = &JiguangError{Code: 0, Message: "请求成功"}
|
||||
|
||||
// 参数错误
|
||||
ErrParamInvalid = &JiguangError{Code: 400, Message: "请求参数不正确"}
|
||||
ErrParamInvalid = &JiguangError{Code: 400, Message: "请求参数不正确,QCXGGB2Q查询为空"}
|
||||
ErrMethodInvalid = &JiguangError{Code: 405, Message: "请求方法不正确"}
|
||||
ErrParamFormInvalid = &JiguangError{Code: 906, Message: "请求参数形式不正确"}
|
||||
ErrBodyIncomplete = &JiguangError{Code: 914, Message: "Body 请求参数不完整"}
|
||||
|
||||
@@ -22,12 +22,31 @@ var (
|
||||
ErrNotFound = errors.New("查询为空")
|
||||
)
|
||||
|
||||
// JiguangResponse 极光API响应结构
|
||||
// JiguangResponse 极光API响应结构(兼容两套字段命名)
|
||||
//
|
||||
// 格式一:ordernum、message、result(定位/查询类接口常见)
|
||||
// 格式二:order_id、msg、data(文档中的 code/msg/order_id/data)
|
||||
type JiguangResponse struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
Message string `json:"message"`
|
||||
OrderID string `json:"order_id"`
|
||||
OrderNum string `json:"ordernum"`
|
||||
Data interface{} `json:"data"`
|
||||
Result interface{} `json:"result"`
|
||||
}
|
||||
|
||||
// normalize 将异名字段合并到 OrderID、Msg,便于后续统一分支使用
|
||||
func (r *JiguangResponse) normalize() {
|
||||
if r == nil {
|
||||
return
|
||||
}
|
||||
if r.OrderID == "" && r.OrderNum != "" {
|
||||
r.OrderID = r.OrderNum
|
||||
}
|
||||
if r.Msg == "" && r.Message != "" {
|
||||
r.Msg = r.Message
|
||||
}
|
||||
}
|
||||
|
||||
// JiguangConfig 极光服务配置
|
||||
@@ -211,6 +230,7 @@ func (j *JiguangService) CallAPI(ctx context.Context, apiCode string, apiPath st
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
jiguangResp.normalize()
|
||||
|
||||
// 记录响应日志(不记录具体响应数据)
|
||||
if j.logger != nil {
|
||||
@@ -225,10 +245,13 @@ func (j *JiguangService) CallAPI(ctx context.Context, apiCode string, apiPath st
|
||||
if jiguangResp.Code != 0 && jiguangResp.Code != 200 {
|
||||
// 创建极光错误
|
||||
jiguangErr := NewJiguangErrorFromCode(jiguangResp.Code)
|
||||
if jiguangErr.Message == fmt.Sprintf("未知错误码: %d", jiguangResp.Code) && jiguangResp.Msg != "" {
|
||||
if jiguangErr.Message == fmt.Sprintf("未知错误码: %d", jiguangResp.Code) {
|
||||
if jiguangResp.Msg != "" {
|
||||
jiguangErr.Message = jiguangResp.Msg
|
||||
} else if jiguangResp.Message != "" {
|
||||
jiguangErr.Message = jiguangResp.Message
|
||||
}
|
||||
}
|
||||
|
||||
// 根据错误类型返回不同的错误
|
||||
if jiguangErr.IsNoRecord() {
|
||||
// 从context中获取apiCode,判断是否需要抛出异常
|
||||
@@ -236,33 +259,27 @@ func (j *JiguangService) CallAPI(ctx context.Context, apiCode string, apiPath st
|
||||
if ctxProcessorCode, ok := ctx.Value("api_code").(string); ok {
|
||||
processorCode = ctxProcessorCode
|
||||
}
|
||||
|
||||
// 定义不需要抛出异常的处理器列表(默认情况下查无记录时抛出异常)
|
||||
processorsNotToThrowError := map[string]bool{
|
||||
// 在这个列表中的处理器,查无记录时返回空数组,不抛出异常
|
||||
// 示例:如果需要添加某个处理器,取消下面的注释
|
||||
// "QCXG9P1C": true,
|
||||
}
|
||||
|
||||
// 如果是不需要抛出异常的处理器,返回空数组;否则(默认)抛出异常
|
||||
if processorsNotToThrowError[processorCode] {
|
||||
// 查无记录时,返回空数组,API调用记录为成功
|
||||
return []byte("[]"), nil
|
||||
}
|
||||
|
||||
// 默认情况下,查无记录时抛出异常
|
||||
// 记录错误日志
|
||||
if j.logger != nil {
|
||||
j.logger.LogErrorWithResponseID(requestID, transactionID, apiCode, jiguangErr, params, jiguangResp.OrderID)
|
||||
}
|
||||
return nil, errors.Join(ErrNotFound, jiguangErr)
|
||||
}
|
||||
|
||||
// 记录错误日志(查无记录的情况不记录错误日志)
|
||||
if j.logger != nil {
|
||||
j.logger.LogErrorWithResponseID(requestID, transactionID, apiCode, jiguangErr, params, jiguangResp.OrderID)
|
||||
}
|
||||
|
||||
if jiguangErr.IsQueryFailed() {
|
||||
return nil, errors.Join(ErrDatasource, jiguangErr)
|
||||
} else if jiguangErr.IsSystemError() {
|
||||
@@ -272,15 +289,18 @@ func (j *JiguangService) CallAPI(ctx context.Context, apiCode string, apiPath st
|
||||
}
|
||||
}
|
||||
|
||||
// 成功响应,返回data字段
|
||||
if jiguangResp.Data == nil {
|
||||
// 成功时业务体在 data 或 result
|
||||
payload := jiguangResp.Data
|
||||
if payload == nil {
|
||||
payload = jiguangResp.Result
|
||||
}
|
||||
if payload == nil {
|
||||
return []byte("{}"), nil
|
||||
}
|
||||
|
||||
// 将data转换为JSON字节
|
||||
dataBytes, err := json.Marshal(jiguangResp.Data)
|
||||
dataBytes, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
err = errors.Join(ErrSystem, fmt.Errorf("data字段序列化失败: %w", err))
|
||||
err = errors.Join(ErrSystem, fmt.Errorf("业务数据序列化失败: %w", err))
|
||||
if j.logger != nil {
|
||||
j.logger.LogErrorWithResponseID(requestID, transactionID, apiCode, err, params, jiguangResp.OrderID)
|
||||
}
|
||||
|
||||
@@ -134,6 +134,8 @@ func (s *WeChatWorkService) SendCertificationNotification(ctx context.Context, n
|
||||
switch notificationType {
|
||||
case "new_application":
|
||||
return s.sendNewApplicationNotification(ctx, data)
|
||||
case "pending_manual_review":
|
||||
return s.sendPendingManualReviewNotification(ctx, data)
|
||||
case "ocr_success":
|
||||
return s.sendOCRSuccessNotification(ctx, data)
|
||||
case "ocr_failed":
|
||||
@@ -177,6 +179,45 @@ func (s *WeChatWorkService) sendNewApplicationNotification(ctx context.Context,
|
||||
return s.SendMarkdownMessage(ctx, content)
|
||||
}
|
||||
|
||||
// sendPendingManualReviewNotification 用户已提交企业信息,待管理员人工审核(三真审核前序步骤)
|
||||
func (s *WeChatWorkService) sendPendingManualReviewNotification(ctx context.Context, data map[string]interface{}) error {
|
||||
companyName := fmt.Sprint(data["company_name"])
|
||||
legalPersonName := fmt.Sprint(data["legal_person_name"])
|
||||
authorizedRepName := fmt.Sprint(data["authorized_rep_name"])
|
||||
contactPhone := fmt.Sprint(data["contact_phone"])
|
||||
apiUsage := fmt.Sprint(data["api_usage"])
|
||||
submitAt := fmt.Sprint(data["submit_at"])
|
||||
|
||||
if authorizedRepName == "" || authorizedRepName == "<nil>" {
|
||||
authorizedRepName = "—"
|
||||
}
|
||||
if apiUsage == "" || apiUsage == "<nil>" {
|
||||
apiUsage = "—"
|
||||
}
|
||||
if contactPhone == "" || contactPhone == "<nil>" {
|
||||
contactPhone = "—"
|
||||
}
|
||||
|
||||
content := fmt.Sprintf(`## 【天远API】📋 企业信息待人工审核
|
||||
|
||||
**企业名称**: %s
|
||||
**法人**: %s
|
||||
**授权申请人**: %s
|
||||
**联系电话**: %s
|
||||
**应用场景说明**: %s
|
||||
**提交时间**: %s
|
||||
|
||||
> 请管理员登录后台 **企业审核** 通过审核后,用户方可进行 e签宝企业认证。`,
|
||||
companyName,
|
||||
legalPersonName,
|
||||
authorizedRepName,
|
||||
contactPhone,
|
||||
apiUsage,
|
||||
submitAt)
|
||||
|
||||
return s.SendMarkdownMessage(ctx, content)
|
||||
}
|
||||
|
||||
// sendOCRSuccessNotification 发送OCR识别成功通知
|
||||
func (s *WeChatWorkService) sendOCRSuccessNotification(ctx context.Context, data map[string]interface{}) error {
|
||||
companyName := data["company_name"].(string)
|
||||
@@ -391,8 +432,7 @@ func (s *WeChatWorkService) sendMessage(ctx context.Context, message map[string]
|
||||
isTimeout = true
|
||||
} else if netErr, ok := err.(interface{ Timeout() bool }); ok && netErr.Timeout() {
|
||||
isTimeout = true
|
||||
} else if errStr := err.Error();
|
||||
errStr == "context deadline exceeded" ||
|
||||
} else if errStr := err.Error(); errStr == "context deadline exceeded" ||
|
||||
errStr == "timeout" ||
|
||||
errStr == "Client.Timeout exceeded" ||
|
||||
errStr == "net/http: request canceled" {
|
||||
|
||||
@@ -4,6 +4,17 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// GetQueryEmptyErrByCode 将数据宝错误码归类为“查询为空/不扣费”错误。
|
||||
// 说明:上游通常依赖 errors.Is(err, ErrQueryEmpty) 来决定是否扣费。
|
||||
func GetQueryEmptyErrByCode(code string) error {
|
||||
switch code {
|
||||
case "10001", "10006":
|
||||
return ErrQueryEmpty
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ShujubaoError 数据宝服务错误
|
||||
type ShujubaoError struct {
|
||||
Code string `json:"code"`
|
||||
|
||||
@@ -292,12 +292,17 @@ func (s *ShujubaoService) CallAPI(ctx context.Context, apiPath string, params ma
|
||||
}
|
||||
|
||||
code := shujubaoResp.Code
|
||||
if code == "10001" || code == "10006" {
|
||||
// 查空/查无:返回空数组,不视为错误
|
||||
return []interface{}{}, nil
|
||||
}
|
||||
if code != "10000" {
|
||||
|
||||
// 成功码只有这三类:其它 code 都走统一错误映射返回
|
||||
if code != "10000" && code != "200" && code != "0" {
|
||||
shujubaoErr := NewShujubaoErrorFromCode(code, shujubaoResp.Message)
|
||||
if queryEmptyErr := GetQueryEmptyErrByCode(code); queryEmptyErr != nil {
|
||||
err = errors.Join(queryEmptyErr, shujubaoErr)
|
||||
if s.logger != nil {
|
||||
s.logger.LogError(requestID, transactionID, apiPath, err, paramsForLog(params))
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if s.logger != nil {
|
||||
s.logger.LogError(requestID, transactionID, apiPath, shujubaoErr, paramsForLog(params))
|
||||
}
|
||||
|
||||
@@ -4,9 +4,11 @@ import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/md5"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -119,3 +121,10 @@ func pkcs7Unpadding(src []byte) ([]byte, error) {
|
||||
|
||||
return src[:length-unpadding], nil
|
||||
}
|
||||
|
||||
// MD5 使用MD5加密数据,返回十六进制字符串
|
||||
func MD5(data string) string {
|
||||
h := md5.New()
|
||||
io.WriteString(h, data)
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
@@ -212,11 +212,12 @@ func (z *ZhichaService) CallAPI(ctx context.Context, proID string, params map[st
|
||||
|
||||
// 201 表示查询为空,兼容其它情况如果data也为空,则返回空对象
|
||||
if zhichaResp.Code == "201" {
|
||||
// 先做类型断言
|
||||
dataMap, ok := zhichaResp.Data.(map[string]interface{})
|
||||
if ok && len(dataMap) > 0 {
|
||||
if ok {
|
||||
// 即使是 {},也原样返回
|
||||
return dataMap, nil
|
||||
}
|
||||
// 兜底:防止解密异常
|
||||
return map[string]interface{}{}, nil
|
||||
}
|
||||
|
||||
@@ -315,6 +316,12 @@ func (z *ZhichaService) Decrypt(encryptedData string) (string, error) {
|
||||
return string(unpadded), nil
|
||||
}
|
||||
|
||||
// MD5 对字符串进行MD5加密并返回32位小写十六进制字符串
|
||||
func (z *ZhichaService) MD5(data string) string {
|
||||
hash := md5.Sum([]byte(data))
|
||||
return hex.EncodeToString(hash[:])
|
||||
}
|
||||
|
||||
// pkcs7Padding 使用PKCS7填充数据
|
||||
func (z *ZhichaService) pkcs7Padding(src []byte, blockSize int) []byte {
|
||||
padding := blockSize - len(src)%blockSize
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"html/template"
|
||||
@@ -116,6 +117,7 @@ func (h *QYGLReportHandler) GetQYGLReportPageByID(c *gin.Context) {
|
||||
// 优先从数据库中查询报告记录
|
||||
if h.reportRepo != nil {
|
||||
if entity, err := h.reportRepo.FindByReportID(c.Request.Context(), id); err == nil && entity != nil {
|
||||
h.maybeScheduleQYGLPDFPregen(c.Request.Context(), id)
|
||||
reportJSON := template.JS(entity.ReportData)
|
||||
c.HTML(http.StatusOK, "qiye.html", gin.H{
|
||||
"ReportJSON": reportJSON,
|
||||
@@ -140,11 +142,36 @@ func (h *QYGLReportHandler) GetQYGLReportPageByID(c *gin.Context) {
|
||||
|
||||
reportJSON := template.JS(reportJSONBytes)
|
||||
|
||||
h.maybeScheduleQYGLPDFPregen(c.Request.Context(), id)
|
||||
|
||||
c.HTML(http.StatusOK, "qiye.html", gin.H{
|
||||
"ReportJSON": reportJSON,
|
||||
})
|
||||
}
|
||||
|
||||
// qyglReportExists 报告是否仍在库或本进程内存中(用于决定是否补开预生成)
|
||||
func (h *QYGLReportHandler) qyglReportExists(ctx context.Context, id string) bool {
|
||||
if h.reportRepo != nil {
|
||||
if e, err := h.reportRepo.FindByReportID(ctx, id); err == nil && e != nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
_, ok := qygl.GetQYGLReport(id)
|
||||
return ok
|
||||
}
|
||||
|
||||
// maybeScheduleQYGLPDFPregen 在已配置公网基址时异步预生成 PDF;Schedule 内部会去重。
|
||||
// 解决:服务重启 / 多实例后内存队列为空,用户打开报告页或轮询状态时仍应能启动预生成。
|
||||
func (h *QYGLReportHandler) maybeScheduleQYGLPDFPregen(ctx context.Context, id string) {
|
||||
if id == "" || h.qyglPDFPregen == nil || !h.qyglPDFPregen.Enabled() {
|
||||
return
|
||||
}
|
||||
if !h.qyglReportExists(ctx, id) {
|
||||
return
|
||||
}
|
||||
h.qyglPDFPregen.ScheduleQYGLReportPDF(context.Background(), id)
|
||||
}
|
||||
|
||||
// GetQYGLReportPDFStatusByID 查询企业报告 PDF 预生成状态(供前端轮询)
|
||||
// GET /reports/qygl/:id/pdf/status
|
||||
func (h *QYGLReportHandler) GetQYGLReportPDFStatusByID(c *gin.Context) {
|
||||
@@ -164,6 +191,10 @@ func (h *QYGLReportHandler) GetQYGLReportPDFStatusByID(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
st, msg := h.qyglPDFPregen.Status(id)
|
||||
if st == pdf.QYGLReportPDFStatusNone && h.qyglReportExists(c.Request.Context(), id) {
|
||||
h.qyglPDFPregen.ScheduleQYGLReportPDF(context.Background(), id)
|
||||
st, msg = h.qyglPDFPregen.Status(id)
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"status": string(st), "message": userFacingPDFStatusMessage(st, msg)})
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,12 @@ import (
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
// ApiPopularityRankingItem API受欢迎榜单项
|
||||
type ApiPopularityRankingItem struct {
|
||||
Name string `json:"name" comment:"产品名称"`
|
||||
Code string `json:"code" comment:"产品编码"`
|
||||
}
|
||||
|
||||
// StatisticsHandler 统计处理器
|
||||
type StatisticsHandler struct {
|
||||
statisticsAppService statistics.StatisticsApplicationService
|
||||
@@ -1226,7 +1232,7 @@ func (h *StatisticsHandler) GetRechargeStatistics(c *gin.Context) {
|
||||
// GetLatestProducts 获取最新产品推荐
|
||||
// @Summary 获取最新产品推荐
|
||||
// @Description 获取近一月内新增的产品,如果近一月内没有新增则返回最新的前10个产品
|
||||
// @Tags 统计
|
||||
// @Tags 统计公开接口
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param limit query int false "返回数量限制" default(10)
|
||||
@@ -1500,3 +1506,73 @@ func (h *StatisticsHandler) AdminGetTodayCertifiedEnterprises(c *gin.Context) {
|
||||
|
||||
h.responseBuilder.Success(c, result.Data, "获取今日认证企业列表成功")
|
||||
}
|
||||
|
||||
// GetPublicApiPopularityRanking 获取API受欢迎程度排行榜(公开接口)
|
||||
// @Summary 获取API受欢迎程度排行榜
|
||||
// @Description 获取API受欢迎程度排行榜,返回原始数据
|
||||
// @Tags 统计公开接口
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param period query string false "时间周期" Enums(today,month,total) default(month)
|
||||
// @Param limit query int false "返回数量" default(10)
|
||||
// @Success 200 {object} interfaces.APIResponse "获取成功"
|
||||
// @Failure 400 {object} interfaces.APIResponse "请求参数错误"
|
||||
// @Failure 500 {object} interfaces.APIResponse "服务器内部错误"
|
||||
// @Router /api/v1/statistics/api-popularity-ranking [get]
|
||||
func (h *StatisticsHandler) GetPublicApiPopularityRanking(c *gin.Context) {
|
||||
period := c.DefaultQuery("period", "month") // 默认月度
|
||||
limit := h.getIntQuery(c, "limit", 10) // 默认10条
|
||||
|
||||
// 调用应用服务获取API受欢迎程度排行榜
|
||||
result, err := h.statisticsAppService.AdminGetApiPopularityRanking(c.Request.Context(), period, limit)
|
||||
if err != nil {
|
||||
h.logger.Error("获取API受欢迎程度排行榜失败", zap.Error(err))
|
||||
h.responseBuilder.InternalError(c, "获取API受欢迎程度排行榜失败")
|
||||
return
|
||||
}
|
||||
|
||||
processedData := removeCallCountWhenDescriptionEqualsName(result.Data)
|
||||
h.responseBuilder.Success(c, processedData, "获取API受欢迎程度排行榜成功")
|
||||
}
|
||||
|
||||
// removeCallCountWhenDescriptionEqualsName 在公开排行榜数据中移除 product_id 和 call_count 字段
|
||||
func removeCallCountWhenDescriptionEqualsName(data interface{}) interface{} {
|
||||
dataMap, ok := data.(map[string]interface{})
|
||||
if !ok {
|
||||
return data
|
||||
}
|
||||
|
||||
rankingsRaw, ok := dataMap["rankings"]
|
||||
if !ok {
|
||||
return data
|
||||
}
|
||||
|
||||
switch rankings := rankingsRaw.(type) {
|
||||
case []map[string]interface{}:
|
||||
for _, item := range rankings {
|
||||
delete(item, "product_id")
|
||||
delete(item, "call_count")
|
||||
}
|
||||
|
||||
case []interface{}:
|
||||
for _, ranking := range rankings {
|
||||
item, ok := ranking.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
delete(item, "product_id")
|
||||
delete(item, "call_count")
|
||||
}
|
||||
}
|
||||
|
||||
return dataMap
|
||||
}
|
||||
|
||||
// getMapKeys 获取map的所有键(用于调试)
|
||||
func getMapKeys(m map[string]interface{}) []string {
|
||||
keys := make([]string, 0, len(m))
|
||||
for k := range m {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
||||
@@ -45,6 +45,12 @@ func (r *StatisticsRoutes) Register(router *sharedhttp.GinRouter) {
|
||||
{
|
||||
// 获取公开统计信息
|
||||
statistics.GET("/public", r.statisticsHandler.GetPublicStatistics)
|
||||
|
||||
// 获取API受欢迎榜单(公开接口)
|
||||
statistics.GET("/api-popularity-ranking", r.statisticsHandler.GetPublicApiPopularityRanking)
|
||||
|
||||
// 获取最新产品推荐(公开接口)
|
||||
statistics.GET("/latest-products", r.statisticsHandler.GetLatestProducts)
|
||||
}
|
||||
|
||||
// 用户统计接口 - 需要认证
|
||||
@@ -58,9 +64,6 @@ func (r *StatisticsRoutes) Register(router *sharedhttp.GinRouter) {
|
||||
userStats.GET("/consumption", r.statisticsHandler.GetConsumptionStatistics)
|
||||
userStats.GET("/recharge", r.statisticsHandler.GetRechargeStatistics)
|
||||
|
||||
// 获取最新产品推荐
|
||||
userStats.GET("/latest-products", r.statisticsHandler.GetLatestProducts)
|
||||
|
||||
// 获取指标列表
|
||||
userStats.GET("/metrics", r.statisticsHandler.GetMetrics)
|
||||
|
||||
|
||||
@@ -113,6 +113,12 @@ func (m *DailyRateLimitMiddleware) Handle() gin.HandlerFunc {
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
// 开发环境debug模式下跳过
|
||||
if m.config.Development.Debug {
|
||||
m.logger.Info("开发环境debug模式下跳过每日限流", zap.String("path", c.Request.URL.Path))
|
||||
c.Next()
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否在排除域名中
|
||||
host := c.Request.Host
|
||||
|
||||
@@ -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{
|
||||
|
||||
615
resources/dev-report/QYGLJ1U9_字段释义_全量.md
Normal file
615
resources/dev-report/QYGLJ1U9_字段释义_全量.md
Normal file
@@ -0,0 +1,615 @@
|
||||
# QYGLJ1U9 企业全景报告 · 字段释义
|
||||
|
||||
## 一、接口返回的补充字段
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ----------- | --------------------------------------------------------- |
|
||||
| `reportId` | 报告编号,用于打开报告页面或下载 PDF。 |
|
||||
| `reportUrl` | 报告访问地址(完整 URL 或站内相对路径,视部署配置而定)。 |
|
||||
|
||||
---
|
||||
|
||||
## 二、根结构
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --------------- | ---------------------------------------------------- |
|
||||
| `reportTime` | 报告生成时间。 |
|
||||
| `entName` | 企业名称。 |
|
||||
| `creditCode` | 统一社会信用代码。 |
|
||||
| `basic` | 登记注册与主体概况。 |
|
||||
| `basicList` | 多主体场景下的基础信息列表;单条结构以实际返回为准。 |
|
||||
| `branches` | 分支机构列表。 |
|
||||
| `shareholding` | 股权结构、股东、出资与股权变动等。 |
|
||||
| `controller` | 实际控制人。 |
|
||||
| `beneficiaries` | 最终受益人。 |
|
||||
| `investments` | 对外投资及法定代表人相关投资。 |
|
||||
| `guarantees` | 年报披露的对外担保。 |
|
||||
| `management` | 高管、法定代表人对外任职、从业人数、社保等。 |
|
||||
| `assets` | 各年度资产与经营概况。 |
|
||||
| `licenses` | 行政许可、许可变更、知识产权出质等。 |
|
||||
| `activities` | 招投标、网站或网店等经营动态。 |
|
||||
| `inspections` | 抽查检查记录。 |
|
||||
| `risks` | 司法、行政处罚、经营异常、税务、抵押出质等风险明细。 |
|
||||
| `timeline` | 工商登记变更时间线。 |
|
||||
| `listed` | 是否上市及上市相关公开信息。 |
|
||||
| `riskOverview` | 综合风险等级、得分、标签与维度命中情况。 |
|
||||
| `annualReports` | 企业年报公示信息列表(一般按报告年度降序)。 |
|
||||
| `taxViolations` | 税收违法记录条数与明细列表。 |
|
||||
| `ownTaxNotices` | 欠税公告条数与明细列表。 |
|
||||
|
||||
---
|
||||
|
||||
## 三、`basic` 主体概况
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ---------------------- | ---------------- |
|
||||
| `entName` | 企业名称 |
|
||||
| `creditCode` | 统一社会信用代码 |
|
||||
| `regNo` | 注册号 |
|
||||
| `orgCode` | 组织机构代码 |
|
||||
| `entType` | 企业类型(中文) |
|
||||
| `entTypeCode` | 企业类型代码 |
|
||||
| `entityTypeCode` | 实体类型代码 |
|
||||
| `establishDate` | 成立日期 |
|
||||
| `registeredCapital` | 注册资本(数值) |
|
||||
| `regCapCurrency` | 注册资本币种 |
|
||||
| `regCapCurrencyCode` | 注册资本币种代码 |
|
||||
| `regOrg` | 登记机关 |
|
||||
| `regOrgCode` | 登记机关代码 |
|
||||
| `regProvince` | 登记所在省 |
|
||||
| `provinceCode` | 省级区划代码 |
|
||||
| `regCity` | 登记所在市 |
|
||||
| `regCityCode` | 地市代码 |
|
||||
| `regDistrict` | 登记所在区县 |
|
||||
| `districtCode` | 区县代码 |
|
||||
| `address` | 住所 |
|
||||
| `postalCode` | 邮政编码 |
|
||||
| `legalRepresentative` | 法定代表人 |
|
||||
| `compositionForm` | 组成形式 |
|
||||
| `approvedBusinessItem` | 许可经营项目 |
|
||||
| `status` | 经营状态(中文) |
|
||||
| `statusCode` | 经营状态代码 |
|
||||
| `operationPeriodFrom` | 营业期限自 |
|
||||
| `operationPeriodTo` | 营业期限至 |
|
||||
| `approveDate` | 核准日期 |
|
||||
| `cancelDate` | 注销日期 |
|
||||
| `revokeDate` | 吊销日期 |
|
||||
| `cancelReason` | 注销原因 |
|
||||
| `revokeReason` | 吊销原因 |
|
||||
| `businessScope` | 经营范围 |
|
||||
| `lastAnnuReportYear` | 最近公示年报年度 |
|
||||
| `oldNames` | 曾用名列表 |
|
||||
|
||||
---
|
||||
|
||||
## 四、`branches[]` 分支机构
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ------------ | ---------------- |
|
||||
| `name` | 机构名称 |
|
||||
| `regNo` | 注册号 |
|
||||
| `creditCode` | 统一社会信用代码 |
|
||||
| `regOrg` | 登记机关 |
|
||||
|
||||
---
|
||||
|
||||
## 五、`shareholding` 股权与出资
|
||||
|
||||
| 字段 | 说明 |
|
||||
| -------------------------- | -------------------------- |
|
||||
| `shareholders` | 股东及出资明细列表 |
|
||||
| `shareholderCount` | 股东人数 |
|
||||
| `registeredCapital` | 注册资本 |
|
||||
| `currency` | 注册资本币种 |
|
||||
| `topHolderName` | 第一大股东名称 |
|
||||
| `topHolderPercent` | 第一大股东持股比例 |
|
||||
| `top5TotalPercent` | 前五名股东持股比例合计 |
|
||||
| `equityChanges` | 股权变更记录 |
|
||||
| `equityPledges` | 股权出质登记 |
|
||||
| `paidInDetails` | 实缴出资明细(与年报关联) |
|
||||
| `subscribedCapitalDetails` | 认缴出资明细(与年报关联) |
|
||||
| `hasEquityPledges` | 是否存在股权出质 |
|
||||
|
||||
### 股东 `shareholders[]` 常见字段
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ------------------------ | -------------------------------------- |
|
||||
| `name` | 股东名称 |
|
||||
| `type` | 股东类型(展示用语) |
|
||||
| `typeCode` | 股东类型代码 |
|
||||
| `ownershipPercent` | 持股比例 |
|
||||
| `subscribedAmount` | 认缴出资额 |
|
||||
| `paidAmount` | 实缴出资额 |
|
||||
| `subscribedCurrency` | 认缴币种 |
|
||||
| `subscribedCurrencyCode` | 认缴币种代码 |
|
||||
| `paidCurrency` | 实缴币种 |
|
||||
| `subscribedDate` | 认缴日期 |
|
||||
| `paidDate` | 实缴日期 |
|
||||
| `subscribedMethod` | 认缴出资方式 |
|
||||
| `subscribedMethodCode` | 认缴出资方式代码 |
|
||||
| `paidMethod` | 实缴出资方式 |
|
||||
| `creditCode` | 股东统一社会信用代码 |
|
||||
| `regNo` | 股东注册号 |
|
||||
| `isHistory` | 是否为历史股东 |
|
||||
| `source` | 数据说明(如来自不同产品线时可能出现) |
|
||||
|
||||
### `equityChanges[]` 股权变更
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ----------------- | -------------- |
|
||||
| `changeDate` | 变更日期 |
|
||||
| `shareholderName` | 股东 |
|
||||
| `percentBefore` | 变更前出资比例 |
|
||||
| `percentAfter` | 变更后出资比例 |
|
||||
| `source` | 信息来源说明 |
|
||||
|
||||
### `equityPledges[]` 股权出质
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --------------- | ------------ |
|
||||
| `regNo` | 登记编号 |
|
||||
| `pledgor` | 出质人 |
|
||||
| `pledgorIdNo` | 出质人证件号 |
|
||||
| `pledgedAmount` | 出质股权数额 |
|
||||
| `pledgee` | 质权人 |
|
||||
| `pledgeeIdNo` | 质权人证件号 |
|
||||
| `regDate` | 登记日期 |
|
||||
| `status` | 登记状态 |
|
||||
| `publicDate` | 公示日期 |
|
||||
|
||||
### `paidInDetails[]` 实缴明细
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ----------------- | ------------ |
|
||||
| `yearReportId` | 关联年报标识 |
|
||||
| `investor` | 股东/投资人 |
|
||||
| `paidDate` | 实缴日期 |
|
||||
| `paidMethod` | 实缴方式 |
|
||||
| `accumulatedPaid` | 累计实缴额 |
|
||||
|
||||
### `subscribedCapitalDetails[]` 认缴明细
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ----------------------- | ------------ |
|
||||
| `yearReportId` | 关联年报标识 |
|
||||
| `investor` | 股东/投资人 |
|
||||
| `subscribedDate` | 认缴日期 |
|
||||
| `subscribedMethod` | 认缴方式 |
|
||||
| `accumulatedSubscribed` | 累计认缴额 |
|
||||
|
||||
---
|
||||
|
||||
## 六、`controller` 实际控制人
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --------- | ------------------------------------------ |
|
||||
| `id` | 实控人标识 |
|
||||
| `name` | 姓名或名称 |
|
||||
| `type` | 类型 |
|
||||
| `percent` | 持股或控制比例 |
|
||||
| `path` | 控制路径(含节点、连线等,结构以实际为准) |
|
||||
| `reason` | 备注说明 |
|
||||
| `source` | 信息来源说明 |
|
||||
|
||||
---
|
||||
|
||||
## 七、`beneficiaries[]` 最终受益人
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ---------- | -------------------------- |
|
||||
| `id` | 受益人标识 |
|
||||
| `name` | 名称 |
|
||||
| `type` | 受益人类型(展示) |
|
||||
| `typeCode` | 类型代码 |
|
||||
| `percent` | 受益权比例 |
|
||||
| `path` | 受益路径(结构以实际为准) |
|
||||
| `reason` | 认定理由等 |
|
||||
| `source` | 信息来源说明 |
|
||||
|
||||
---
|
||||
|
||||
## 八、`investments` 对外投资
|
||||
|
||||
| 字段 | 说明 |
|
||||
| -------------------------------- | -------------------------------- |
|
||||
| `totalCount` | 对外投资企业数量 |
|
||||
| `totalAmount` | 对外认缴出资金额合计 |
|
||||
| `list` | 对外投资企业列表 |
|
||||
| `legalRepresentativeInvestments` | 法定代表人对外投资或任职相关企业 |
|
||||
|
||||
### `list[]` 常见字段
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ---------------- | ---------------- |
|
||||
| `entName` | 被投资企业名称 |
|
||||
| `creditCode` | 统一社会信用代码 |
|
||||
| `regNo` | 注册号 |
|
||||
| `entType` | 企业类型 |
|
||||
| `regCap` | 注册资本 |
|
||||
| `regCapCurrency` | 注册资本币种 |
|
||||
| `entStatus` | 企业经营状态 |
|
||||
| `regOrg` | 登记机关 |
|
||||
| `establishDate` | 成立日期 |
|
||||
| `investAmount` | 认缴投资额 |
|
||||
| `investCurrency` | 投资币种 |
|
||||
| `investPercent` | 投资比例 |
|
||||
| `investMethod` | 投资方式 |
|
||||
| `isListed` | 是否上市公司 |
|
||||
| `source` | 信息来源说明 |
|
||||
|
||||
### `legalRepresentativeInvestments[]` 常见字段
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --------------- | ---------------- |
|
||||
| `entName` | 企业名称 |
|
||||
| `creditCode` | 统一社会信用代码 |
|
||||
| `regNo` | 注册号 |
|
||||
| `entType` | 企业类型 |
|
||||
| `regCap` | 注册资本 |
|
||||
| `entStatus` | 经营状态 |
|
||||
| `regOrg` | 登记机关 |
|
||||
| `establishDate` | 成立日期 |
|
||||
| `investAmount` | 投资额 |
|
||||
| `investPercent` | 投资比例 |
|
||||
| `investMethod` | 投资方式 |
|
||||
|
||||
---
|
||||
|
||||
## 九、`guarantees[]` 对外担保
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ----------------- | ------------------- |
|
||||
| `yearReportId` | 关联年报 |
|
||||
| `mortgagor` | 债务人/抵押相关主体 |
|
||||
| `creditor` | 债权人 |
|
||||
| `principalAmount` | 主债权金额 |
|
||||
| `principalKind` | 主债权种类 |
|
||||
| `guaranteeType` | 担保方式 |
|
||||
| `periodFrom` | 履行债务起始日 |
|
||||
| `periodTo` | 履行债务截止日 |
|
||||
| `guaranteePeriod` | 保证期间 |
|
||||
|
||||
---
|
||||
|
||||
## 十、`management` 人员与社保
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ----------------------------------- | -------------------------------------------------- |
|
||||
| `executives` | 主要管理人员 |
|
||||
| `legalRepresentativeOtherPositions` | 法定代表人在其他企业的任职 |
|
||||
| `employeeCount` | 从业人数 |
|
||||
| `femaleEmployeeCount` | 女性从业人数 |
|
||||
| `socialSecurity` | 单位参保缴费等社会保险信息(字段名以实际返回为准) |
|
||||
|
||||
### `executives[]`
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ---------- | ---- |
|
||||
| `name` | 姓名 |
|
||||
| `position` | 职务 |
|
||||
|
||||
### `legalRepresentativeOtherPositions[]`
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ------------ | ---------------- |
|
||||
| `entName` | 任职单位名称 |
|
||||
| `position` | 职务 |
|
||||
| `name` | 人员姓名 |
|
||||
| `regNo` | 注册号 |
|
||||
| `creditCode` | 统一社会信用代码 |
|
||||
| `entStatus` | 企业状态 |
|
||||
|
||||
---
|
||||
|
||||
## 十一、`assets` 资产与经营
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ------- | -------------------------- |
|
||||
| `years` | 按年报年度的资产与损益摘要 |
|
||||
|
||||
### `years[]`
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --------------------- | ------------------ |
|
||||
| `year` | 年报年度 |
|
||||
| `reportDate` | 年报关联标识 |
|
||||
| `assetTotal` | 资产总额 |
|
||||
| `revenueTotal` | 销售(营业)总收入 |
|
||||
| `mainBusinessRevenue` | 主营业务收入 |
|
||||
| `taxTotal` | 纳税总额 |
|
||||
| `equityTotal` | 所有者权益合计 |
|
||||
| `profitTotal` | 利润总额 |
|
||||
| `netProfit` | 净利润 |
|
||||
| `liabilityTotal` | 负债总额 |
|
||||
| `businessStatus` | 企业经营状态 |
|
||||
| `mainBusiness` | 主营业务 |
|
||||
|
||||
---
|
||||
|
||||
## 十二、`licenses` 许可与知识产权
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --------------- | ------------------------------ |
|
||||
| `permits` | 行政许可 |
|
||||
| `permitChanges` | 行政许可变更 |
|
||||
| `ipPledges` | 知识产权出质 |
|
||||
| `otherLicenses` | 其他许可(预留,常见为空列表) |
|
||||
|
||||
### `permits[]`
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --------- | -------- |
|
||||
| `name` | 许可名称 |
|
||||
| `valFrom` | 有效期自 |
|
||||
| `valTo` | 有效期至 |
|
||||
| `licAnth` | 许可机关 |
|
||||
| `licItem` | 许可内容 |
|
||||
|
||||
### `permitChanges[]`
|
||||
|
||||
| 字段 | 说明 |
|
||||
| -------------- | ---------- |
|
||||
| `changeDate` | 变更日期 |
|
||||
| `detailBefore` | 变更前内容 |
|
||||
| `detailAfter` | 变更后内容 |
|
||||
| `changeType` | 变更事项 |
|
||||
|
||||
---
|
||||
|
||||
## 十三、`activities` 经营动态
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ---------- | -------------- |
|
||||
| `bids` | 招投标信息 |
|
||||
| `websites` | 网站或网店信息 |
|
||||
|
||||
---
|
||||
|
||||
## 十四、`inspections[]` 抽查检查
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ------------- | -------- |
|
||||
| `dataType` | 抽查类型 |
|
||||
| `regOrg` | 检查机关 |
|
||||
| `inspectDate` | 检查日期 |
|
||||
| `result` | 检查结果 |
|
||||
|
||||
---
|
||||
|
||||
## 十五、`risks` 风险与合规
|
||||
|
||||
### 汇总标志
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ---------------------- | -------------------------------------------------------- |
|
||||
| `riskLevel` | 风险等级文字(低/中/高),与 `riskOverview` 计算口径不同 |
|
||||
| `riskScore` | 风险分值,与 `riskOverview` 计算口径不同 |
|
||||
| `hasCourtJudgments` | 是否存在裁判文书相关记录 |
|
||||
| `hasJudicialAssists` | 是否存在司法协助 |
|
||||
| `hasDishonestDebtors` | 是否存在失信被执行人 |
|
||||
| `hasLimitHighDebtors` | 是否存在限制高消费被执行人 |
|
||||
| `hasAdminPenalty` | 是否存在行政处罚 |
|
||||
| `hasException` | 是否存在经营异常名录记录 |
|
||||
| `hasSeriousIllegal` | 是否存在严重违法失信等记录 |
|
||||
| `hasTaxOwing` | 是否存在欠税记录 |
|
||||
| `hasSeriousTaxIllegal` | 是否存在重大税收违法 |
|
||||
| `hasMortgage` | 是否存在动产抵押 |
|
||||
| `hasEquityPledges` | 是否存在股权出质 |
|
||||
| `hasQuickCancel` | 是否存在简易注销相关公告 |
|
||||
| `dishonestDebtorCount` | 失信记录条数 |
|
||||
| `limitHighDebtorCount` | 限高记录条数 |
|
||||
|
||||
### 列表类字段(内容为监管或司法公开原始结构,子字段以实际为准)
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --------------------- | ---------------------------------- |
|
||||
| `courtJudgments` | 裁判文书 |
|
||||
| `judicialAssists` | 司法协助 |
|
||||
| `dishonestDebtors` | 失信被执行人(已做字段映射的列表) |
|
||||
| `limitHighDebtors` | 限制高消费被执行人 |
|
||||
| `adminPenalties` | 行政处罚 |
|
||||
| `adminPenaltyUpdates` | 行政处罚变更或补充 |
|
||||
| `exceptions` | 经营异常 |
|
||||
| `seriousIllegals` | 严重违法 |
|
||||
| `mortgages` | 动产抵押 |
|
||||
|
||||
### `dishonestDebtors[]` 失信被执行人(映射后)
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ------------------- | -------------------- |
|
||||
| `id` | 记录标识 |
|
||||
| `obligation` | 生效法律文书确定义务 |
|
||||
| `judgmentAmountEst` | 判决履行金额(估计) |
|
||||
| `discreditDetail` | 失信行为情形 |
|
||||
| `execCourt` | 执行法院 |
|
||||
| `caseNo` | 案号 |
|
||||
| `execBasisNo` | 执行依据文号 |
|
||||
| `performanceStatus` | 履行情况 |
|
||||
| `execBasisOrg` | 执行依据作出单位 |
|
||||
| `publishDate` | 发布日期 |
|
||||
| `gender` | 性别 |
|
||||
| `filingDate` | 立案日期 |
|
||||
| `province` | 省份 |
|
||||
|
||||
### `adminPenaltyUpdates[]`
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --------------- | -------- |
|
||||
| `updateDate` | 更新日期 |
|
||||
| `updateContent` | 更新内容 |
|
||||
|
||||
### `mortgages[]` 动产抵押
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ------------------ | ---------------- |
|
||||
| `regNo` | 登记编号 |
|
||||
| `regDate` | 登记日期 |
|
||||
| `regOrg` | 登记机关 |
|
||||
| `guaranteedAmount` | 被担保主债权数额 |
|
||||
| `status` | 登记状态 |
|
||||
| `publicDate` | 公示日期 |
|
||||
| `details` | 登记公示信息摘要 |
|
||||
| `mortgagees` | 抵押权人 |
|
||||
| `collaterals` | 抵押物 |
|
||||
| `debts` | 被担保主债权 |
|
||||
| `alterations` | 变更 |
|
||||
| `cancellations` | 注销 |
|
||||
|
||||
### `litigation` 涉诉案件分类
|
||||
|
||||
含行政、执行、保全、民事、刑事、破产、管辖、赔偿等类别(键名为英文分类代码)。每一类下为:
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ------- | -------------- |
|
||||
| `count` | 该类别案件数量 |
|
||||
| `cases` | 该类别案件列表 |
|
||||
|
||||
案件 `cases[]` 常见统一字段:
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --------------- | -------------- |
|
||||
| `caseNo` | 案号 |
|
||||
| `court` | 法院 |
|
||||
| `region` | 地域 |
|
||||
| `filingDate` | 立案日期 |
|
||||
| `judgmentDate` | 裁判日期 |
|
||||
| `trialLevel` | 审理程序 |
|
||||
| `caseType` | 案件类型 |
|
||||
| `status` | 案件进展 |
|
||||
| `cause` | 案由 |
|
||||
| `amount` | 争议金额或标的 |
|
||||
| `victoryResult` | 裁判结果侧记 |
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ------------ | -------------------- |
|
||||
| `totalCases` | 上述各类案件合计条数 |
|
||||
|
||||
### `quickCancel` 简易注销
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ---------------- | ---------------- |
|
||||
| `entName` | 企业名称 |
|
||||
| `creditCode` | 统一社会信用代码 |
|
||||
| `regNo` | 注册号 |
|
||||
| `regOrg` | 登记机关 |
|
||||
| `noticeFromDate` | 公告开始日 |
|
||||
| `noticeToDate` | 公告结束日 |
|
||||
| `cancelResult` | 简易注销结果 |
|
||||
| `dissents` | 异议信息列表 |
|
||||
|
||||
异议项:`dissentOrg` 异议提出单位、`dissentDes` 异议内容、`dissentDate` 异议日期。
|
||||
|
||||
### `liquidation` 清算
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ----------- | -------------- |
|
||||
| `principal` | 清算组负责人 |
|
||||
| `members` | 清算组成员名单 |
|
||||
|
||||
### `taxRecords` 税务相关原始汇总
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ------------------- | -------------------------- |
|
||||
| `taxLevelAYears` | 纳税信用等级等相关年度信息 |
|
||||
| `seriousTaxIllegal` | 重大税收违法案件 |
|
||||
| `taxOwings` | 欠税信息 |
|
||||
|
||||
---
|
||||
|
||||
## 十六、`timeline[]` 工商变更时间线
|
||||
|
||||
| 字段 | 说明 |
|
||||
| -------------- | ------------ |
|
||||
| `date` | 变更日期 |
|
||||
| `type` | 变更事项类型 |
|
||||
| `title` | 变更事项标题 |
|
||||
| `detailBefore` | 变更前内容 |
|
||||
| `detailAfter` | 变更后内容 |
|
||||
| `source` | 信息来源 |
|
||||
|
||||
---
|
||||
|
||||
## 十七、`listed` 上市信息
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ----------------- | ---------------------------- |
|
||||
| `isListed` | 是否上市企业 |
|
||||
| `company` | 上市主体工商登记相关信息 |
|
||||
| `stock` | 股票公开信息;无数据时可为空 |
|
||||
| `topShareholders` | 前十大股东公开信息 |
|
||||
| `listedManagers` | 上市公司高管公开信息 |
|
||||
|
||||
### `company`
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ------------ | ---------------- |
|
||||
| `bizScope` | 经营范围 |
|
||||
| `creditCode` | 统一社会信用代码 |
|
||||
| `regAddr` | 注册地址 |
|
||||
| `regCapital` | 注册资本 |
|
||||
| `orgCode` | 组织机构代码 |
|
||||
| `cur` | 币种代码 |
|
||||
| `curName` | 币种名称 |
|
||||
|
||||
---
|
||||
|
||||
## 十八、`riskOverview` 综合风险(推荐用于总览展示)
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ----------- | ------------------------------------------------------- |
|
||||
| `riskLevel` | 综合风险等级:低 / 中 / 高 |
|
||||
| `riskScore` | 综合风险得分(0–100,分数越高表示综合风险越低) |
|
||||
| `tags` | 命中风险点的简短标签 |
|
||||
| `items` | 各检查维度是否命中,`name` 为维度名称,`hit` 为是否命中 |
|
||||
|
||||
---
|
||||
|
||||
## 十九、`annualReports[]` 企业年报
|
||||
|
||||
每条为一年度公示年报,字段名为**小驼峰**,具体键集合随公示数据扩展而变化。常见包含网站网店、股东及出资、对外投资、社保、对外担保、股权变更、年报修改等子模块(多为对象或数组嵌套)。
|
||||
|
||||
---
|
||||
|
||||
## 二十、`taxViolations` 税收违法
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ------- | ------------------------------------------------ |
|
||||
| `total` | 记录条数 |
|
||||
| `items` | 税收违法案件列表,字段名为小驼峰,以实际返回为准 |
|
||||
|
||||
常见字段示例:企业名称、纳税人识别号、案件性质、违法事实、处罚依据、违法起止时间、公示机关、检查机关等。
|
||||
|
||||
---
|
||||
|
||||
## 二十一、`ownTaxNotices` 欠税公告
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ------- | ------------ |
|
||||
| `total` | 公告条数 |
|
||||
| `items` | 欠税公告明细 |
|
||||
|
||||
### `items[]`
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ------------------ | ---------------- |
|
||||
| `taxIdNumber` | 纳税人识别号 |
|
||||
| `taxpayerName` | 纳税人名称 |
|
||||
| `taxCategory` | 欠缴税种 |
|
||||
| `ownTaxBalance` | 欠税余额 |
|
||||
| `ownTaxAmount` | 欠税金额 |
|
||||
| `newOwnTaxBalance` | 新发生欠税余额 |
|
||||
| `taxType` | 税务记录类型 |
|
||||
| `publishDate` | 发布日期 |
|
||||
| `department` | 主管税务机关 |
|
||||
| `location` | 经营地点 |
|
||||
| `legalPersonName` | 法定代表人 |
|
||||
| `personIdNumber` | 身份证件号码 |
|
||||
| `personIdName` | 身份证件名称栏目 |
|
||||
| `taxpayerType` | 纳税人类型 |
|
||||
| `regType` | 登记注册类型 |
|
||||
|
||||
---
|
||||
|
||||
## 二十二、`basicList[]`
|
||||
|
||||
企业关联的多个登记主体基础信息时出现的列表,单条对象字段以实际返回为准。
|
||||
203
resources/dev-report/QYGLJ1U9_客户字段说明.md
Normal file
203
resources/dev-report/QYGLJ1U9_客户字段说明.md
Normal file
@@ -0,0 +1,203 @@
|
||||
# QYGLJ1U9 企业全景报告字段说明
|
||||
|
||||
## 一、返回对象总览
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| --------------- | ------------- | -------------------------------------------------- |
|
||||
| `reportId` | string | 报告编号,可用于后续按编号访问报告页面或下载 PDF。 |
|
||||
| `reportUrl` | string | 报告访问链接。 |
|
||||
| `reportTime` | string | 报告生成时间,格式示例:`2026-03-21 19:30:45`。 |
|
||||
| `entName` | string | 企业名称。 |
|
||||
| `creditCode` | string | 统一社会信用代码。 |
|
||||
| `basic` | object | 企业主体基础信息。 |
|
||||
| `branches` | array | 分支机构列表。 |
|
||||
| `shareholding` | object | 股权结构与股东信息。 |
|
||||
| `controller` | object / null | 实际控制人信息。 |
|
||||
| `beneficiaries` | array | 最终受益人列表。 |
|
||||
| `investments` | object | 对外投资信息。 |
|
||||
| `guarantees` | array | 对外担保信息。 |
|
||||
| `management` | object | 高管、人员和社保相关信息。 |
|
||||
| `assets` | object | 资产经营类年度信息。 |
|
||||
| `licenses` | object | 行政许可、许可变更、知识产权出质等。 |
|
||||
| `activities` | object | 招投标、网站网店等经营活动信息。 |
|
||||
| `inspections` | array | 抽查检查信息。 |
|
||||
| `risks` | object | 风险与合规信息。 |
|
||||
| `timeline` | array | 工商变更时间线。 |
|
||||
| `listed` | object / null | 上市相关信息。 |
|
||||
| `riskOverview` | object | 风险总览(等级、分值、标签)。 |
|
||||
| `annualReports` | array | 企业年报列表。 |
|
||||
| `taxViolations` | object | 税收违法信息,结构为 `{ total, items }`。 |
|
||||
| `ownTaxNotices` | object | 欠税公告信息,结构为 `{ total, items }`。 |
|
||||
|
||||
---
|
||||
|
||||
## 二、核心字段说明
|
||||
|
||||
### 1) `basic` 企业主体基础信息
|
||||
|
||||
常见字段如下:
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ------------------------------------------- | ---------------- |
|
||||
| `entName` | 企业名称 |
|
||||
| `creditCode` | 统一社会信用代码 |
|
||||
| `regNo` | 注册号 |
|
||||
| `orgCode` | 组织机构代码 |
|
||||
| `entType` | 企业类型 |
|
||||
| `establishDate` | 成立日期 |
|
||||
| `registeredCapital` | 注册资本 |
|
||||
| `regCapCurrency` | 注册资本币种 |
|
||||
| `legalRepresentative` | 法定代表人 |
|
||||
| `status` | 经营状态 |
|
||||
| `operationPeriodFrom` / `operationPeriodTo` | 营业期限起止 |
|
||||
| `regOrg` | 登记机关 |
|
||||
| `address` | 企业地址 |
|
||||
| `businessScope` | 经营范围 |
|
||||
| `oldNames` | 曾用名列表 |
|
||||
|
||||
### 2) `shareholding` 股权结构
|
||||
|
||||
| 字段 | 说明 |
|
||||
| -------------------------- | ---------------------- |
|
||||
| `shareholders` | 股东列表 |
|
||||
| `shareholderCount` | 股东人数 |
|
||||
| `topHolderName` | 第一大股东名称 |
|
||||
| `topHolderPercent` | 第一大股东持股比例 |
|
||||
| `top5TotalPercent` | 前五大股东持股比例合计 |
|
||||
| `equityChanges` | 股权变更记录 |
|
||||
| `equityPledges` | 股权出质记录 |
|
||||
| `paidInDetails` | 实缴出资明细 |
|
||||
| `subscribedCapitalDetails` | 认缴出资明细 |
|
||||
|
||||
`shareholders` 常见子字段:
|
||||
`name`、`type`、`ownershipPercent`、`subscribedAmount`、`paidAmount`、`subscribedDate`、`paidDate`。
|
||||
|
||||
### 3) `controller` 实际控制人
|
||||
|
||||
常见字段:`id`、`name`、`type`、`percent`、`path`、`reason`。
|
||||
|
||||
### 4) `beneficiaries` 最终受益人
|
||||
|
||||
每条常见字段:`id`、`name`、`type`、`percent`、`path`、`reason`。
|
||||
|
||||
### 5) `investments` 对外投资
|
||||
|
||||
| 字段 | 说明 |
|
||||
| -------------------------------- | ------------------------ |
|
||||
| `totalCount` | 对外投资企业数量 |
|
||||
| `totalAmount` | 对外投资金额汇总(如有) |
|
||||
| `list` | 对外投资企业列表 |
|
||||
| `legalRepresentativeInvestments` | 法定代表人对外投资列表 |
|
||||
|
||||
`list` 常见子字段:`entName`、`creditCode`、`entStatus`、`regCap`、`investAmount`、`investPercent`。
|
||||
|
||||
### 6) `management` 管理层与人员信息
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ----------------------------------- | ---------------------- |
|
||||
| `executives` | 高管列表(姓名、职务) |
|
||||
| `legalRepresentativeOtherPositions` | 法人对外任职信息 |
|
||||
| `employeeCount` | 员工人数 |
|
||||
| `femaleEmployeeCount` | 女性员工人数 |
|
||||
| `socialSecurity` | 社保相关字段集合 |
|
||||
|
||||
### 7) `assets` 资产经营信息
|
||||
|
||||
`assets.years` 为按年度的经营数据,常见字段:
|
||||
`year`、`assetTotal`、`revenueTotal`、`mainBusinessRevenue`、`taxTotal`、`equityTotal`、`profitTotal`、`netProfit`、`liabilityTotal`。
|
||||
|
||||
### 8) `licenses` 许可与资质信息
|
||||
|
||||
| 字段 | 说明 |
|
||||
| --------------- | ---------------- |
|
||||
| `permits` | 行政许可列表 |
|
||||
| `permitChanges` | 行政许可变更列表 |
|
||||
| `ipPledges` | 知识产权出质列表 |
|
||||
| `otherLicenses` | 其他许可信息 |
|
||||
|
||||
### 9) `activities` 经营活动信息
|
||||
|
||||
| 字段 | 说明 |
|
||||
| ---------- | -------------- |
|
||||
| `bids` | 招投标信息 |
|
||||
| `websites` | 网站或网店信息 |
|
||||
|
||||
### 10) `inspections` 抽查检查
|
||||
|
||||
每条常见字段:`dataType`、`regOrg`、`inspectDate`、`result`。
|
||||
|
||||
---
|
||||
|
||||
## 三、风险相关字段
|
||||
|
||||
### 1) `riskOverview` 风险总览(建议用于首页展示)
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
| ----------- | ------ | ---------------------------------------------------- |
|
||||
| `riskLevel` | string | 风险等级:`低` / `中` / `高`。 |
|
||||
| `riskScore` | number | 风险分值(0-100)。 |
|
||||
| `tags` | array | 风险标签列表。 |
|
||||
| `items` | array | 各类风险项命中情况,元素结构通常为 `{ name, hit }`。 |
|
||||
|
||||
### 2) `risks` 风险详情
|
||||
|
||||
常见布尔字段:
|
||||
`hasCourtJudgments`、`hasJudicialAssists`、`hasDishonestDebtors`、`hasLimitHighDebtors`、`hasAdminPenalty`、`hasException`、`hasSeriousIllegal`、`hasTaxOwing`、`hasSeriousTaxIllegal`、`hasMortgage`、`hasEquityPledges`、`hasQuickCancel`。
|
||||
|
||||
常见明细字段:
|
||||
`dishonestDebtors`、`limitHighDebtors`、`litigation`、`adminPenalties`、`adminPenaltyUpdates`、`exceptions`、`seriousIllegals`、`mortgages`、`taxRecords`、`courtJudgments`、`judicialAssists`、`quickCancel`、`liquidation`。
|
||||
|
||||
---
|
||||
|
||||
## 四、年报与税务字段
|
||||
|
||||
### 1) `annualReports` 企业年报列表
|
||||
|
||||
每个元素代表一个年度年报,字段较多,常见包括:
|
||||
|
||||
- 基础信息(如年度、企业基本经营情况)
|
||||
- 股东与出资信息
|
||||
- 对外投资信息
|
||||
- 网站网店信息
|
||||
- 社保信息
|
||||
- 对外担保信息
|
||||
- 股权变更信息
|
||||
- 年报变更信息
|
||||
|
||||
### 2) `taxViolations` 税收违法信息
|
||||
|
||||
结构示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"total": 2,
|
||||
"items": [
|
||||
{
|
||||
"entityName": "示例企业",
|
||||
"taxpayerCode": "xxxx",
|
||||
"illegalFact": "......",
|
||||
"publishDepartment": "......",
|
||||
"illegalTime": "2025-06-12"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 3) `ownTaxNotices` 欠税公告信息
|
||||
|
||||
结构示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"total": 1,
|
||||
"items": [
|
||||
{
|
||||
"taxpayerName": "示例企业",
|
||||
"taxIdNumber": "xxxx",
|
||||
"taxCategory": "增值税",
|
||||
"ownTaxBalance": "100000",
|
||||
"publishDate": "2025-12-01"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
@@ -3434,6 +3434,14 @@
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (
|
||||
!reportId &&
|
||||
reportData &&
|
||||
reportData.reportId &&
|
||||
typeof reportData.reportId === "string"
|
||||
) {
|
||||
reportId = reportData.reportId;
|
||||
}
|
||||
if (!reportId) {
|
||||
console.error(
|
||||
"无法从当前 URL 解析报告编号,路径为",
|
||||
|
||||
Reference in New Issue
Block a user