# 征信解析客户端与接口设计方案

## 1. 背景

本次需求来自以下两份输入材料：

- `assets/征信解析/HTML引擎服务_ 接口设计说明书_1.docx`
- `assets/征信解析/征信解析接口payload.xlsx`

根据说明书，本次目标外部接口为 `POST /xfeature-mngs/conversation/htmlEval`，请求格式为 `multipart/form-data`，核心入参为：

- `model`
- `file`
- `hType`

返回体结构为：

- `message`
- `status_code`
- `payload`

根据 Excel，`payload` 当前至少包含以下三个主题域对象：

- `lx_header`
- `lx_debt`
- `lx_publictype`

当前仓库中已有银行流水分析平台的集成代码，集中在 `ccdi-lsfx` 模块；但征信解析服务与流水分析平台不是同一套接口语义和配置边界。本次目标是在不破坏现有 `lsfx` 链路的前提下，新增一套独立的征信解析调用能力，并在 `ccdi-lsfx` 中暴露一个可供联调使用的后端接口。

## 2. 目标

- 在 `ccdi-lsfx` 模块中新增独立的征信解析 `Client`。
- 在 `ccdi-lsfx` 模块中新增对外联调接口，用于接收 HTML 文件并调用征信解析服务。
- 征信解析配置与现有 `lsfx` 配置完全隔离，不共用同一配置前缀。
- 配置项使用单一完整 URL，不拆分 `base-url` 和 `endpoints`。
- 外部服务成功响应时，保留 `message`、`status_code`、`payload` 的原始语义。
- 控制器仅做最小参数校验，不额外引入项目上传记录、异步任务、状态轮询或结果落库。

## 3. 非目标

- 不接入 `ccdi-project` 现有项目文件上传主链路。
- 不新增征信解析结果落库逻辑。
- 不补充前端页面接入。
- 不把征信解析 `Client` 并入现有 `LsfxAnalysisClient`。
- 不将征信解析配置挂到 `lsfx.api.*` 下。
- 不额外设计说明书之外的兜底流程、降级流程或兼容协议。

## 4. 方案对比

### 4.1 方案 A：直接扩展现有 `LsfxAnalysisClient`

把征信解析方法直接加到现有 `LsfxAnalysisClient`，并继续使用 `lsfx.api.*` 配置。

优点：

- 改动文件数最少。

缺点：

- 语义上把两套不同外部服务混在同一个 Client 中。
- 配置边界不清晰，后续切换环境容易误改现有流水分析配置。
- 不符合本次“新增 Client，并在配置文件里与 lsfx 分开”的要求。

### 4.2 方案 B：在 `ccdi-lsfx` 中新增独立 `CreditParseClient`

在 `ccdi-lsfx` 中新增独立控制器和独立 `Client`，并使用新的配置前缀承载完整 URL。

优点：

- 满足“接口加在 `ccdi-lsfx` 中”的要求。
- 满足“新增 Client，在配置文件里与 lsfx 分开”的要求。
- 与现有银行流水分析集成边界清晰，不会污染主链路。
- 仍然复用现有 `HttpUtil`，实现路径最短。

缺点：

- 需要新增少量类和配置项。

### 4.3 方案 C：在 `ccdi-project` 中直接暴露业务接口并自行发起调用

由 `ccdi-project` 新增控制器和 service，直接使用 `RestTemplate` 调征信解析平台。

优点：

- 对业务模块看起来更直接。

缺点：

- 与用户已确认的“接口加在 `ccdi-lsfx` 中”不一致。
- 会把外部平台调用能力分散到多个模块。
- 后续复用和维护成本更高。

## 5. 推荐方案

采用方案 B：在 `ccdi-lsfx` 中新增独立 `CreditParseClient`、独立配置和独立联调接口。

推荐原因：

- 与已确认需求完全一致。
- 保持最短路径实现，不引入无关业务改造。
- 外部服务边界清晰，后续如果还要补企业征信或更多解析接口，可以继续沿用这一结构。

## 6. 模块边界与目录设计

本次改动集中在 `ccdi-lsfx` 模块。

建议落点如下：

- `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/controller/CreditParseController.java`
- `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/client/CreditParseClient.java`
- `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/response/CreditParseResponse.java`
- `ccdi-lsfx/src/main/java/com/ruoyi/lsfx/domain/response/CreditParsePayload.java`
- `ruoyi-admin/src/main/resources/application-dev.yml`
- `ruoyi-admin/src/main/resources/application-nas.yml`

