This commit is contained in:
2026-06-20 20:49:21 +08:00
16 changed files with 254 additions and 153 deletions

View File

@@ -255,28 +255,17 @@ func collectAPIData(ctx context.Context, params dto.DWBG8B4DReq, deps *processor
return apiData
}
// callProcessor 调用指定的处理器
// callProcessor 调用指定的处理器(走注册表,含白名单包装)
func callProcessor(ctx context.Context, apiCode string, params []byte, deps *processors.ProcessorDependencies) (interface{}, error) {
// 通过CombService获取处理器
if combSvc, ok := deps.CombService.(interface {
GetProcessor(apiCode string) (processors.ProcessorFunc, bool)
}); ok {
processor, exists := combSvc.GetProcessor(apiCode)
if !exists {
return nil, fmt.Errorf("未找到处理器: %s", apiCode)
}
respBytes, err := processor(ctx, params, deps)
if err != nil {
return nil, err
}
var data interface{}
if err := json.Unmarshal(respBytes, &data); err != nil {
return nil, fmt.Errorf("解析响应失败: %w", err)
}
return data, nil
respBytes, err := processors.InvokeRegisteredProcessor(ctx, apiCode, params, deps)
if err != nil {
return nil, err
}
return nil, fmt.Errorf("无法获取处理器: %sCombService不支持GetProcessor方法", apiCode)
var data interface{}
if err := json.Unmarshal(respBytes, &data); err != nil {
return nil, fmt.Errorf("解析响应失败: %w", err)
}
return data, nil
}
// exportAPIDataToJSON 将API数据导出为JSON文件方便调试

View File

@@ -0,0 +1,32 @@
package processors
import (
"context"
"fmt"
)
// ProcessorRegistry 已注册处理器查询(由 CombService 实现)
type ProcessorRegistry interface {
GetProcessor(apiCode string) (ProcessorFunc, bool)
}
// InvokeRegisteredProcessor 通过注册表调用处理器(含白名单包装),聚合处理器内部转接应使用此方法。
func InvokeRegisteredProcessor(
ctx context.Context,
apiCode string,
params []byte,
deps *ProcessorDependencies,
) ([]byte, error) {
if deps == nil || deps.CombService == nil {
return nil, fmt.Errorf("CombService 未配置,无法调用处理器: %s", apiCode)
}
registry, ok := deps.CombService.(ProcessorRegistry)
if !ok {
return nil, fmt.Errorf("CombService 不支持 GetProcessor无法调用处理器: %s", apiCode)
}
processor, exists := registry.GetProcessor(apiCode)
if !exists {
return nil, fmt.Errorf("未找到处理器: %s", apiCode)
}
return processor(ctx, params, deps)
}

View File

@@ -349,29 +349,17 @@ func collectAPIData(ctx context.Context, params dto.PDFG01GZReq, deps *processor
return apiData
}
// callProcessor 调用指定的处理器
// callProcessor 调用指定的处理器(走注册表,含白名单包装)
func callProcessor(ctx context.Context, apiCode string, params []byte, deps *processors.ProcessorDependencies) (interface{}, error) {
// 通过CombService获取处理器
if combSvc, ok := deps.CombService.(interface {
GetProcessor(apiCode string) (processors.ProcessorFunc, bool)
}); ok {
processor, exists := combSvc.GetProcessor(apiCode)
if !exists {
return nil, fmt.Errorf("未找到处理器: %s", apiCode)
}
respBytes, err := processor(ctx, params, deps)
if err != nil {
return nil, err
}
var data interface{}
if err := json.Unmarshal(respBytes, &data); err != nil {
return nil, fmt.Errorf("解析响应失败: %w", err)
}
return data, nil
respBytes, err := processors.InvokeRegisteredProcessor(ctx, apiCode, params, deps)
if err != nil {
return nil, err
}
// 如果无法通过CombService获取返回错误
return nil, fmt.Errorf("无法获取处理器: %sCombService不支持GetProcessor方法", apiCode)
var data interface{}
if err := json.Unmarshal(respBytes, &data); err != nil {
return nil, fmt.Errorf("解析响应失败: %w", err)
}
return data, nil
}
// formatDataForPDF 格式化数据为PDF生成服务需要的格式

View File

@@ -25,7 +25,7 @@ func ProcessQCXG4D2ERequest(ctx context.Context, params []byte, deps *processors
return nil, errors.Join(processors.ErrSystem, err)
}
raw, err := ProcessQCXGM4CLRequest(ctx, m4clParams, deps)
raw, err := processors.InvokeRegisteredProcessor(ctx, "QCXGM4CL", m4clParams, deps)
if err != nil {
return nil, err
}

View File

@@ -28,7 +28,7 @@ func ProcessQCXG5F3ARequest(ctx context.Context, params []byte, deps *processors
return nil, errors.Join(processors.ErrSystem, err)
}
raw, err := ProcessQCXGM4CLRequest(ctx, m4clParams, deps)
raw, err := processors.InvokeRegisteredProcessor(ctx, "QCXGM4CL", m4clParams, deps)
if err != nil {
return nil, err
}

View File

@@ -28,7 +28,7 @@ func ProcessQCXG9P1CRequest(ctx context.Context, params []byte, deps *processors
return nil, errors.Join(processors.ErrSystem, err)
}
raw, err := ProcessQCXGM4CLRequest(ctx, m4clParams, deps)
raw, err := processors.InvokeRegisteredProcessor(ctx, "QCXGM4CL", m4clParams, deps)
if err != nil {
return nil, err
}

View File

@@ -50,7 +50,7 @@ func ProcessQYGL3F8ERequest(ctx context.Context, params []byte, deps *processors
return nil, errors.Join(processors.ErrSystem, err)
}
b4c0Response, err := ProcessQYGL6S1BRequest(ctx, b4c0ParamsBytes, deps)
b4c0Response, err := processors.InvokeRegisteredProcessor(ctx, "QYGL6S1B", b4c0ParamsBytes, deps)
if err != nil {
log.Error("QYGL3F8E调用QYGL6S1B失败", zap.Error(err))
return nil, err // 错误已经是处理器标准错误,直接返回
@@ -620,25 +620,7 @@ func callProcessorSafely(ctx context.Context, processorType, entCode string, dep
}
var response []byte
switch processorType {
case "QYGL5A3C":
response, err = ProcessQYGL5A3CRequest(ctx, paramsBytes, deps)
case "QYGL8B4D":
response, err = ProcessQYGL8B4DRequest(ctx, paramsBytes, deps)
case "QYGL9E2F":
response, err = ProcessQYGL9E2FRequest(ctx, paramsBytes, deps)
case "QYGL7C1A":
response, err = ProcessQYGL7C1ARequest(ctx, paramsBytes, deps)
case "QYGL7D9A":
response, err = ProcessQYGL7D9ARequest(ctx, paramsBytes, deps)
case "QYGL4B2E":
response, err = ProcessQYGL4B2ERequest(ctx, paramsBytes, deps)
default:
log.Warn("QYGL3F8E未知的处理器类型",
zap.String("processor_type", processorType),
)
return map[string]interface{}{}
}
response, err = processors.InvokeRegisteredProcessor(ctx, processorType, paramsBytes, deps)
if err != nil {
// 如果是查询为空错误,返回空对象
@@ -681,7 +663,7 @@ func callQYGL5S1IProcessorSafely(ctx context.Context, entCode string, entName st
if err != nil {
return map[string]interface{}{}
}
response, err := ProcessQYGL5S1IRequest(ctx, paramsBytes, deps)
response, err := processors.InvokeRegisteredProcessor(ctx, "QYGL5S1I", paramsBytes, deps)
if err != nil {
return map[string]interface{}{}
}

View File

@@ -39,7 +39,7 @@ func ProcessQYGLJ1U9Request(ctx context.Context, params []byte, deps *processors
resultsCh := make(chan apiResult, 7)
var wg sync.WaitGroup
call := func(key string, req interface{}, fn func(context.Context, []byte, *processors.ProcessorDependencies) ([]byte, error)) {
call := func(key, apiCode string, req interface{}) {
wg.Add(1)
go func() {
defer wg.Done()
@@ -48,7 +48,7 @@ func ProcessQYGLJ1U9Request(ctx context.Context, params []byte, deps *processors
resultsCh <- apiResult{key: key, err: err}
return
}
resp, err := fn(ctx, b, deps)
resp, err := processors.InvokeRegisteredProcessor(ctx, apiCode, b, deps)
if err != nil {
resultsCh <- apiResult{key: key, err: err}
return
@@ -70,46 +70,46 @@ func ProcessQYGLJ1U9Request(ctx context.Context, params []byte, deps *processors
}
// 企业全量信息核验V2QYGLUY3S
call("jiguangFull", map[string]interface{}{
call("jiguangFull", "QYGLUY3S", map[string]interface{}{
"ent_name": p.EntName,
"ent_code": p.EntCode,
}, ProcessQYGLUY3SRequest)
})
// 企业股权结构全景QYGLJ0Q1
call("equityPanorama", map[string]interface{}{
call("equityPanorama", "QYGLJ0Q1", map[string]interface{}{
"ent_name": p.EntName,
}, ProcessQYGLJ0Q1Request)
})
// 企业司法涉诉V2QYGL5S1I
call("judicialCertFull", map[string]interface{}{
call("judicialCertFull", "QYGL5S1I", map[string]interface{}{
"ent_name": p.EntName,
"ent_code": p.EntCode,
}, ProcessQYGL5S1IRequest)
})
// 企业年报信息核验QYGLDJ12
call("annualReport", map[string]interface{}{
call("annualReport", "QYGLDJ12", map[string]interface{}{
"ent_name": p.EntName,
"ent_code": p.EntCode,
}, ProcessQYGLDJ12Request)
})
// 企业税收违法核查QYGL8848
call("taxViolation", map[string]interface{}{
call("taxViolation", "QYGL8848", map[string]interface{}{
"ent_name": p.EntName,
"ent_code": p.EntCode,
}, ProcessQYGL8848Request)
})
// 欠税公告QYGL7D9A天眼查 OwnTaxkeyword 为统一社会信用代码)
call("taxArrears", map[string]interface{}{
call("taxArrears", "QYGL7D9A", map[string]interface{}{
"ent_code": p.EntCode,
"page_size": 20,
"page_num": 1,
}, ProcessQYGL7D9ARequest)
})
// 企业进出口信用核查QYGLDJ33
call("customsCredit", map[string]interface{}{
call("customsCredit", "QYGLDJ33", map[string]interface{}{
"ent_name": p.EntName,
"ent_code": p.EntCode,
}, ProcessQYGLDJ33Request)
})
wg.Wait()
close(resultsCh)

View File

@@ -0,0 +1,58 @@
package processors
import "context"
type whitelistContextKey struct{}
// WhitelistMatch 请求级白名单命中项(身份匹配,不含当前顶层 api_code 过滤)
type WhitelistMatch struct {
ID string
APICodes []string
IsGlobal bool
}
// WhitelistContext 写入 context 的白名单状态,供各处理器按 api_code 判断是否返回查询为空
type WhitelistContext struct {
Matches []WhitelistMatch
}
// WithWhitelistContext 将白名单命中结果写入 context
func WithWhitelistContext(ctx context.Context, matches []WhitelistMatch) context.Context {
if len(matches) == 0 {
return ctx
}
return context.WithValue(ctx, whitelistContextKey{}, &WhitelistContext{Matches: matches})
}
// WhitelistFromContext 从 context 读取白名单状态
func WhitelistFromContext(ctx context.Context) *WhitelistContext {
wc, ok := ctx.Value(whitelistContextKey{}).(*WhitelistContext)
if !ok || wc == nil {
return nil
}
return wc
}
// WhitelistShouldReturnEmpty 根据 context 中的白名单与当前 api_code 判断是否应返回查询为空。
// 入参是否命中、命中哪些 api_code 由应用层 EnrichContext 写入;此处仅读 ctx。
func WhitelistShouldReturnEmpty(ctx context.Context, apiCode string) bool {
wc := WhitelistFromContext(ctx)
if wc == nil {
return false
}
for _, m := range wc.Matches {
if matchesWhitelistAPICode(m.APICodes, apiCode) {
return true
}
}
return false
}
func matchesWhitelistAPICode(apiCodes []string, apiCode string) bool {
for _, code := range apiCodes {
if code == "*" || code == apiCode {
return true
}
}
return false
}

View File

@@ -0,0 +1,32 @@
package processors
import (
"context"
"testing"
)
func TestWhitelistShouldReturnEmpty_PerAPICode(t *testing.T) {
ctx := WithWhitelistContext(context.Background(), []WhitelistMatch{
{ID: "1", APICodes: []string{"FLXG0V4B"}},
})
if !WhitelistShouldReturnEmpty(ctx, "FLXG0V4B") {
t.Fatal("FLXG0V4B should hit")
}
if WhitelistShouldReturnEmpty(ctx, "JRZQ8A2D") {
t.Fatal("JRZQ8A2D should not hit entry scoped to FLXG0V4B")
}
}
func TestWhitelistShouldReturnEmpty_WildcardMatchesAnyAPICode(t *testing.T) {
ctx := WithWhitelistContext(context.Background(), []WhitelistMatch{
{ID: "1", APICodes: []string{"*"}},
})
if !WhitelistShouldReturnEmpty(ctx, "QYGL8261") {
t.Fatal("wildcard should match any api_code in ctx")
}
if !WhitelistShouldReturnEmpty(ctx, "YYSY8B1C") {
t.Fatal("wildcard should match mobile-only api_code when in ctx")
}
}

View File

@@ -9,6 +9,6 @@ import (
// ProcessYYSY8C2DRequest YYSY8C2D API处理方法 - 运营商三要素查询
func ProcessYYSY8C2DRequest(ctx context.Context, params []byte, deps *processors.ProcessorDependencies) ([]byte, error) {
return ProcessYYSY9A1BRequest(ctx, params, deps)
return processors.InvokeRegisteredProcessor(ctx, "YYSY9A1B", params, deps)
}