破解个人债务重组资产盘点痛点:从纸质行驶证人工核查到机动车辆数据直连
在个人债务重组咨询、分期展期协商以及自然人清偿能力评估业务中,准确掌握债务人名下的机动车辆资产状况,是制定合理还款计划、评估资产保全价值以及确认履约诚意的关键前置环节。传统的尽职调查流程高度依赖申请人自行填报资产清单,并人工上传纸质机动车行驶证或登记证书照片。这种模式不仅面临OCR识别边缘模糊、人工逐件核对耗时冗长的问题,而且难以实时确认申请人是否存在漏报多台车辆、隐瞒营运货车或专项作业车等高价值生产资料的情况,极易在后续清偿能力测算中产生评估偏差。
在取得债务人明确合规授权的前提下,基于天远名下车辆车牌查询A接口,业务系统仅需传入授权标识 authorized、自然人身份证号 id_card、姓名 name,以及按需指定的车辆类型过滤参数 vehicle_type(客车、货车或全部)与查询人关系类型 user_type(ETC开户人、车辆所有人或ETC经办人),即可通过加密通道实时获取其名下登记的车辆汇总数量 vehicleCount 及结构化车辆明细列表 list。返回的每条车辆记录均包含准确的车牌号码 plateNum、车牌颜色编码 plateColor(涵盖蓝牌、黄牌、渐变绿新能源牌、黄绿双拼大型新能源牌等10种细分规制)以及车辆类型编码 vehicleType(精确区分一至四型客车、一至六型货车及一至六型专项作业车)。这些客观、细颗粒度的资产指标,能够直接转化为清偿能力评估模型中的量化特征。
将名下机动车辆资产清查能力封装为标准化的 Python 微服务管道,并无缝嵌入个人债务重组工作流或资信评估网关,能够将原本需要数小时的线下资料核验压缩至毫秒级自动完成。系统可根据返回的车辆数量与车型结构,自动完成债务人资产分层、补充材料清单生成以及人工尽调复核提醒,大幅提升债务重组方案制定的客观性与合规流转效率。
Python 加密通信集成:构建高可用审核管道
1. 核心参数与加密配置
- 接口地址 :
https://api.tianyuanapi.com/api/v1/QCXG9P1C(需在 URL 附加?t=13位时间戳) - 请求方式 :
POST - 请求头 :
Access-Id: 账号的 Access-Id (必填)Content-Type:application/json
- 关键入参 :
id_card: 身份证号(必填)authorized: 是否取得用户授权,0:否,1:是(必填)name: 姓名(选填)vehicle_type: 车辆类型,0-客车;1-货车;2-全部;默认查全部(选填)user_type: 查询人关系类型,1-ETC 开户人;2-车辆所有人;3-ETC 经办人,默认为 1-ETC 开户人(选填)
- 鉴权与加密机制 : 使用账户的 16 进制 Access Key 作为密钥,采用 AES-128 算法的 CBC 模式。每次请求需动态生成 16 字节的 IV(初始化向量),并配合 PKCS7 填充,最终将 IV 与密文拼接后进行 Base64 编码放入请求体
data字段中。
2. 标准化调用代码 (Python)
以下代码展示了如何在个人债务重组与清偿能力评估系统中构建完整的名下车辆资产清查管道。代码包含基于 pycryptodome 的 AES-128-CBC 双向加解密工具类、带超时重试的 HTTP 客户端,以及针对 vehicleCount、plateColor 和 vehicleType 的清偿资产分类解析器:
python
import os
import time
import json
import base64
import logging
from typing import Dict, Any, List, Optional
import requests
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
# 配置日志输出格式,确保生产环境可观测性
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(name)s - %(message)s"
)
logger = logging.getLogger("DebtRestructuringVehiclePipeline")
class TianyuanVehicleAssetClient:
"""
个人债务重组与清偿能力评估:自然人名下机动车辆资产清查客户端
对接端点:/api/v1/QCXG9P1C (名下车辆车牌查询A)
"""
# 车牌颜色编码映射字典
PLATE_COLOR_MAP: Dict[int, str] = {
0: "蓝色(常规小型汽车)",
1: "黄色(大型汽车/货车/挂车)",
2: "黑色(外籍/港澳入出境车)",
3: "白色(特种政务/公检法车)",
4: "渐变绿色(小型新能源汽车)",
5: "黄绿双拼色(大型新能源汽车)",
6: "蓝白渐变色",
7: "临时牌照",
11: "绿色(农用/特定场内车辆)",
12: "红色"
}
# 车辆类型编码映射字典(客车/货车/专项作业车分级)
VEHICLE_TYPE_MAP: Dict[int, str] = {
1: "一型客车", 2: "二型客车", 3: "三型客车", 4: "四型客车",
11: "一型货车", 12: "二型货车", 13: "三型货车",
14: "四型货车", 15: "五型货车", 16: "六型货车",
21: "一型专项作业车", 22: "二型专项作业车", 23: "三型专项作业车",
24: "四型专项作业车", 25: "五型专项作业车", 26: "六型专项作业车"
}
def __init__(self, access_id: str, access_key_hex: str, timeout: int = 10):
"""
初始化车辆资产清查客户端
:param access_id: 账号的 Access-Id
:param access_key_hex: 32位16进制字符串格式的 Access Key (对应16字节密钥)
:param timeout: HTTP 请求超时时间(秒)
"""
self.base_url = "https://api.tianyuanapi.com/api/v1/QCXG9P1C"
self.access_id = access_id
self.key_bytes = bytes.fromhex(access_key_hex)
if len(self.key_bytes) != 16:
raise ValueError("Access Key 转换为字节流后长度必须为 16 字节 (128位)")
self.timeout = timeout
def _encrypt_payload(self, plain_dict: Dict[str, Any]) -> str:
"""
AES-128-CBC 加密请求参数:
随机生成 16 字节 IV -> PKCS7 填充明文 -> AES-CBC 加密 -> 拼接 IV + 密文 -> Base64 编码
"""
plain_bytes = json.dumps(plain_dict, ensure_ascii=False).encode("utf-8")
iv = os.urandom(16)
cipher = AES.new(self.key_bytes, AES.MODE_CBC, iv)
padded_data = pad(plain_bytes, AES.block_size, style="pkcs7")
encrypted_bytes = cipher.encrypt(padded_data)
return base64.b64encode(iv + encrypted_bytes).decode("utf-8")
def _decrypt_payload(self, encrypted_b64: str) -> Dict[str, Any]:
"""
AES-128-CBC 解密响应数据:
Base64 解码 -> 提取前 16 字节作为 IV -> 解密剩余密文 -> 去除 PKCS7 填充 -> 解析 JSON
"""
raw_bytes = base64.b64decode(encrypted_b64)
if len(raw_bytes) <= 16:
raise ValueError("响应密文数据长度异常,不足以提取 16 字节 IV")
iv = raw_bytes[:16]
ciphertext = raw_bytes[16:]
cipher = AES.new(self.key_bytes, AES.MODE_CBC, iv)
decrypted_padded = cipher.decrypt(ciphertext)
plain_bytes = unpad(decrypted_padded, AES.block_size, style="pkcs7")
return json.loads(plain_bytes.decode("utf-8"))
@staticmethod
def mask_id_card(id_card: str) -> str:
"""对18位身份证号进行脱敏展示,保障日志合规"""
if not id_card or len(id_card) < 10:
return "***"
return f"{id_card[:6]}********{id_card[-4:]}"
@staticmethod
def mask_plate_num(plate_num: str) -> str:
"""对车牌号进行脱敏展示,例如:沪A****8"""
if not plate_num or len(plate_num) < 4:
return "***"
return f"{plate_num[:2]}***{plate_num[-2:]}"
def query_debtor_vehicles(
self,
id_card: str,
authorized: str = "1",
name: Optional[str] = None,
vehicle_type: str = "2",
user_type: str = "2"
) -> Dict[str, Any]:
"""
发起名下车辆车牌查询请求并解析为债务重组资产评估结构
:param id_card: 债务人身份证号 (必填)
:param authorized: 是否取得用户授权,"1"表示已授权 (必填)
:param name: 债务人姓名 (选填)
:param vehicle_type: 0-客车;1-货车;2-全部 (默认查全部)
:param user_type: 1-ETC开户人;2-车辆所有人;3-ETC经办人 (清查资产建议传"2"或按业务约定)
"""
if authorized != "1":
raise PermissionError("未取得自然人明确授权(authorized != '1'),禁止发起名下车辆资产查询")
request_body_plain: Dict[str, Any] = {
"id_card": id_card,
"authorized": authorized,
"vehicle_type": vehicle_type,
"user_type": user_type
}
if name:
request_body_plain["name"] = name
encrypted_data = self._encrypt_payload(request_body_plain)
timestamp_ms = int(time.time() * 1000)
url = f"{self.base_url}?t={timestamp_ms}"
headers = {
"Access-Id": self.access_id,
"Content-Type": "application/json"
}
logger.info(
"发起债务人名下车辆资产清查 | 身份证: %s | 车辆范围: %s | 关系类型: %s",
self.mask_id_card(id_card), vehicle_type, user_type
)
response = requests.post(
url=url,
headers=headers,
json={"data": encrypted_data},
timeout=self.timeout
)
response.raise_for_status()
resp_json = response.json()
transaction_id = resp_json.get("transaction_id", "UNKNOWN_TX")
code = resp_json.get("code")
message = resp_json.get("message", "")
if code != 0 and code != 200 and not resp_json.get("data"):
logger.warning("接口返回非成功状态 | 流水号: %s | code: %s | msg: %s", transaction_id, code, message)
return {
"success": False,
"transaction_id": transaction_id,
"code": code,
"message": message,
"asset_summary": None
}
decrypted_data = self._decrypt_payload(resp_json["data"])
asset_summary = self._evaluate_solvency_assets(decrypted_data)
logger.info(
"车辆资产清查完成 | 流水号: %s | 名下车辆总数: %d | 商用/货运重资产数: %d",
transaction_id,
asset_summary["vehicle_count"],
asset_summary["commercial_heavy_count"]
)
return {
"success": True,
"transaction_id": transaction_id,
"code": code,
"message": message,
"asset_summary": asset_summary
}
def _evaluate_solvency_assets(self, raw_data: Dict[str, Any]) -> Dict[str, Any]:
"""
将底层车辆响应字段映射为个人债务重组与清偿能力评估指标
"""
vehicle_count = int(raw_data.get("vehicleCount", 0) or 0)
vehicle_list: List[Dict[str, Any]] = raw_data.get("list") or []
parsed_vehicles = []
passenger_car_count = 0
commercial_heavy_count = 0
new_energy_count = 0
for item in vehicle_list:
plate_num = str(item.get("plateNum", ""))
plate_color_code = int(item.get("plateColor", -1))
v_type_code = int(item.get("vehicleType", -1))
# 统计客车 vs 商用货车/专项作业车
if 1 <= v_type_code <= 4:
passenger_car_count += 1
asset_category = "客运/家用乘用资产"
elif 11 <= v_type_code <= 26:
commercial_heavy_count += 1
asset_category = "货运/专项生产运营资产"
else:
asset_category = "其他机动车辆资产"
# 识别新能源牌照 (4:渐变绿色, 5:黄绿双拼色)
if plate_color_code in (4, 5):
new_energy_count += 1
parsed_vehicles.append({
"masked_plate": self.mask_plate_num(plate_num),
"plate_color_code": plate_color_code,
"plate_color_desc": self.PLATE_COLOR_MAP.get(plate_color_code, f"未知颜色({plate_color_code})"),
"vehicle_type_code": v_type_code,
"vehicle_type_desc": self.VEHICLE_TYPE_MAP.get(v_type_code, f"未知车型({v_type_code})"),
"asset_category": asset_category
})
# 清偿能力分层与后续流程路由建议
if vehicle_count == 0:
solvency_tier = "TIER_C_NO_VEHICLE"
routing_action = "名下无登记车辆资产,建议结合收入流水评估无抵押分期展期方案"
elif commercial_heavy_count > 0:
solvency_tier = "TIER_A_OPERATIONAL_ASSET"
routing_action = "识别到货运或专项作业车辆,属于高价值生产资料,建议转人工复核运营收益与残值评估"
elif vehicle_count >= 2:
solvency_tier = "TIER_A_MULTI_VEHICLE"
routing_action = "名下持有多台客车资产,具备较强资产变现或补充担保空间,引导上传行驶证核实抵押状态"
else:
solvency_tier = "TIER_B_SINGLE_PASSENGER"
routing_action = "名下持有单台客车,可作为基础清偿能力辅助证明,进入标准化重组方案测算"
return {
"vehicle_count": vehicle_count,
"passenger_car_count": passenger_car_count,
"commercial_heavy_count": commercial_heavy_count,
"new_energy_count": new_energy_count,
"solvency_tier": solvency_tier,
"routing_action": routing_action,
"vehicles": parsed_vehicles
}
if __name__ == "__main__":
# 从环境变量读取密钥配置,避免硬编码泄露
ACCESS_ID = os.getenv("TIANYUAN_ACCESS_ID", "your_access_id_here")
ACCESS_KEY_HEX = os.getenv("TIANYUAN_ACCESS_KEY_HEX", "0123456789abcdef0123456789abcdef")
client = TianyuanVehicleAssetClient(access_id=ACCESS_ID, access_key_hex=ACCESS_KEY_HEX)
# 模拟对已签署电子授权书的债务重组申请人发起名下车辆资产清查
try:
result = client.query_debtor_vehicles(
id_card="310101198806154312",
name="张伟",
authorized="1",
vehicle_type="2", # 2-查询全部车型(客车+货车+专项作业车)
user_type="2" # 2-按车辆所有人维度核查
)
print(json.dumps(result, ensure_ascii=False, indent=2))
except Exception as exc:
logger.error("名下车辆资产清查管道执行异常: %s", exc)
3. 终端快捷验证 (cURL)
在完成本地密钥生成与明文 payload 的 AES-128-CBC 加密后,研发人员可通过以下 cURL 命令在终端快速验证网络连通性与网关签名配置:
bash
curl -X POST "https://api.tianyuanapi.com/api/v1/QCXG9P1C?t=1728182400000" \
-H "Access-Id: YOUR_ACCESS_ID" \
-H "Content-Type: application/json" \
-d '{
"data": "5K8mP2xR9vL4nQ7wT1yB6zC3dF0gH8jK9lM2nP5qR8sT1uV4wX7yZ0aB3cD6eF9g=="
}'
网关正常响应时将返回包含 code、message、transaction_id 及加密 data 字符串的 JSON 结构。通过调用上述 Python 代码中的 _decrypt_payload 方法对 data 字段进行 Base64 解码、提取前 16 字节 IV 并执行 AES-CBC 解密,即可还原出包含 vehicleCount 与 list 的明文 JSON 数据。
核心机动车辆资产数据解析与业务映射
在个人债务重组与清偿能力评估场景中,接口返回的每一个字段都直接对应着资产盘点与可支配偿债来源的判定逻辑。下表列出了完整的数据字典与业务映射规则:
| 字段路径 | 数据类型 | 枚举值/取值说明 | 债务重组与清偿能力评估映射逻辑 |
|---|---|---|---|
vehicleCount |
Integer | 自然人名下命中的车辆总数(0 表示未查得车辆) |
核心清偿资产基数 :判断债务人是否具备机动车辆固定资产的第一过滤指标;当 vehicleCount >= 2 时,通常意味着存在非刚需代步的第二台车,具备较高的资产处置或补充增信空间。 |
list |
Array | 车辆明细对象数组 | 逐车估值与权属核对列表:与申请人自行申报的资产清单进行交叉比对,识别是否存在漏报、少报情形。 |
list[].plateNum |
String | 完整车牌号码(如 沪AD12345、苏B88991) |
属地与牌照价值参考:结合车牌前缀可判断车辆注册地与限牌城市属性(如沪A、京A、粤B蓝牌/黄牌),辅助评估车辆流通性与司法协查管辖范围。 |
list[].plateColor |
Integer | 0:蓝色、1:黄色、2:黑色、3:白色、4:渐变绿色、5:黄绿双拼色、6:蓝白渐变色、7:临时牌照、11:绿色、12:红色 |
能源类型与营运属性辅助识别 :0(蓝色)代表传统燃油小型车;4(渐变绿色)代表新能源小型乘用车;1(黄色)与 5(黄绿双拼色)常对应中型以上客货车或大型新能源商用车;7(临时牌照)提示近期新购置或过户流转中车辆。 |
list[].vehicleType |
Integer | 1~4:一至四型客车;11~16:一至六型货车;21~26:一至六型专项作业车 |
清偿资产折现率与生产资料定性 :1(一型客车)多为个人或家庭日常代步乘用车;2~4(二至四型客车)为中大型客运车辆;11~16(一至六型货车)与 21~26(一至六型专项作业车)属于高价值生产经营性资产,其持续运营现金流是评估分期偿债能力的重要依据。 |
技术提示 :在工程落地上,需特别注意两点边界处理。第一,当查询对象名下无登记车辆时,
vehicleCount返回0,此时list字段可能为空数组[]或null,Python 解析层应使用raw_data.get("list") or []防御空指针异常。第二,车牌号plateNum与身份证号id_card属于高敏感个人身份与财产标识(PII),在写入应用日志、消息队列或前端脱敏展示时,务必进行掩码处理(如身份证显示为310101********4312、车牌号显示为沪A***45、关联手机号显示为138****0000),严禁明文落盘至非加密日志系统。
场景化应用:让核验数据赋能合规闭环
1. 个人债务重组申请的自动化资产盘点与申报交叉核验
在债务人提交分期展期或利息减免重组申请时,往往需要在表单中勾选"名下是否有车"并填写车牌号。通过将名下车辆车牌查询A接入前置准入网关,系统可在申请人完成电子授权后自动触发核查(设置 vehicle_type="2" 查询全部车型,user_type="2" 核查车辆所有人):
- 自动匹配通过 :若申请人申报"名下无车"且接口返回
vehicleCount == 0,或者申请人填报的车牌号与list[].plateNum完全一致且为单一vehicleType == 1(一型客车),系统自动标记"资产申报一致",直接放行至自动化清偿方案测算节点。 - 引导补充验证与人工复核 :若申请人申报无车或仅申报1台代步车,但接口返回
vehicleCount >= 2,或list中出现未申报的plateNum,系统将触发"资产信息偏差提醒",自动向申请人推送补充说明工单,引导其上传对应车牌的机动车登记证书(大绿本)以核实车辆是否已设立抵押或实际转让,并同步流转至人工复核队列。
2. 个体经营者货运及专项作业车生产资料清偿能力分层
许多申请个人债务重组的自然人是个体货运司机、工程机械车主或冷链物流从业者。这类群体虽然短期现金流紧张,但名下往往持有高价值的营运货车或工程专项作业车。
- 高价值生产资料识别 :当解析管道发现
list[].vehicleType落在11~16(一至六型货车,尤其是三型及以上中重型货车)或21~26(一至六型专项作业车),且plateColor为1(黄色)或5(黄绿双拼大型新能源)时,系统将其归类为"具备核心生产资料型债务人"。 - 差异化重组策略生成:对于此类群体,系统不会简单采取一刀切的短期催收策略,而是引导其补充货运平台流水或工程承包合同,为其匹配保留生产资料使用权的长期分期清偿方案,实现债权回收率与债务人持续经营能力的双赢。
3. ETC开户人与车辆所有人关系穿透核查
在部分复杂的家庭或关联方债务梳理中,当事人可能声称车辆仅为代办ETC或非本人所有。研发团队可利用入参 user_type 的枚举能力(1-ETC开户人、2-车辆所有人、3-ETC经办人)进行组合穿透:
- 先以
user_type="2"(车辆所有人)核查自然人直接享有物权的车辆; - 若需核实其实际绑定通行扣费的常用车辆,可辅助调用
user_type="1"(ETC开户人)进行比对。若某台plateColor == 4(渐变绿色新能源客车)仅在user_type="1"下出现而在user_type="2"下未出现,则客观印证了"名下代缴通行费但非登记所有人"的陈述,帮助法务与风控专员快速厘清财产边界。
生产环境接入的安全与合规边界
-
严格的个人隐私授权留痕 (
authorized强校验) 名下机动车辆与车牌信息属于受法律严格保护的个人财产隐私。在调用本接口前,业务前端必须向自然人清晰展示《个人信息与资产核查授权书》,明确告知查询目的仅限于个人债务重组与清偿能力评估。后端网关必须校验电子签名存证流水,仅在存证有效时才向接口传入authorized="1",并将每次查询返回的transaction_id与用户的授权文件哈希绑定归档,确保全链路可审计。 -
全链路密文传输与分级脱敏存储 接口通信层强制采用 AES-128-CBC 算法,且每次请求必须通过密码学安全的随机数生成器(如 Python 的
os.urandom(16))动态生成 16 字节 IV,严禁在代码中固定 IV 字符串导致相同身份证号生成相同密文。对于解密后的plateNum等敏感数据,若业务数据库确需持久化保存,应在落库前使用应用内 KMS 密钥二次加密存储,前端界面仅向经授权的重组顾问展示脱敏后的车牌信息。 -
高并发网关限流控制与平滑降级机制 在批量处理存量债务重组案件盘点时,切忌使用无限制的多线程并发直接冲击外部数据接口。建议在 Python 服务层引入令牌桶(Token Bucket)或基于 Redis 的分布式限流器,将请求速率控制在商定 QPS 阈值以内。同时,针对偶发的网络抖动或上游通道维护,应设置合理的超时时间(如
timeout=10秒)与指数退避重试策略;若重试后仍未获取结果,应将工单平滑降级至"待补充人工核验行驶证"状态,保障主业务流程不中断。