职责划分如下：

- `CreditParseController`
  - 接收联调方上传的 HTML 文件和可选参数。
  - 做最小参数校验。
  - 将 `MultipartFile` 转换为临时文件。
  - 调用 `CreditParseClient`。
  - 返回 `AjaxResult`。

- `CreditParseClient`
  - 读取独立配置中的完整 URL。
  - 组装 `multipart/form-data` 参数。
  - 调用现有 `HttpUtil.uploadFile(...)`。
  - 统一记录调用日志和抛出平台调用异常。

- `CreditParseResponse`
  - 映射外部接口返回的 `message`、`status_code`、`payload`。

- `CreditParsePayload`
  - 承载 `lx_header`、`lx_debt`、`lx_publictype` 三个主题域对象。

## 7. 配置设计

征信解析配置独立于 `lsfx`，使用新的配置前缀。

建议配置结构如下：

```yml
credit-parse:
  api:
    url: http://64.202.94.120:8081/xfeature-mngs/conversation/htmlEval
```

说明：

- `url` 保存单一完整地址，不拆分为 `base-url` 和 `endpoints`。
- 不复用 `lsfx.api.connection-timeout`、`lsfx.api.read-timeout` 等配置项。
- 本次实现优先沿用现有 `RestTemplate` Bean；若后续确需独立超时配置，再单独扩展专用 HTTP 配置，不在本次设计中预埋。

## 8. 对外接口设计

### 8.1 接口定义

- 方法：`POST`
- 路径：`/lsfx/credit/parse`
- Content-Type：`multipart/form-data`

接口放在 `ccdi-lsfx` 模块中，作为征信解析联调入口。

### 8.2 请求参数

| 参数名 | 类型 | 必填 | 默认值 | 说明 |
| ------ | ---- | ---- | ------ | ---- |
| `file` | `MultipartFile` | 是 | 无 | 征信 HTML 文件 |
| `model` | `String` | 否 | `LXCUSTALL` | 主题域编码 |
| `hType` | `String` | 否 | `PERSON` | 报文类型 |

### 8.3 本地校验规则

控制器层只做最小必要校验：

- `file` 不能为空。
- 文件名不能为空。
- 文件后缀必须为 `.html` 或 `.htm`。
- `model` 为空时补默认值 `LXCUSTALL`。
- `hType` 为空时补默认值 `PERSON`。

不在控制器层重复实现外部平台业务码校验，不提前限制：

- `model` 是否只能取 `LXCUSTALL`
- `hType` 是否只能取 `PERSON` 或 `ENTERPRISE`

上述业务校验结果以外部服务返回的 `status_code` 为准。

### 8.4 返回规则

返回采用若依标准 `AjaxResult` 包装，规则如下：

- 本地参数校验失败：返回 `AjaxResult.error(...)`
- 外部调用成功拿到响应：返回 `AjaxResult.success(responseBody)`
- 网络异常、超时、反序列化异常或其他调用异常：返回 `AjaxResult.error("征信解析调用失败：" + e.getMessage())`

其中 `AjaxResult.data` 中保留外部原始业务语义，例如：

```json
{
  "code": 200,
  "msg": "操作成功",
  "data": {
    "message": "成功",
    "status_code": "0",
    "payload": {
      "lx_header": {},
      "lx_debt": {},
      "lx_publictype": {}
    }
  }
}
```

这意味着：

- 外部业务失败码如 `ERR_99999`、`ERR_10002` 仍然作为正常 HTTP 调用结果返回。
- 调用方根据 `data.status_code` 判断业务成功与否。
- 本地接口不对外部业务码做二次翻译，避免语义偏移。

## 9. Client 设计

### 9.1 调用链

调用链固定为：

`CreditParseController` -> `CreditParseClient` -> `HttpUtil.uploadFile(...)` -> 征信解析服务

### 9.2 请求组装

`CreditParseClient` 发送 `multipart/form-data`，参数仅包含：

- `model`
- `file`
- `hType`

不新增说明书之外的扩展字段。

### 9.3 URL 读取

`CreditParseClient` 直接读取：

- `credit-parse.api.url`

不做 URL 拼接。

### 9.4 日志

日志风格保持与 `ccdi-lsfx` 现有习惯一致，至少包含：

