新增征信解析字段配置与生成服务
This commit is contained in:
159
lsfx-mock-server/config/credit_feature_schema.json
Normal file
159
lsfx-mock-server/config/credit_feature_schema.json
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"domain": "lx_header",
|
||||||
|
"field": "query_cert_no",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_header",
|
||||||
|
"field": "query_cust_name",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_header",
|
||||||
|
"field": "report_time",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_debt",
|
||||||
|
"field": "uncle_bank_house_bal",
|
||||||
|
"type": "amount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_debt",
|
||||||
|
"field": "uncle_bank_house_lmt",
|
||||||
|
"type": "amount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_debt",
|
||||||
|
"field": "uncle_bank_house_state",
|
||||||
|
"type": "status",
|
||||||
|
"options": ["正常", "逾期", "不良"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_debt",
|
||||||
|
"field": "uncle_bank_car_bal",
|
||||||
|
"type": "amount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_debt",
|
||||||
|
"field": "uncle_bank_car_lmt",
|
||||||
|
"type": "amount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_debt",
|
||||||
|
"field": "uncle_bank_car_state",
|
||||||
|
"type": "status",
|
||||||
|
"options": ["正常", "逾期", "不良"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_debt",
|
||||||
|
"field": "uncle_bank_manage_bal",
|
||||||
|
"type": "amount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_debt",
|
||||||
|
"field": "uncle_bank_manage_lmt",
|
||||||
|
"type": "amount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_debt",
|
||||||
|
"field": "uncle_bank_manage_state",
|
||||||
|
"type": "status",
|
||||||
|
"options": ["正常", "逾期", "不良"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_debt",
|
||||||
|
"field": "uncle_bank_consume_bal",
|
||||||
|
"type": "amount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_debt",
|
||||||
|
"field": "uncle_bank_consume_lmt",
|
||||||
|
"type": "amount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_debt",
|
||||||
|
"field": "uncle_bank_consume_state",
|
||||||
|
"type": "status",
|
||||||
|
"options": ["正常", "逾期", "不良"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_debt",
|
||||||
|
"field": "uncle_bank_other_bal",
|
||||||
|
"type": "amount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_debt",
|
||||||
|
"field": "uncle_bank_other_lmt",
|
||||||
|
"type": "amount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_debt",
|
||||||
|
"field": "uncle_bank_other_state",
|
||||||
|
"type": "status",
|
||||||
|
"options": ["正常", "逾期", "不良"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_debt",
|
||||||
|
"field": "uncle_not_bank_bal",
|
||||||
|
"type": "amount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_debt",
|
||||||
|
"field": "uncle_not_bank_lmt",
|
||||||
|
"type": "amount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_debt",
|
||||||
|
"field": "uncle_not_bank_state",
|
||||||
|
"type": "status",
|
||||||
|
"options": ["正常", "逾期", "不良"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_debt",
|
||||||
|
"field": "uncle_credit_cart_bal",
|
||||||
|
"type": "amount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_debt",
|
||||||
|
"field": "uncle_credit_cart_lmt",
|
||||||
|
"type": "amount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_debt",
|
||||||
|
"field": "uncle_credit_cart_state",
|
||||||
|
"type": "status",
|
||||||
|
"options": ["正常", "逾期", "不良"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_publictype",
|
||||||
|
"field": "civil_cnt",
|
||||||
|
"type": "count"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_publictype",
|
||||||
|
"field": "enforce_cnt",
|
||||||
|
"type": "count"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_publictype",
|
||||||
|
"field": "adm_cnt",
|
||||||
|
"type": "count"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_publictype",
|
||||||
|
"field": "civil_lmt",
|
||||||
|
"type": "amount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_publictype",
|
||||||
|
"field": "enforce_lmt",
|
||||||
|
"type": "amount"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"domain": "lx_publictype",
|
||||||
|
"field": "adm_lmt",
|
||||||
|
"type": "amount"
|
||||||
|
}
|
||||||
|
]
|
||||||
44
lsfx-mock-server/config/credit_response_examples.json
Normal file
44
lsfx-mock-server/config/credit_response_examples.json
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"success": {
|
||||||
|
"message": "成功",
|
||||||
|
"payload": {},
|
||||||
|
"status_code": "0"
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"ERR_99999": {
|
||||||
|
"message": "关键参数缺失,参数名: XX",
|
||||||
|
"payload": null,
|
||||||
|
"status_code": "ERR_99999"
|
||||||
|
},
|
||||||
|
"ERR_10001": {
|
||||||
|
"message": "无效的证件号码",
|
||||||
|
"payload": null,
|
||||||
|
"status_code": "ERR_10001"
|
||||||
|
},
|
||||||
|
"ERR_10002": {
|
||||||
|
"message": "无效的主题域",
|
||||||
|
"payload": null,
|
||||||
|
"status_code": "ERR_10002"
|
||||||
|
},
|
||||||
|
"ERR_10003": {
|
||||||
|
"message": "报文类型无效,仅支持JSON/XML",
|
||||||
|
"payload": null,
|
||||||
|
"status_code": "ERR_10003"
|
||||||
|
},
|
||||||
|
"ERR_10004": {
|
||||||
|
"message": "无效机构号或行社号",
|
||||||
|
"payload": null,
|
||||||
|
"status_code": "ERR_10004"
|
||||||
|
},
|
||||||
|
"ERR_10005": {
|
||||||
|
"message": "无权限访问",
|
||||||
|
"payload": null,
|
||||||
|
"status_code": "ERR_10005"
|
||||||
|
},
|
||||||
|
"ERR_10006": {
|
||||||
|
"message": "尽调报告生成异常:异步事件发送失败",
|
||||||
|
"payload": null,
|
||||||
|
"status_code": "ERR_10006"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
86
lsfx-mock-server/services/credit_payload_service.py
Normal file
86
lsfx-mock-server/services/credit_payload_service.py
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
import json
|
||||||
|
import random
|
||||||
|
from datetime import date, timedelta
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, List
|
||||||
|
|
||||||
|
|
||||||
|
class CreditPayloadService:
|
||||||
|
"""根据征信字段 schema 生成稳定随机的 mock payload。"""
|
||||||
|
|
||||||
|
def __init__(self, schema_path: str):
|
||||||
|
self.schema_path = schema_path
|
||||||
|
self.schema = self._load_schema()
|
||||||
|
|
||||||
|
def generate_payload(self, model: str, h_type: str, filename: str) -> dict:
|
||||||
|
rng = random.Random(self._build_seed(model, h_type, filename))
|
||||||
|
payload = {
|
||||||
|
"lx_header": {},
|
||||||
|
"lx_debt": {},
|
||||||
|
"lx_publictype": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
for item in self.schema:
|
||||||
|
domain = item["domain"]
|
||||||
|
field = item["field"]
|
||||||
|
field_type = item["type"]
|
||||||
|
payload[domain][field] = self._generate_value(field, field_type, item, rng)
|
||||||
|
|
||||||
|
return payload
|
||||||
|
|
||||||
|
def _load_schema(self) -> List[dict]:
|
||||||
|
schema_file = Path(self.schema_path)
|
||||||
|
if not schema_file.is_absolute():
|
||||||
|
schema_file = Path(__file__).resolve().parent.parent / schema_file
|
||||||
|
return json.loads(schema_file.read_text(encoding="utf-8"))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _build_seed(model: str, h_type: str, filename: str) -> str:
|
||||||
|
return f"{model}|{h_type}|{filename}"
|
||||||
|
|
||||||
|
def _generate_value(
|
||||||
|
self,
|
||||||
|
field: str,
|
||||||
|
field_type: str,
|
||||||
|
item: dict,
|
||||||
|
rng: random.Random,
|
||||||
|
) -> str:
|
||||||
|
if field_type == "string":
|
||||||
|
return self._generate_string(field, rng)
|
||||||
|
if field_type == "amount":
|
||||||
|
return f"{rng.uniform(0, 500000):.2f}"
|
||||||
|
if field_type == "count":
|
||||||
|
return str(rng.randint(0, 20))
|
||||||
|
if field_type == "status":
|
||||||
|
return rng.choice(item["options"])
|
||||||
|
raise ValueError(f"Unsupported field type: {field_type}")
|
||||||
|
|
||||||
|
def _generate_string(self, field: str, rng: random.Random) -> str:
|
||||||
|
if field == "query_cert_no":
|
||||||
|
return self._generate_cert_no(rng)
|
||||||
|
if field == "query_cust_name":
|
||||||
|
return self._generate_name(rng)
|
||||||
|
if field == "report_time":
|
||||||
|
return self._generate_report_date(rng)
|
||||||
|
return f"mock_{rng.randint(1000, 9999)}"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _generate_cert_no(rng: random.Random) -> str:
|
||||||
|
area_code = "330781"
|
||||||
|
start_date = date(1980, 1, 1)
|
||||||
|
birthday = start_date + timedelta(days=rng.randint(0, 14000))
|
||||||
|
sequence = f"{rng.randint(100, 999)}"
|
||||||
|
check_code = rng.choice("0123456789X")
|
||||||
|
return f"{area_code}{birthday.strftime('%Y%m%d')}{sequence}{check_code}"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _generate_name(rng: random.Random) -> str:
|
||||||
|
surnames = ["张", "王", "李", "赵", "陈", "刘", "周", "吴"]
|
||||||
|
given_names = ["伟", "芳", "娜", "敏", "静", "磊", "洋", "婷", "超", "洁"]
|
||||||
|
return f"{rng.choice(surnames)}{rng.choice(given_names)}{rng.choice(given_names)}"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _generate_report_date(rng: random.Random) -> str:
|
||||||
|
base_date = date(2024, 1, 1)
|
||||||
|
report_date = base_date + timedelta(days=rng.randint(0, 365))
|
||||||
|
return report_date.strftime("%Y-%m-%d")
|
||||||
35
lsfx-mock-server/tests/test_credit_payload_service.py
Normal file
35
lsfx-mock-server/tests/test_credit_payload_service.py
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
from services.credit_payload_service import CreditPayloadService
|
||||||
|
|
||||||
|
|
||||||
|
def test_generate_payload_should_be_stable_for_same_input():
|
||||||
|
service = CreditPayloadService("config/credit_feature_schema.json")
|
||||||
|
|
||||||
|
payload1 = service.generate_payload(
|
||||||
|
model="LXCUSTALL",
|
||||||
|
h_type="PERSON",
|
||||||
|
filename="credit-report-a.html",
|
||||||
|
)
|
||||||
|
payload2 = service.generate_payload(
|
||||||
|
model="LXCUSTALL",
|
||||||
|
h_type="PERSON",
|
||||||
|
filename="credit-report-a.html",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert payload1 == payload2
|
||||||
|
assert set(payload1.keys()) == {"lx_header", "lx_debt", "lx_publictype"}
|
||||||
|
assert len(payload1["lx_debt"]) == 21
|
||||||
|
assert len(payload1["lx_publictype"]) == 6
|
||||||
|
|
||||||
|
|
||||||
|
def test_generate_payload_should_use_schema_type_rules():
|
||||||
|
service = CreditPayloadService("config/credit_feature_schema.json")
|
||||||
|
|
||||||
|
payload = service.generate_payload(
|
||||||
|
model="LXCUSTALL",
|
||||||
|
h_type="ENTERPRISE",
|
||||||
|
filename="credit-report-b.html",
|
||||||
|
)
|
||||||
|
|
||||||
|
assert payload["lx_debt"]["uncle_bank_house_state"] in {"正常", "逾期", "不良"}
|
||||||
|
assert payload["lx_header"]["report_time"].count("-") == 2
|
||||||
|
assert payload["lx_publictype"]["civil_cnt"].isdigit()
|
||||||
Reference in New Issue
Block a user