前后端联调翻车的四个固定位置:字段名、日期、空值、跨域
上周四下午两点开始联调订单导出,六点收工。四个小时改掉的三十多行里,没有一行是业务逻辑。
翻的车就四辆,位置几乎每次都一样。

一、字段名:响应里是 order_no,前端写的是 orderNo
Network 里翻出响应,字段叫 order_no。前端 interface 写的是 orderNo,取出来一列 undefined,页面空白,控制台不报错。
查下去是有人给开放平台加了一行:
spring:
jackson:
property-naming-strategy: SNAKE_CASE
全局一开,所有接口跟着变,自己人用的也变。
要么全项目统一一种命名,谁也别单独改;要么只给对外开放的 DTO 加 @JsonProperty("order_no"),一个字段顶一行,别动全局。
map-underscore-to-camel-case 管的是数据库到实体,property-naming-strategy 管的是实体到 JSON,这两个开关经常被当成一个用。
定位这类问题不用读代码:Network 里挑一个请求,把 Response 的字段名和前端 interface 的字段名并排抄下来,缺哪个一眼就看见,比在代码里翻快得多。
二、空值:字段直接消失了
前端崩在 data.list.length。接口返回的是 {"code":200,"data":{}},list 根本不在里面。
配置里开了 default-property-inclusion: non_null,值为 null 的字段整个不输出。前端拿到的是 undefined,不是 null。这两个在 JS 里不是一回事,JSON.stringify 出来的东西都不一样。
集合查不到数据返回空数组,不返回 null。 这一条没什么可商量的。另外前端取值一律写 ?.,别赌字段在不在。
还有一种:后端存的是空字符串 "",前端 !remark 一样判成没填,但库里就多了一条空串。字段加了唯一索引,第二条就插不进去。
三、日期:一个差八小时,一个少一天
导出文件里的时间整体早八小时。字段用的是 java.util.Date,JVM 时区和数据库时区没对齐。
另一个更气人:筛 10 月 1 日到 20 日,20 号当天的单子全没。
AND create_time BETWEEN #{startDate} AND #{endDate}
endDate 是 2026-10-20,转成时间就是零点,20 号一整天被切在外面。
改在 SQL,别在前端补一天:
AND create_time < DATE_ADD(#{endDate}, INTERVAL 1 DAY)
时间字段统一用 LocalDateTime,不带时区;确实要存某个时刻的地方用 Instant。
这两类好区分:整体差八小时是时区,只差结束那一天是边界。
四、跨域:curl 通,浏览器不通
curl 打过去 200,浏览器报 blocked by CORS policy。
这个错的指纹很好认:GET 正常,POST 全挂。因为 Content-Type: application/json 会先发 OPTIONS 预检,登录拦截器不认识 OPTIONS,回了个 401。
if (HttpMethod.OPTIONS.matches(request.getMethod())) return true;
本地也可以让前端配 Vite 代理,浏览器看成同源,后端不必为 localhost 开口子。
如果后端配了 server.servlet.context-path: /order-service,真实路径前面多一段,前端代理只写了 /api 就转不过去。启动日志里 with context path 那句会告诉你。
两个是约定,两个是环境
字段名和空值,出问题的地方是同一份东西被写了两遍:后端一份 DTO,前端一份 interface,中间没有人对过。谁都没写错,合起来不通。
日期和跨域不是。时区取决于服务器装在哪,跨域取决于前面有没有网关、拦截器怎么写。同一份代码换台机器就换一种坏法。
飞算 JavaAI 的多项目支持管的是前一类。把前后端挂进同一个 IDEA 工作区(2025.2 起支持,装 Multi-Project Workspace 插件),智能会话能同时看见两边,一个需求进去,后端 DTO 和前端 interface 出自同一次生成,命名策略、有没有这个字段、集合还是 null,从生成那一下就是一份。

刚才那个 bug 里,DTO 叫 orderCode,同一次生成出来的 interface 也叫 orderCode,不会再出现一边驼峰一边下划线,那行命名策略配置也就没有存在的必要。
生成质量还看模型这一头。插件里那几档,是在 Java 上表现好的开源模型上调出来的;旁边另开一栏直连第三方,GLM-5.2、5.3 和 DeepSeek-V4 都在可选范围里,用惯哪个就用哪个。
挂进去不改原来的东西。目录还在原处,pom.xml、package.json 一个字节不动,工作区自己能记住的只是一份项目路径。
上游还有 /前后端设计:先出数据库设计和接口定义,再照着接口画页面,产物落在项目 docs/ 里。接口和表结构是这条链中间的两步,写下来的是文件,不是会话里的几句话。

后一类它不管。时区怎么设、SQL 怎么写、拦截器放不放 OPTIONS,答案不在代码里,在部署方式和数据库里。它不知道你的服务器在哪台机器上,也不知道网关前面挂了什么。替你猜一次,就是给下次埋一个更难的坑。
剩下两个也不是没办法。跨域配置、日期的序列化格式,都能提前写进项目模板,新建服务时自动带上,别每次现查。这属于脚手架管的事。
通用 AI 编码方式也能改这些代码,把前端贴进对话照样改得动。它卡住的地方是只看得见一个仓库,两边一致不一致,靠你在中间传话。下个需求重新贴一遍,还得记得上次哪里对不上。
下次还是这四辆车
这类问题查起来有个共同动线:先在浏览器里看真实数据,再回头翻代码。在代码里找「应该是什么」,容易绕进「我写得没问题」;在响应里看「实际是什么」,几秒钟就能定位。
- 字段名:Network 的 Response,跟 interface 逐个对
- 空值:字段在不在 JSON 里,不在就是被 inclusive 过滤了
- 日期:差八小时是时区,差一天是边界
- 跨域:Console 第一条报错,是 CORS 还是 401
这四条写下来不到一页,真出事还是得看自己项目那一份。每个项目的坑都在细节里:字段名是哪个大小写、日期差在哪一层、网关有没有改写路径。清单只负责定位到位置,修还得看现场。我们组后来把这些约定贴进了 PR 模板,改接口的人顺手勾一遍,比开会讲一次管用。
改完一处别急着往下一条。把那个请求在 Network 里留个副本,四条都改完再一次性重放,省得改第三处的时候把第一处又弄回去。
第二天我把四条写进组里的检查单,补了第五行:接口返回的字段,谁改谁在 PR 里贴一份新响应。这条比前四条都管用,逼着改的人自己看一眼实际输出。
这四个位置,我们组现在过一遍十分钟。前两个看接口定义,后两个看部署配置。以前是一下午,还得两个人对着喊。
前两个位置现在很少耗时间了。飞算 JavaAI 把两个仓库放进同一个工作区,接口定义先落成文件,DTO 和 interface 同一次生成,字段名和空值不用再对。剩下两个还得自己排,时区和跨域从来不看代码写得漂不漂亮。
说回那次联调。四条修完已经六点,导出跑出来还是少一条数据。查到最后一行的条件是 status != 3,把 status 为 null 的单子全排掉了,跟上面四条都没关系。联调结束从来不等于功能对了,只是两边终于能对上话。
你们联调最常卡在哪一类?我这边是空值,每次都是。
- 点赞
- 收藏
- 关注作者
评论(0)