f
This commit is contained in:
@@ -42,5 +42,9 @@ func ProcessJRZQ0OO1Request(ctx context.Context, params []byte, deps *processors
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
|
||||
return respBytes, nil
|
||||
transformed, err := transformJRZQ0OO1Response(respBytes)
|
||||
if err != nil {
|
||||
return nil, errors.Join(processors.ErrSystem, err)
|
||||
}
|
||||
return transformed, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
package jrzq
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// JRZQ0OO1 字段命名约定:{窗口}_{机构类型}_{事件}_{指标}
|
||||
// 窗口: m1/m3/m6/m12
|
||||
// 机构类型: 省略=全部贷款;bank=银行;cfc=消金;sloan=小贷
|
||||
// 事件: total/severe_overdue/overdue/repay_fail/repay_ok/repay_remind/
|
||||
//
|
||||
// disburse_fail/disburse_ok/audit_fail/audit_ok/apply/register/verify_code/marketing/other
|
||||
//
|
||||
// 指标: cnt=次数;org=机构数
|
||||
var (
|
||||
jrzq0OO1Periods = []string{"m1", "m3", "m6", "m12"}
|
||||
jrzq0OO1InstTypes = []string{"", "bank", "cfc", "sloan"}
|
||||
jrzq0OO1Events = []string{
|
||||
"total", "severe_overdue", "overdue", "repay_fail", "repay_ok",
|
||||
"repay_remind", "disburse_fail", "disburse_ok", "audit_fail", "audit_ok",
|
||||
"apply", "register", "verify_code", "marketing", "other",
|
||||
}
|
||||
jrzq0OO1Metrics = []string{"cnt", "org"}
|
||||
|
||||
// jrzq0OO1FieldMap B001-B480 -> 语义化扁平字段名
|
||||
jrzq0OO1FieldMap = buildJRZQ0OO1FieldMap()
|
||||
)
|
||||
|
||||
func buildJRZQ0OO1FieldMap() map[string]string {
|
||||
m := make(map[string]string, 480)
|
||||
n := 1
|
||||
for _, period := range jrzq0OO1Periods {
|
||||
for _, inst := range jrzq0OO1InstTypes {
|
||||
for _, event := range jrzq0OO1Events {
|
||||
for _, metric := range jrzq0OO1Metrics {
|
||||
key := fmt.Sprintf("B%03d", n)
|
||||
parts := []string{period}
|
||||
if inst != "" {
|
||||
parts = append(parts, inst)
|
||||
}
|
||||
parts = append(parts, event, metric)
|
||||
m[key] = strings.Join(parts, "_")
|
||||
n++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// transformJRZQ0OO1Response 将 Info360 的 Bxxx 字段重命名为语义化扁平字段。
|
||||
// 未在映射表中的键原样保留。
|
||||
func transformJRZQ0OO1Response(respBytes []byte) ([]byte, error) {
|
||||
var raw map[string]interface{}
|
||||
if err := json.Unmarshal(respBytes, &raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
out := make(map[string]interface{}, len(raw))
|
||||
for k, v := range raw {
|
||||
if renamed, ok := jrzq0OO1FieldMap[k]; ok {
|
||||
out[renamed] = v
|
||||
continue
|
||||
}
|
||||
out[k] = v
|
||||
}
|
||||
return json.Marshal(out)
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package jrzq
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBuildJRZQ0OO1FieldMap_CountAndBounds(t *testing.T) {
|
||||
if len(jrzq0OO1FieldMap) != 480 {
|
||||
t.Fatalf("expected 480 mappings, got %d", len(jrzq0OO1FieldMap))
|
||||
}
|
||||
if got, want := jrzq0OO1FieldMap["B001"], "m1_total_cnt"; got != want {
|
||||
t.Fatalf("B001: got %q want %q", got, want)
|
||||
}
|
||||
if got, want := jrzq0OO1FieldMap["B480"], "m12_sloan_other_org"; got != want {
|
||||
t.Fatalf("B480: got %q want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestJRZQ0OO1FieldMap_DocSpotChecks(t *testing.T) {
|
||||
cases := map[string]string{
|
||||
"B001": "m1_total_cnt", // 最近1个月贷款类总次数
|
||||
"B009": "m1_repay_ok_cnt", // 最近1个月还款成功次数
|
||||
"B031": "m1_bank_total_cnt", // 最近1个月银行类贷款总次数
|
||||
"B129": "m3_repay_ok_cnt", // 最近3个月还款成功次数
|
||||
"B130": "m3_repay_ok_org", // 最近3个月还款成功机构数
|
||||
"B133": "m3_disburse_fail_cnt", // 最近3个月放款失败次数
|
||||
"B134": "m3_disburse_fail_org", // 最近3个月放款失败机构数
|
||||
"B157": "m3_bank_repay_fail_cnt", // 最近3个月银行类还款失败次数
|
||||
"B158": "m3_bank_repay_fail_org", // 最近3个月银行类还款失败机构数
|
||||
"B219": "m3_sloan_repay_ok_cnt", // 最近3个月小贷类还款成功次数
|
||||
"B220": "m3_sloan_repay_ok_org", // 最近3个月小贷类还款成功机构数
|
||||
"B223": "m3_sloan_disburse_fail_cnt", // 最近3个月小贷类放款失败次数
|
||||
"B224": "m3_sloan_disburse_fail_org", // 最近3个月小贷类放款失败机构数
|
||||
"B247": "m6_repay_fail_cnt", // 最近6个月还款失败次数
|
||||
"B248": "m6_repay_fail_org", // 最近6个月还款失败机构数
|
||||
"B257": "m6_audit_fail_cnt", // 最近6个月审核失败次数
|
||||
"B258": "m6_audit_fail_org", // 最近6个月审核失败机构数
|
||||
"B261": "m6_apply_cnt", // 最近6个月贷款申请次数
|
||||
"B262": "m6_apply_org", // 最近6个月贷款申请机构数
|
||||
"B285": "m6_bank_disburse_ok_cnt", // 最近6个月银行类放款成功次数
|
||||
"B286": "m6_bank_disburse_ok_org", // 最近6个月银行类放款成功机构数
|
||||
"B347": "m6_sloan_audit_fail_cnt", // 最近6个月小贷类审核失败次数
|
||||
"B348": "m6_sloan_audit_fail_org", // 最近6个月小贷类审核失败机构数
|
||||
"B351": "m6_sloan_apply_cnt", // 最近6个月小贷类贷款申请次数
|
||||
"B352": "m6_sloan_apply_org", // 最近6个月小贷类贷款申请机构数
|
||||
"B375": "m12_disburse_ok_cnt", // 最近12个月放款成功次数
|
||||
"B376": "m12_disburse_ok_org", // 最近12个月放款成功机构数
|
||||
"B061": "m1_cfc_total_cnt", // 最近1个月持牌消金类贷款总次数
|
||||
"B301": "m6_cfc_total_cnt", // 最近6个月消金类贷款总次数
|
||||
}
|
||||
for src, want := range cases {
|
||||
if got := jrzq0OO1FieldMap[src]; got != want {
|
||||
t.Errorf("%s: got %q want %q", src, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTransformJRZQ0OO1Response_Sample(t *testing.T) {
|
||||
in := []byte(`{
|
||||
"B129": 6,
|
||||
"B130": 3,
|
||||
"B133": 4,
|
||||
"B134": 2,
|
||||
"B157": 2,
|
||||
"B158": 1,
|
||||
"B219": 6,
|
||||
"B220": 3,
|
||||
"B223": 4,
|
||||
"B224": 2,
|
||||
"B247": 2,
|
||||
"B248": 1,
|
||||
"B257": 6,
|
||||
"B258": 3,
|
||||
"B261": 4,
|
||||
"B262": 2,
|
||||
"B285": 2,
|
||||
"B286": 1,
|
||||
"B347": 6,
|
||||
"B348": 3,
|
||||
"B351": 4,
|
||||
"B352": 2,
|
||||
"B375": 2,
|
||||
"B376": 1
|
||||
}`)
|
||||
|
||||
outBytes, err := transformJRZQ0OO1Response(in)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var out map[string]interface{}
|
||||
if err := json.Unmarshal(outBytes, &out); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
want := map[string]float64{
|
||||
"m3_repay_ok_cnt": 6,
|
||||
"m3_repay_ok_org": 3,
|
||||
"m3_disburse_fail_cnt": 4,
|
||||
"m3_disburse_fail_org": 2,
|
||||
"m3_bank_repay_fail_cnt": 2,
|
||||
"m3_bank_repay_fail_org": 1,
|
||||
"m3_sloan_repay_ok_cnt": 6,
|
||||
"m3_sloan_repay_ok_org": 3,
|
||||
"m3_sloan_disburse_fail_cnt": 4,
|
||||
"m3_sloan_disburse_fail_org": 2,
|
||||
"m6_repay_fail_cnt": 2,
|
||||
"m6_repay_fail_org": 1,
|
||||
"m6_audit_fail_cnt": 6,
|
||||
"m6_audit_fail_org": 3,
|
||||
"m6_apply_cnt": 4,
|
||||
"m6_apply_org": 2,
|
||||
"m6_bank_disburse_ok_cnt": 2,
|
||||
"m6_bank_disburse_ok_org": 1,
|
||||
"m6_sloan_audit_fail_cnt": 6,
|
||||
"m6_sloan_audit_fail_org": 3,
|
||||
"m6_sloan_apply_cnt": 4,
|
||||
"m6_sloan_apply_org": 2,
|
||||
"m12_disburse_ok_cnt": 2,
|
||||
"m12_disburse_ok_org": 1,
|
||||
}
|
||||
|
||||
if len(out) != len(want) {
|
||||
t.Fatalf("field count: got %d want %d, out=%v", len(out), len(want), out)
|
||||
}
|
||||
for k, w := range want {
|
||||
got, ok := out[k]
|
||||
if !ok {
|
||||
t.Errorf("missing key %s", k)
|
||||
continue
|
||||
}
|
||||
num, ok := got.(float64)
|
||||
if !ok || num != w {
|
||||
t.Errorf("%s: got %#v want %v", k, got, w)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTransformJRZQ0OO1Response_KeepUnknown(t *testing.T) {
|
||||
in := []byte(`{"B001":1,"extra":"x"}`)
|
||||
outBytes, err := transformJRZQ0OO1Response(in)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var out map[string]interface{}
|
||||
if err := json.Unmarshal(outBytes, &out); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, ok := out["m1_total_cnt"]; !ok {
|
||||
t.Fatal("expected m1_total_cnt")
|
||||
}
|
||||
if out["extra"] != "x" {
|
||||
t.Fatalf("expected extra kept, got %#v", out["extra"])
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user