- 请求开始：文件名、`model`、`hType`
- 请求结束：耗时、返回 `status_code`
- 请求失败：错误信息和异常堆栈

### 9.5 异常

`CreditParseClient` 统一抛出 `LsfxApiException` 风格异常，保持 `ccdi-lsfx` 模块内部异常语义一致。

## 10. 响应对象设计

### 10.1 请求 DTO

本次不新增请求 DTO。

原因：

- 控制器直接接收 `MultipartFile file, String model, String hType` 即可。
- `Client` 内部直接构造 `Map<String, Object>` 最短路径实现。
- 纯透传场景下，引入请求 DTO 只会增加样板代码。

### 10.2 响应 DTO

建议新增以下响应对象：

#### `CreditParseResponse`

- `String message`
- `String statusCode`
- `CreditParsePayload payload`

其中 `statusCode` 使用注解映射 JSON 字段 `status_code`。

#### `CreditParsePayload`

- `Map<String, Object> lxHeader`
- `Map<String, Object> lxDebt`
- `Map<String, Object> lxPublictype`

其中字段名使用注解映射：

- `lx_header`
- `lx_debt`
- `lx_publictype`

### 10.3 为何不展开成 30+ 强类型字段

本次目标是“添加调用征信解析接口的方法”，不是“建立完整征信指标领域模型”。因此本次不把 Excel 中的 30 余个字段全部定义为强类型 Java 属性，原因如下：

- 当前需求核心是打通调用，不是做复杂业务计算。
- 外部字段后续仍可能调整，强类型化会增加维护成本。
- 三段 `Map<String, Object>` 已足够支撑联调与响应透传。

后续如果业务明确需要对单个字段做服务内计算或落库，再单独收敛更细的领域对象。

## 11. 文件处理设计

由于现有 `HttpUtil.uploadFile(...)` 接收的是 `File` 对象，控制器层需要把上传的 `MultipartFile` 转为临时文件。

处理规则如下：

- 使用系统临时目录创建临时文件。
- 临时文件名保留原始后缀，方便外部服务识别文件类型。
- `Client` 调用结束后，在 `finally` 中删除临时文件。
- 临时文件删除失败只记录日志，不覆盖主流程结果。

该处理仅用于本次同步调用，不沉淀到项目上传目录，不复用 `ccdi-project` 的文件上传记录表。

## 12. 测试设计

本次测试只覆盖后端最小闭环，不扩展到前端。

建议至少覆盖以下场景：

1. 合法 HTML 文件上传，外部服务返回 `status_code = 0`
2. 文件为空，本地接口直接返回参数错误
3. 文件后缀非法，本地接口直接返回参数错误
4. 外部服务不可达或超时，接口返回调用失败信息
5. 外部服务返回业务失败码，例如 `ERR_99999`，本地接口仍然按成功调用返回 `AjaxResult.success(...)`

如果测试过程中需要临时启动后端进程，测试完成后必须主动关闭，避免残留进程占用端口。

## 13. 实施边界确认

本次实施只包含以下内容：

- 新增 `CreditParseClient`
- 新增 `CreditParseController`
- 新增征信解析响应对象
- 新增独立配置项 `credit-parse.api.url`
- 补充后端测试

本次明确不包含：

- `ccdi-project` 主上传链路改造
- 项目上传记录表扩展
- 征信结果落库
- 前端接入
- 额外的兼容性补丁或兜底方案

## 14. 风险与约束

- 外部服务返回结构以说明书和联调实际为准；若实际返回字段名与说明书不一致，需要在实现阶段按真实返回修正映射。
- 本次沿用现有 `RestTemplate` Bean；若后续征信解析服务需要独立连接池或独立超时，再单独设计，不在本次范围内扩展。
- 由于本次不做落库，调用结果仅用于联调和即时查看。

## 15. 预期交付物

- 一份后端设计实现代码，落在 `ccdi-lsfx` 模块
- 一份后端实施计划文档，落在 `docs/plans/backend/`
- 一份前端实施计划文档，落在 `docs/plans/frontend/`，明确本次无前端实施项
- 一份本次改动实施记录文档，落在 `docs/reports/implementation/`

本方案采用“`ccdi-lsfx` 内新增独立征信解析 Client 与联调接口、配置与 `lsfx` 完全隔离”的方式，以最短路径补齐征信解析调用能力，同时保证与现有银行流水分析链路边界清晰，符合当前需求边界。
