4. 接口映射关系定义

好,我们到了第四节课。这节课要聊的东西,是适配改造里最核心的“翻译官”——接口映射关系。

说白了,SDK适配改造,本质就是一场“翻译工程”。

你把旧接口的“方言”翻译成新接口的“普通话”。翻译得准不准,全看映射关系定义得细不细。我见过太多项目,前期映射没做好,后期联调时天天在修数据,那叫一个痛苦。

4.1 映射表设计

我个人习惯,在写任何代码之前,先画一张映射表。这张表就是你的“作战地图”。

映射表不仅记录字段名,还要记录值、类型、行为。别嫌麻烦,你节省的每一分钟规划,都会在后期联调时省回一小时。

核心原则: 映射表是适配层的“宪法”。新旧接口之间的所有差异,都必须在这里定义清楚。

一个标准的映射表,至少包含以下维度:

维度 说明 示例
源字段路径 旧接口中的字段位置 request.user.name
目标字段路径 新接口中的字段位置 newRequest.userInfo.nickName
源数据类型 旧字段的类型 int
目标数据类型 新字段的类型 string
转换规则 如何从源变成目标 toString()
默认值 源字段为空时的兜底 "未知用户"
备注 边界情况或特殊处理 注意:name 可能含特殊字符

你想想看,设计这张表时,最关键的是什么?是字段路径的准确性。我记得有一次,老接口的订单号是 order_id,新接口变成了 transactionNo,看起来就是改了个名字,但新接口还要求订单号必须是大写。如果你只配了字段名,没配转换规则,那上线后就是一堆报错。

避坑指南: 我曾经在映射表里漏了字段路径中的数组索引。老接口返回 items[0].id,新接口是 products[0].productId。光改名字没用,索引的映射也必须清晰。否则,有时候数据对,有时候空,排查起来极其崩溃。

4.2 请求参数映射

请求参数的映射,是适配改造的第一道坎。它解决的是“怎么把旧的数据结构,装进新接口的请求里”。

我这里有一个常用的映射模板,你可以直接拿来用。

public class RequestMapper {
    public NewRequest map(OldRequest oldReq) {
        // 你看到的每个字段,都对应映射表中的一行
        NewRequest newReq = new NewRequest();
        
        // 字段重命名 + 默认值处理
        newReq.setTransactionNo(
            oldReq.getOrderId() != null ? 
            oldReq.getOrderId().toUpperCase() : 
            "DEFAULT_ORDER"
        );
        
        // 子对象嵌套映射
        UserInfo info = new UserInfo();
        info.setNickName(oldReq.getUserName());
        newReq.setUserInfo(info);
        
        // 列表映射
        List<Product> products = oldReq.getItems()
            .stream()
            .map(item -> {
                Product p = new Product();
                p.setProductId(item.getId());
                return p;
            })
            .collect(Collectors.toList());
        newReq.setProducts(products);
        
        return newReq;
    }
}

这里有个容易被忽略的点:参数校验时机。

是映射前校验旧参数?还是映射后校验新参数?我建议双校验。映射前保证旧数据完整,映射后保证新接口不报错。

参数映射三步法:

  1. 解构:把旧请求拆解成最小原子字段
  2. 转换:按映射表逐一处理(改名、改值、改类型)
  3. 重组:按新接口结构重新组装成新请求

4.3 响应结果映射

响应映射比请求映射更复杂。因为返回的数据结构,往往千奇百怪。

你拿到旧接口的响应,要把它变成新接口格式返回给调用方。这个过程,核心是容忍旧、适配新。

我举个例子。旧接口成功了返回 {"status": 1},失败了返回 {"status": 0, "msg": "error"};新接口期望的是 {"code": "SUCCESS", "data": null}。

这时候,映射不能只改字段名,你得把业务逻辑也带进去。

public NewResponse map(OldResponse oldResp) {
    NewResponse newResp = new NewResponse();
    
    // 状态码映射——这是纯业务逻辑
    if (oldResp.getStatus() == 1) {
        newResp.setCode("SUCCESS");
    } else if (oldResp.getStatus() == 0) {
        newResp.setCode("FAILED");
        newResp.setMessage(oldResp.getMsg());
    } else {
        // 你永远要留一手
        newResp.setCode("UNKNOWN");
        newResp.setMessage("未识别的状态码: " + oldResp.getStatus());
    }
    
    // 数据映射——注意空指针
    if (oldResp.getData() != null) {
        newResp.setData(convertData(oldResp.getData()));
    }
    
    return newResp;
}
个人经验: 响应映射里最容易被忽略的是“状态码做聚合”的情况。比如旧接口返回一个列表,每项有自己的状态码。我曾遇到一个项目,旧接口每笔订单都有独立的状态标志,新接口只要求一个整体状态。那你就得写一个聚合逻辑:只要有一笔失败,整体就失败。这个逻辑如果只写在代码里,别人根本看不懂,必须体现在映射表里。

4.4 字段类型转换规则

终于到这里了。类型转换,是映射里的“最后一公里”。很多Bug,就出在类型不对上。

下面这张表格,汇总了我在项目中遇到过的最常见的类型转换场景。

源类型 目标类型 转换规则 注意点
int String String.valueOf() 注意科学计数法?不会,valueOf 不会。但 toString 会受对象影响,建议显式用 valueOf。
String int Integer.parseInt() 字符串必须数字,否则抛异常。建议用正则 \d+ 预校验。
long int (int) value 强转。注意溢出。我曾经因为订单ID从long转int,直接截断了。
boolean String value ? "Y" : "N" 别直接 Boolean.toString(),那是小写true/false,很多系统不接受。
String Date new SimpleDateFormat("yyyy-MM-dd").parse() 格式必须严格匹配。我建议用 LocalDate 替代老旧的 SimpleDateFormat。
List<String> String String.join(",", list) 注意特殊字符。如果列表里有逗号,就乱了,建议用分号或其他分隔符。
重要提醒: 类型转换时,一定要考虑空值和默认值。一个 null 的 Integer 转成 int 会报 NullPointerException。我习惯在映射层统一做一层 if (value == null) return defValue 的处理,别指望上一层或下一层帮你兜底。

为什么类型转换这么容易出错?因为很多开发觉得“大概差不多就行了”。结果就是线上出现NaN、null、空对象、格式不对。

我个人的做法是:每种转换都写单元测试。边界值、null值、超长值、特殊符号,全覆盖。这招帮我挡掉了很多线上事故。

嗯,映射关系这块,内容不少。但你看下来会发现,核心就是一张表、一套代码、几个测试。把这三点做好,适配改造就稳了一大半。

接口映射关系定义核心流程 旧接口(源) 映射表设计 请求参数映射 响应结果映射 字段类型转换 适配层封装 新接口(目标) 映射表是适配层的“宪法” ⚠ 避坑: 1. 漏掉数组索引映射 2. 类型转换忽略null