新增征信解析接口与错误模拟
This commit is contained in:
37
lsfx-mock-server/routers/credit_api.py
Normal file
37
lsfx-mock-server/routers/credit_api.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
from fastapi import APIRouter, File, Form, UploadFile
|
||||||
|
|
||||||
|
from services.credit_debug_service import CreditDebugService
|
||||||
|
from services.credit_payload_service import CreditPayloadService
|
||||||
|
|
||||||
|
router = APIRouter()
|
||||||
|
payload_service = CreditPayloadService("config/credit_feature_schema.json")
|
||||||
|
debug_service = CreditDebugService("config/credit_response_examples.json")
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/xfeature-mngs/conversation/htmlEval")
|
||||||
|
async def html_eval(
|
||||||
|
model: Optional[str] = Form(None),
|
||||||
|
hType: Optional[str] = Form(None),
|
||||||
|
file: Optional[UploadFile] = File(None),
|
||||||
|
):
|
||||||
|
error_response = debug_service.validate_request(
|
||||||
|
model=model,
|
||||||
|
h_type=hType,
|
||||||
|
file_present=file is not None,
|
||||||
|
)
|
||||||
|
if error_response:
|
||||||
|
return error_response
|
||||||
|
|
||||||
|
payload = payload_service.generate_payload(
|
||||||
|
model=model,
|
||||||
|
h_type=hType,
|
||||||
|
filename=file.filename or "credit.html",
|
||||||
|
)
|
||||||
|
return debug_service.build_success_response(payload)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/credit/health")
|
||||||
|
async def credit_health():
|
||||||
|
return {"status": "healthy", "service": "credit-mock"}
|
||||||
62
lsfx-mock-server/services/credit_debug_service.py
Normal file
62
lsfx-mock-server/services/credit_debug_service.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import copy
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
|
||||||
|
class CreditDebugService:
|
||||||
|
"""处理征信解析接口的调试标记、参数校验与响应封装。"""
|
||||||
|
|
||||||
|
VALID_MODEL = "LXCUSTALL"
|
||||||
|
VALID_HTYPES = {"PERSON", "ENTERPRISE"}
|
||||||
|
|
||||||
|
def __init__(self, template_path: str):
|
||||||
|
self.template_path = template_path
|
||||||
|
self.templates = self._load_templates()
|
||||||
|
|
||||||
|
def validate_request(self, model: Optional[str], h_type: Optional[str], file_present: bool):
|
||||||
|
if not model:
|
||||||
|
return self.build_missing_param_response("model")
|
||||||
|
if not file_present:
|
||||||
|
return self.build_missing_param_response("file")
|
||||||
|
if not h_type:
|
||||||
|
return self.build_missing_param_response("hType")
|
||||||
|
|
||||||
|
error_code = self.detect_error_marker(model)
|
||||||
|
if error_code:
|
||||||
|
return self.build_error_response(error_code)
|
||||||
|
|
||||||
|
if model != self.VALID_MODEL:
|
||||||
|
return self.build_error_response("ERR_10002")
|
||||||
|
if h_type not in self.VALID_HTYPES:
|
||||||
|
return self.build_error_response("ERR_10003")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def build_success_response(self, payload: dict) -> dict:
|
||||||
|
response = copy.deepcopy(self.templates["success"])
|
||||||
|
response["payload"] = payload
|
||||||
|
return response
|
||||||
|
|
||||||
|
def build_missing_param_response(self, param_name: str) -> dict:
|
||||||
|
response = self.build_error_response("ERR_99999")
|
||||||
|
response["message"] = response["message"].replace("XX", param_name)
|
||||||
|
return response
|
||||||
|
|
||||||
|
def build_error_response(self, error_code: str) -> dict:
|
||||||
|
return copy.deepcopy(self.templates["errors"][error_code])
|
||||||
|
|
||||||
|
def detect_error_marker(self, model: str) -> Optional[str]:
|
||||||
|
matched = re.search(r"error_(ERR_\d+)", model)
|
||||||
|
if not matched:
|
||||||
|
return None
|
||||||
|
error_code = matched.group(1)
|
||||||
|
if error_code in self.templates["errors"]:
|
||||||
|
return error_code
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _load_templates(self) -> dict:
|
||||||
|
template_file = Path(self.template_path)
|
||||||
|
if not template_file.is_absolute():
|
||||||
|
template_file = Path(__file__).resolve().parent.parent / template_file
|
||||||
|
return json.loads(template_file.read_text(encoding="utf-8"))
|
||||||
@@ -37,7 +37,21 @@ def reset_file_service_state():
|
|||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def client():
|
def client():
|
||||||
"""创建测试客户端"""
|
"""创建测试客户端"""
|
||||||
return TestClient(app)
|
original_routes = list(app.router.routes)
|
||||||
|
try:
|
||||||
|
from routers import credit_api
|
||||||
|
|
||||||
|
if not any(route.path == "/xfeature-mngs/conversation/htmlEval" for route in app.routes):
|
||||||
|
app.include_router(credit_api.router, tags=["征信解析接口"])
|
||||||
|
app.openapi_schema = None
|
||||||
|
except ModuleNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
yield TestClient(app)
|
||||||
|
finally:
|
||||||
|
app.router.routes[:] = original_routes
|
||||||
|
app.openapi_schema = None
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@@ -68,3 +82,9 @@ def sample_inner_flow_request():
|
|||||||
"dataEndDateId": 20240131,
|
"dataEndDateId": 20240131,
|
||||||
"uploadUserId": 902001,
|
"uploadUserId": 902001,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def sample_credit_html_file():
|
||||||
|
"""示例征信 HTML 文件。"""
|
||||||
|
return ("credit.html", b"<html></html>", "text/html")
|
||||||
|
|||||||
45
lsfx-mock-server/tests/test_credit_api.py
Normal file
45
lsfx-mock-server/tests/test_credit_api.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
def test_html_eval_should_return_credit_payload(client, sample_credit_html_file):
|
||||||
|
response = client.post(
|
||||||
|
"/xfeature-mngs/conversation/htmlEval",
|
||||||
|
data={"model": "LXCUSTALL", "hType": "PERSON"},
|
||||||
|
files={"file": sample_credit_html_file},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
data = response.json()
|
||||||
|
assert data["status_code"] == "0"
|
||||||
|
assert data["message"] == "成功"
|
||||||
|
assert "lx_header" in data["payload"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_html_eval_should_return_err_99999_for_missing_model(client, sample_credit_html_file):
|
||||||
|
response = client.post(
|
||||||
|
"/xfeature-mngs/conversation/htmlEval",
|
||||||
|
data={"hType": "PERSON"},
|
||||||
|
files={"file": sample_credit_html_file},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json()["status_code"] == "ERR_99999"
|
||||||
|
|
||||||
|
|
||||||
|
def test_html_eval_should_return_err_10003_for_invalid_h_type(client, sample_credit_html_file):
|
||||||
|
response = client.post(
|
||||||
|
"/xfeature-mngs/conversation/htmlEval",
|
||||||
|
data={"model": "LXCUSTALL", "hType": "JSON"},
|
||||||
|
files={"file": sample_credit_html_file},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json()["status_code"] == "ERR_10003"
|
||||||
|
|
||||||
|
|
||||||
|
def test_html_eval_should_support_debug_error_marker(client, sample_credit_html_file):
|
||||||
|
response = client.post(
|
||||||
|
"/xfeature-mngs/conversation/htmlEval",
|
||||||
|
data={"model": "error_ERR_10001", "hType": "PERSON"},
|
||||||
|
files={"file": sample_credit_html_file},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
assert response.json()["status_code"] == "ERR_10001"
|
||||||
Reference in New Issue
Block a user