Files
ccdi/docs/plans/backend/2026-03-23-credit-parsing-mock-server-backend-implementation.md

448 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Credit Parsing Mock Server Backend Implementation Plan
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** 在现有 `lsfx-mock-server` 中新增征信解析 Mock 能力,提供 `POST /xfeature-mngs/conversation/htmlEval` 接口,支持稳定随机生成征信 `payload`、最小错误码模拟与健康检查能力。
**Architecture:** 复用现有 FastAPI 应用与测试基础设施,不新建独立服务。征信解析能力拆成 `schema 配置 + payload 生成 service + 调试 service + 独立 router` 四层,接口层只负责 `form-data` 接收与响应组装,字段生成和错误处理分别下沉到独立 service 中,避免与现有 LSFX Mock 逻辑混杂。
**Tech Stack:** Python 3, FastAPI, pytest, python-multipart, JSON, Bash
---
## File Structure
- `lsfx-mock-server/config/credit_feature_schema.json`: 固化 Excel 中 30 个征信指标字段的主题域、字段名、类型与枚举范围。
- `lsfx-mock-server/config/credit_response_examples.json`: 固化成功与失败响应模板,避免响应结构散落在路由中。
- `lsfx-mock-server/services/credit_payload_service.py`: 负责稳定随机种子生成、schema 加载和 `payload` 构造。
- `lsfx-mock-server/services/credit_debug_service.py`: 负责参数校验、调试标记识别与错误响应封装。
- `lsfx-mock-server/routers/credit_api.py`: 负责新增征信解析接口和征信健康检查接口。
- `lsfx-mock-server/main.py`: 注册征信解析 router并在应用描述中补充接口说明。
- `lsfx-mock-server/README.md`: 补充征信解析 Mock 的启动方式、接口路径和调试用法。
- `lsfx-mock-server/tests/test_credit_payload_service.py`: 锁定稳定随机与字段类型语义。
- `lsfx-mock-server/tests/test_credit_api.py`: 锁定征信解析接口成功、缺参、错误码与健康检查行为。
- `lsfx-mock-server/tests/test_startup.py`: 锁定新 router 已被主应用注册。
- `docs/reports/implementation/2026-03-23-credit-parsing-mock-server-backend-record.md`: 记录本次后端实施内容。
- `docs/tests/records/2026-03-23-credit-parsing-mock-server-backend-verification.md`: 记录本次后端验证命令与结果。
### Task 1: 建立征信字段 schema 和稳定随机 payload 生成能力
**Files:**
- Create: `lsfx-mock-server/config/credit_feature_schema.json`
- Create: `lsfx-mock-server/config/credit_response_examples.json`
- Create: `lsfx-mock-server/services/credit_payload_service.py`
- Create: `lsfx-mock-server/tests/test_credit_payload_service.py`
- Reference: `docs/design/2026-03-23-credit-parsing-mock-server-design.md`
- Reference: `assets/征信解析/征信解析接口payload.xlsx`
- [ ] **Step 1: Write the failing test**
`lsfx-mock-server/tests/test_credit_payload_service.py` 中先补两条失败用例锁定“同一输入返回稳定随机相同结果”和“schema 中的状态字段严格落在枚举范围内”:
```python
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()
```
- [ ] **Step 2: Run test to verify it fails**
Run:
```bash
cd lsfx-mock-server
python3 -m pytest tests/test_credit_payload_service.py -v
```
Expected:
- `FAIL`
- 原因是 `CreditPayloadService`、schema 配置文件和响应模板尚不存在
- [ ] **Step 3: Write minimal implementation**
按最小路径落地:
1.`lsfx-mock-server/config/credit_feature_schema.json` 中把 Excel 30 个字段完整整理为 JSON至少包含
```json
[
{ "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_state", "type": "status", "options": ["正常", "逾期", "不良"] }
]
```
2.`lsfx-mock-server/config/credit_response_examples.json` 中固化成功/失败模板:
```json
{
"success": { "message": "成功", "payload": {}, "status_code": "0" },
"errors": {
"ERR_99999": { "message": "关键参数缺失,参数名: XX", "payload": null, "status_code": "ERR_99999" }
}
}
```
3.`lsfx-mock-server/services/credit_payload_service.py` 中实现最小服务:
```python
class CreditPayloadService:
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))
...
```
4. 生成规则只保留设计文档确认的 4 类:
- `string`
- `amount`
- `count`
- `status`
- [ ] **Step 4: Run test to verify it passes**
Run:
```bash
cd lsfx-mock-server
python3 -m pytest tests/test_credit_payload_service.py -v
```
Expected:
- `PASS`
- 同一输入生成稳定一致结果,不同字段类型输出格式符合约定
- [ ] **Step 5: Commit**
```bash
git add lsfx-mock-server/config/credit_feature_schema.json lsfx-mock-server/config/credit_response_examples.json lsfx-mock-server/services/credit_payload_service.py lsfx-mock-server/tests/test_credit_payload_service.py
git commit -m "新增征信解析字段配置与生成服务"
```
### Task 2: 实现参数校验、错误码模拟和征信解析接口
**Files:**
- Create: `lsfx-mock-server/services/credit_debug_service.py`
- Create: `lsfx-mock-server/routers/credit_api.py`
- Create: `lsfx-mock-server/tests/test_credit_api.py`
- Modify: `lsfx-mock-server/tests/conftest.py`
- Reference: `docs/design/2026-03-23-credit-parsing-mock-server-design.md`
- [ ] **Step 1: Write the failing test**
`lsfx-mock-server/tests/test_credit_api.py` 中先补 4 类失败/成功用例,确保接口不走 FastAPI 默认 422而是走说明书约定的业务响应
```python
def test_html_eval_should_return_credit_payload(client):
response = client.post(
"/xfeature-mngs/conversation/htmlEval",
data={"model": "LXCUSTALL", "hType": "PERSON"},
files={"file": ("credit.html", b"<html></html>", "text/html")},
)
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):
response = client.post(
"/xfeature-mngs/conversation/htmlEval",
data={"hType": "PERSON"},
files={"file": ("credit.html", b"<html></html>", "text/html")},
)
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):
response = client.post(
"/xfeature-mngs/conversation/htmlEval",
data={"model": "LXCUSTALL", "hType": "JSON"},
files={"file": ("credit.html", b"<html></html>", "text/html")},
)
assert response.status_code == 200
assert response.json()["status_code"] == "ERR_10003"
def test_html_eval_should_support_debug_error_marker(client):
response = client.post(
"/xfeature-mngs/conversation/htmlEval",
data={"model": "error_ERR_10001", "hType": "PERSON"},
files={"file": ("credit.html", b"<html></html>", "text/html")},
)
assert response.status_code == 200
assert response.json()["status_code"] == "ERR_10001"
```
- [ ] **Step 2: Run test to verify it fails**
Run:
```bash
cd lsfx-mock-server
python3 -m pytest tests/test_credit_api.py -v
```
Expected:
- `FAIL`
- 原因是征信解析 router 和调试 service 尚不存在
- [ ] **Step 3: Write minimal implementation**
按设计文档实现最小闭环:
1.`lsfx-mock-server/services/credit_debug_service.py` 中定义错误码映射和业务校验入口:
```python
class CreditDebugService:
ERROR_MESSAGES = {
"ERR_99999": "关键参数缺失,参数名: XX",
"ERR_10001": "无效的证件号码",
"ERR_10002": "无效的主题域",
"ERR_10003": "报文类型无效仅支持JSON/XML",
"ERR_10004": "无效机构号或行社号",
"ERR_10005": "无权限访问",
"ERR_10006": "尽调报告生成异常:异步事件发送失败",
}
```
2.`lsfx-mock-server/routers/credit_api.py` 中让参数全部使用可空 `Form(None)` / `File(None)`,再手工校验,避免缺参时被 FastAPI 直接拦成 422
```python
@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),
):
...
```
3. 正常流程调用 `CreditPayloadService.generate_payload()`
4. 失败流程统一返回 `message + payload(null) + status_code`
5.`tests/conftest.py` 中按需补共享 fixture例如测试上传的默认 HTML 文件内容。
- [ ] **Step 4: Run test to verify it passes**
Run:
```bash
cd lsfx-mock-server
python3 -m pytest tests/test_credit_api.py -v
```
Expected:
- `PASS`
- 接口成功、缺参、非法 `hType`、调试错误码都返回预期业务结构
- [ ] **Step 5: Commit**
```bash
git add lsfx-mock-server/services/credit_debug_service.py lsfx-mock-server/routers/credit_api.py lsfx-mock-server/tests/test_credit_api.py lsfx-mock-server/tests/conftest.py
git commit -m "新增征信解析接口与错误模拟"
```
### Task 3: 注册新 router 并补启动文档
**Files:**
- Modify: `lsfx-mock-server/main.py`
- Modify: `lsfx-mock-server/README.md`
- Modify: `lsfx-mock-server/tests/test_startup.py`
- Reference: `lsfx-mock-server/dev.py`
- [ ] **Step 1: Write the failing test**
先在 `lsfx-mock-server/tests/test_startup.py` 中补应用注册断言,锁定主应用已经包含征信解析接口和健康检查接口:
```python
from main import app
def test_app_should_register_credit_mock_routes():
paths = {route.path for route in app.routes}
assert "/xfeature-mngs/conversation/htmlEval" in paths
assert "/credit/health" in paths
```
- [ ] **Step 2: Run test to verify it fails**
Run:
```bash
cd lsfx-mock-server
python3 -m pytest tests/test_startup.py -v
```
Expected:
- `FAIL`
- 原因是 `main.py` 还未注册征信解析 router
- [ ] **Step 3: Write minimal implementation**
1.`lsfx-mock-server/main.py` 中引入并注册 `credit_api.router`
```python
from routers import api, credit_api
app.include_router(api.router, tags=["流水分析接口"])
app.include_router(credit_api.router, tags=["征信解析接口"])
```
2. 在应用描述中补充征信解析能力说明。
3.`lsfx-mock-server/README.md` 中补充:
- 新接口路径
- `curl``requests` 调用示例
- `error_ERR_10001` 调试方式
- `GET /credit/health` 用法
- [ ] **Step 4: Run test to verify it passes**
Run:
```bash
cd lsfx-mock-server
python3 -m pytest tests/test_startup.py tests/test_credit_api.py -v
```
Expected:
- `PASS`
- 主应用已注册征信解析 routerREADME 与运行方式保持一致
- [ ] **Step 5: Commit**
```bash
git add lsfx-mock-server/main.py lsfx-mock-server/README.md lsfx-mock-server/tests/test_startup.py
git commit -m "注册征信解析Mock路由并补充说明"
```
### Task 4: 端到端验证并沉淀实施记录
**Files:**
- Create: `docs/reports/implementation/2026-03-23-credit-parsing-mock-server-backend-record.md`
- Create: `docs/tests/records/2026-03-23-credit-parsing-mock-server-backend-verification.md`
- Reference: `docs/design/2026-03-23-credit-parsing-mock-server-design.md`
- Reference: `docs/plans/backend/2026-03-23-credit-parsing-mock-server-backend-implementation.md`
- [ ] **Step 1: Run targeted automated tests**
Run:
```bash
cd lsfx-mock-server
python3 -m pytest tests/test_credit_payload_service.py tests/test_credit_api.py tests/test_startup.py -v
```
Expected:
- `PASS`
- 征信字段生成、接口返回和路由注册全部通过
- [ ] **Step 2: Run manual startup and smoke verification**
Run:
```bash
cd lsfx-mock-server
python3 main.py > /tmp/credit-mock-server.log 2>&1 &
echo $! > /tmp/credit-mock-server.pid
```
再执行:
```bash
curl -s http://127.0.0.1:8000/credit/health
curl -s -X POST http://127.0.0.1:8000/xfeature-mngs/conversation/htmlEval \
-F model=LXCUSTALL \
-F hType=PERSON \
-F file=@/tmp/sample-credit.html
```
Expected:
- 健康检查返回 `healthy`
- 成功接口返回 `status_code: "0"`,且 `payload` 包含三大主题域
- [ ] **Step 3: Stop the started process**
Run:
```bash
kill "$(cat /tmp/credit-mock-server.pid)"
rm -f /tmp/credit-mock-server.pid
```
Expected:
- 本次验证启动的 Mock 进程已停止,不留下端口占用
- [ ] **Step 4: Write implementation and verification records**
`docs/reports/implementation/2026-03-23-credit-parsing-mock-server-backend-record.md` 中记录:
- 新增接口、配置文件、service 和测试文件
- 稳定随机策略
- 说明书歧义处理方式
`docs/tests/records/2026-03-23-credit-parsing-mock-server-backend-verification.md` 中记录:
- 自动化测试命令与结果
- 手工 `curl` 验证命令与结果
- 启停服务过程
- [ ] **Step 5: Commit**
```bash
git add docs/reports/implementation/2026-03-23-credit-parsing-mock-server-backend-record.md docs/tests/records/2026-03-23-credit-parsing-mock-server-backend-verification.md
git commit -m "补充征信解析Mock后端实施与验证记录"
```