ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Spring Boot+Vue前后端分离实战:战舰世界百科系统从零到部署

Spring Boot+Vue前后端分离实战:战舰世界百科系统从零到部署 做这个《战舰世界》游戏百科信息系统时很多人第一反应是“这不就是个CRUD管理后台吗”但实际操作下来从技术选型、数据库设计到前后端联调再到打包部署每一步都有不少值得复盘的地方。我用Spring Boot Vue这套组合完整做了一遍前后端分离的开发模式在这个项目里体现得很充分今天就把它从零到部署的全过程拆开讲清楚。先说下这个系统是干什么的它本质上是一个以《战舰世界》游戏内容为背景的百科资料管理平台前端用Vue渲染舰船数据、版本信息、游戏玩法攻略等内容后端用Spring Boot提供RESTful API配合MySQL做数据持久化。整个项目非常适合JavaWeb方向的学生作为课程设计参考也很适合刚接触前后端分离开发的初学者用来理解Spring Boot与Vue的交互机制。1. 项目整体设计与技术选型思路1.1 为什么选择Spring Boot Vue这套组合先说后端。Spring Boot在JavaWeb领域的地位基本不需要多解释它解决了传统SSH或者Spring MVC时代大量繁琐的XML配置问题内嵌Tomcat容器意味着打一个jar包就能直接跑起来这对于课程设计或者中小型项目来说非常适合。而且Spring Boot的生态足够丰富Spring Data JPA、MyBatis、Spring Security这些组件都在它有很好的集成方案。前端选Vue核心原因是它的渐进式框架特性。如果你只是做个简单的百科展示页面Vue可以当模板引擎用但如果你需要做后台管理、数据表格、动态路由这些复杂交互它的组件化开发和响应式数据绑定又能很好地支撑。《战舰世界》百科系统包含舰船列表、分类筛选、详情展示、后台管理等多个页面用Vue来做这些交互逻辑比直接JSPJavaScript要舒服得多。那为什么不直接用传统JSP呢这里有个重要的认知转变。JavaWeb的传统做法是JSP写页面Servlet或者Controller返回视图前端代码和服务端代码混合在一起。这种模式在项目规模小的时候没问题但一旦页面交互变复杂前后端耦合会很高。拆成前后端分离之后后端只需要把JSON数据返回给前端前端自己决定怎么渲染好处就是职责清晰、后期扩展方便。我做这个项目的时候前台展示页面和后台管理页面之间的切换就是通过Vue路由控制的数据源完全一样只是展示逻辑不同这个体验是JSP那种模版方式很难做到的。1.2 百科信息系统的需求拆解与功能边界《战舰世界》百科信息系统的定位不是做一个游戏官网而是一个“内容管理 信息检索”的系统。所以功能边界必须明确否则很容易做成一锅粥。我在设计时把系统划分成两大面向前台展示和后台管理。前台面向普通用户要能浏览舰船列表、按分类筛选、搜索特定舰船、查看舰船详情、阅读版本更新日志和玩法攻略后台面向内容管理员要能维护舰船数据、管理百科内容、发布游戏资讯。这两个面向共用同一套数据模型只是API的权限粒度不同。这个设计的核心价值在于它把CRUD这个基础能力放到了场景里而不是为了CRUD而CRUD。比如舰船列表不是简单地把数据表扔到页面上而是需要处理分类筛选、关键词搜索、分页加载这些真实需求。每个舰船的详情页需要有基本属性血量、主炮、鱼雷、航速等、历史背景、玩法评价等内容。这些需求直接影响数据库表结构的设计也直接影响API接口的粒度划分。2. 数据库设计与核心API规划2.1 数据表设计与字段规划数据库设计是整个项目的地基我建议动手之前先画一遍实体关系图。这个系统的核心实体是舰船信息围绕它展开的有舰船分类、游戏版本、攻略文章、用户评论这几个维度。舰船分类表ship_category这个很简单存分类名称、简介和排序字段。舰船基本信息表ship_info是这个项目的大头字段设计要充分考虑百科场景。我当时设计的核心字段包括船名、英文名、所属国家美、日、德、苏、英、法、意、泛亚等、战舰类型驱逐舰、巡洋舰、战列舰、航母、等级I到X级、生存性血量、机动性航速、转向半径、火炮系统主炮口径、射程、装填时间、鱼雷系统部分舰船没有、防空能力、隐蔽系数等。这里有一个实操经验分享不要把数值性属性和文本性属性全堆在一张表里。我第一版设计时把舰船的详细属性字段全部放在ship_info表中结果表字段超过30个管理后台的编辑表单变得极其冗长。后来改成舰船主表和舰船专属属性表分离的方式ship_info表只存通用信息和关联字段ship_attr表存不同舰种的差异化属性用JSON格式存储灵活字段。这个改造对于后期扩展新舰船类型特别有效因为新舰种可能引入新的属性维度而你又不想为了一个字段去改大表结构。版本更新表game_version用于发布版本更新日志字段包括版本号、更新时间、更新内容、适用服务器等。攻略文章表通过关联分类和标签来组织百科内容支持多级分类。2.2 RESTful API接口设计规范API设计这块我遵循的是RESTful风格但不过度追求“RESTful洁癖”。核心接口大致如下GET /api/ship/list?categoryId1typedestroyerpage1size10 —— 舰船列表支持分类筛选和分页GET /api/ship/detail/{id} —— 舰船详情GET /api/ship/search?keywordYamato —— 关键词搜索GET /api/version/list —— 版本更新列表GET /api/article/list?categoryguide —— 攻略文章列表POST /api/admin/ship/save —— 后台保存舰船含新增和修改DELETE /api/admin/ship/{id} —— 后台删除舰船POST /api/admin/login —— 管理员登录列表接口我是一定会做分页的这是刚需。分页参数统一为page第几页和size每页几条因为前端的表格组件基本都是这个约定。后台数据量不大时很多人喜欢直接查全表但百科类系统的数据会持续增长而且前端需要适配table组件的分页逻辑所以统一分页接口是更合理的选择。关于接口返回格式我花了半天时间踩了一个坑后总结出了一个统一规范后端所有API返回JsonResult对象其结构包含三个字段code200表示成功500表示失败、message提示信息、data业务数据。这个规范看着简单但它避免了前端每个页面都要处理不同错误格式的问题。如果今天这个接口返回{status:1}明天那个返回{code:ok}前端Axios拦截器就无法统一处理错误提示工作量会成倍增加。3. 环境搭建与项目初始化实录3.1 后端开发环境准备后端环境这块我用的是JDK 8。虽然JDK 17已经出了很久但考虑到Spring Boot 2.x对JDK 8的兼容性最稳定而且课程设计或公司旧项目的环境大多是8所以没必要追求新版本。IDE选择上IntelliJ IDEA是首选如果你用Eclipse也没有问题但IDEA对Spring Boot的自动化提示确实更友好。Maven配置里有一个关键操作一定要配置国内镜像。原因很简单Spring Boot项目需要拉取大量的依赖包如果你用默认的Maven中央仓库在国内网络环境下拉取速度会让你怀疑人生。在Maven的settings.xml中加入阿里云镜像项目初始化下载依赖的时间能从十几分钟降到一两分钟。创建项目的方式推荐直接从Spring Initializr网站生成初始项目或者在IDEA里通过Spring Initializr创建。勾选Spring Web、MySQL Driver、MyBatis或Spring Data JPA、Lombok这几个依赖。我自己习惯用MyBatis因为SQL可控性更强但Spring Data JPA的代码量更少两者都可以看你的基础。如果你对SQL比较熟就用MyBatis如果不想写SQLJPA的自动CRUD功能能省不少事。3.2 前端项目初始化与依赖安装前端用Vue CLI或者Vite创建项目我用的Vue CLI脚手架vue create battleship-web。选择Vue 2还是Vue 3这取决于你的学习背景和项目要求。我做的时候用的Vue 2因为Element UI在Vue 2下最成熟资料也多。如果你选Vue 3对应的是Element Plus。创建完之后第一件事是把项目跑通npm run serve能在本地启动开发服务器。这里有一个初学者很容易踩的坑Vue开发服务器默认运行在8080端口而Spring Boot如果不改配置默认也是8080两者会冲突。常见做法是把Vue的端口改成8081vue.config.js里设置devServer.port或者把Spring Boot的端口改成8082。我习惯是把后端改成8082因为前端保持8080这个约定俗成的端口在浏览器访问习惯上更顺手。前端依赖安装阶段npm默认源的下载速度同样是个问题。解决方案是配置淘宝镜像源npm config set registry https://registry.npm.taobao.org/。这一步几乎每个用npm安装依赖的人都该做尤其是装Element UI、Axios这些较大的包时差距很明显。3.3 数据库初始化与连接配置数据库建议先用Navicat或者命令行创建一个空库比如就叫wows_encyclopedia。表结构不用手工建我准备了SQL脚本直接把脚本导入数据库就能生成所有表和初始数据。Spring Boot连接数据库需要配置application.yml文件核心配置项包括数据源URL、用户名、密码、MyBatis的mapper xml位置等。有一个实操细节数据库URL一定要加上characterEncodingutf-8和useSSLfalse这两个参数。前者解决中文乱码问题后者避免MySQL 8.x在开发环境下频繁加载SSL证书导致连接缓慢甚至失败。4. 后端核心功能模块实现详解4.1 统一返回体与全局异常处理统一返回体我之前已经说过这里重点说异常处理。很多初学Spring Boot的同学在Controller里写大量的try-catch然后把错误信息直接返回给前端。这个做法的问题很明显每一处都要写重复代码而且流程控制混乱。正确的做法是利用Spring Boot的RestControllerAdvice做全局异常处理。在GlobalExceptionHandler中用ExceptionHandler注解定义一个方法捕获Service层抛出的自定义业务异常统一转成JsonResult格式返回。这样Controller层的每个方法只需要写正常流程遇到异常直接throw一个BizException即可由全局异常处理器兜底。全局异常处理还有一个附加价值它提供了统一的日志记录入口。我在异常处理方法里加了一条log.error记录包含异常类名、方法名、错误信息这样排查问题时直接看服务端日志就行不用一个方法一个方法地找。这个习惯对后期维护和答辩演示都有很大帮助。4.2 分页查询与动态条件搜索舰船列表的查询是这个项目的核心接口它的业务逻辑是用户可能按照分类战列舰/驱逐舰、等级、国家、关键词等多个条件组合筛选也可能不传任何条件查全部。MyBatis实现动态SQL最顺手的方式是用 和 标签。我不太推荐在Java代码里手动拼接SQL字符串容易出错且难以维护。用MyBatis的动态XML每个筛选条件单独判断有参数才拼到SQL里没有参数就自动跳过。查询语句大致长这样select idselectShipList resultTypecom.example.entity.ShipInfo SELECT * FROM ship_info where if testcategoryId ! null and categoryId ! AND category_id #{categoryId} /if if testtype ! null and type ! AND type #{type} /if if testkeyword ! null and keyword ! AND (ship_name LIKE CONCAT(%, #{keyword}, %) OR ship_name_en LIKE CONCAT(%, #{keyword}, %)) /if /where ORDER BY ship_level, ship_type /select分页用PageHelper插件这个插件是国人开发的用法非常简单在查询前一行调用PageHelper.startPage(pageNum, pageSize)查询结果自动完成分页包装。返回的数据结构里除了记录列表还要返回总数total前端表格组件才能正确计算总页数。很多人第一次用PageHelper会犯一个错误startPage之后紧接着的查询必须就是需要分页的那个查询中间不能插入其他SQL操作否则PageHelper会把所有SQL都拦截了导致数据出错。这个小坑我在项目里遇到过写在这里提醒各位。4.3 文件上传与管理百科系统里舰船图片、攻略配图都需要上传功能。Spring Boot处理文件上传比较简单通过MultipartFile这个接口接收前端传过来的文件然后保存到服务器指定目录。但在做部署的时候要注意一个问题如果把图片保存到Spring Boot的jar包内部目录每次重新部署上传的图片就会被清空。正确做法是把图片存放到一个独立的静态资源目录比如/opt/battleship/upload/然后通过配置WebMvcConfigurer的addResourceHandlers方法把URL路径映射到这个物理目录。Configuration public class WebConfig implements WebMvcConfigurer { Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/upload/**) .addResourceHandler(file:/opt/battleship/upload/); } }这样配置之后前端就能通过http://服务器IP:8082/upload/xxx.jpg直接访问上传的图片了。但需要注意这只是开发环境的方案生产环境如果并发访问量高最好还是用Nginx直接托管静态资源让Spring Boot专注于处理API请求。4.4 后台登录鉴权方案百科系统的后台管理不能让所有人随便进入所以需要一个简单的登录鉴权机制。因为这是课程设计级别的系统我没有引入Spring Security这种重量级的框架而是采用基于Token的轻量鉴权方案。实现逻辑是用户输入账号密码登录成功后后端生成一个UUID作为token存到Redis里也可以用内存Map同时把这个token返回给前端。前端在后续的请求头中带上Authorization: token字段后端通过拦截器HandlerInterceptor在请求进入Controller之前校验token是否有效。这个方案的好处是简单清晰而且符合当前主流的前后端分离开发模式。如果你不想引入Redis用本地Map存储token也能跑但缺点是这样的内存Map在服务重启后就丢了多实例部署时也不好共享生产环境建议换Redis。5. 前端页面实现与接口联调细节5.1 项目目录结构与路由规划前端项目的目录结构直接决定后期维护的舒心程度。我在src目录下按模块来划分不要把所有组件堆在一层。核心结构如下views/home —— 前台首页、舰船列表页、舰船详情页views/admin —— 后台管理登录页、舰船管理表格页、文章管理页components —— 公共组件分页组件、舰船卡片组件等api —— 按模块封装的接口调用文件router —— 路由配置文件路由配置采用了常规的一级路由嵌套二级路由方式。前台页面和后台管理页面共用同一个壳子通过不同路由进入。在路由配置中有个要点后台管理相关的页面需要添加路由守卫检查浏览器本地是否存了token没有则跳转到登录页。router.beforeEach((to, from, next) { if (to.meta.requiresAuth !localStorage.getItem(token)) { next(/admin/login); } else { next(); } });这个守卫虽然逻辑简单但它是后台管理系统的安全底线。没有它用户手动在地址栏输入/admin/ship就能直接绕过登录访问管理界面。5.2 Axios请求封装与拦截器前后端分离模式下前端与后端交互全靠Axios。我强烈建议对Axios做一层封装不要在每个组件里直接裸调axios.get。封装的核心目的是统一处理BaseURL、请求头、错误提示、Token注入这些公共逻辑。我在项目里创建了一个request.js文件用法是把Axios实例化后设置baseURL为http://localhost:8082/api然后添加请求拦截器和响应拦截器。请求拦截器的作用是每次请求自动带上adminToken代码逻辑就是service.interceptors.request.use(config { const token localStorage.getItem(adminToken); if (token) { config.headers[Authorization] token; } return config; });响应拦截器的作用更关键后端返回的数据格式是{code, message, data}如果code不等于200说明业务处理失败此时拦截器自动弹出错误提示让调用方直接处理data就行。service.interceptors.response.use(response { const res response.data; if (res.code ! 200) { ElementUI.Message.error(res.message); return Promise.reject(new Error(res.message)); } return res; });这个封装做完之后项目里每个api目录下的接口函数都变得非常清爽只需要关心请求方法、URL和参数只用处理正常业务的返回值。我见过不少同学在每个页面组件里都写一遍错误处理逻辑可以说是完全没必要的重复劳动。5.3 舰船列表页与详情页实现舰船列表页是前台的核心页面它需要同时应对展示和筛选的需求。页面上部是筛选区包含战舰类型下拉框、国家下拉框、关键词输入框下部是舰船卡片网格每个卡片显示舰船的名称、等级、类型、国籍和缩略图。这个页面用Element UI的组件组合起来很快但有一个交互细节值得琢磨当用户切换筛选条件时需要重新请求列表接口并且重置页码到第一页。因为如果不重置页码用户在第5页切换了筛选条件但请求发的是page5新条件下可能总共就3页这样会返回空数据。我刚开始写的时候就犯了这个错后来加了change事件里同时重置page1的逻辑才解决。舰船详情页相对简单从路由参数中获取舰船id调用/detail接口拿到数据然后分组展示。舰船数据按模块分区展示比较合理基本信息区、武器配置区、生存与机动区、历史背景区。每个区域用卡片组合视觉上清晰用户查找信息也方便。5.4 后台管理表格页实现后台管理页面的核心是一个带分页的表格以舰船管理为例表格展示舰船名称、类型、等级、国家操作列包含编辑和删除按钮。页面顶部有新增按钮和搜索框。Element UI表格和分页组件是无缝配合的但后端返回数据结构需要和分页组件的current-page和page-size绑定好。这块我建议单独封装一个数据加载方法loadData()每次表格数据变化切换分页、搜索、删除、编辑都统一调用这个方法保持数据流的单一口径。新增和编辑共用同一个弹窗表单组件通过传入的props判断是新增还是编辑模式从而决定提交时调用的是save接口还是update接口。这里的细节是表单校验规则Element UI的form组件提供rules校验机制字段必填和格式校验都在这里配置尽量不要在后端再次做重复校验。当然后端也需要做基础的非空校验因为API可能被其他客户端直接调用不能依赖前端做唯一保障。6. 项目部署全流程记录6.1 本地开发环境联调步骤开发模式下前端和后端是分开启动的Spring Boot启动在8082端口Vue开发服务器在8080端口。由于端口不同浏览器中运行的前端页面请求http://localhost:8082/api下的接口时属于跨域请求浏览器会拦截。解决方式有两种后端CORS配置和Vue开发代理。我推荐用Vue的代理配置因为它在开发环境模拟了生产环境Nginx反向代理的访问方式更接近真实部署形态而且不需要在后端加任何CORS相关代码。在vue.config.js中配置代理module.exports { devServer: { port: 8080, proxy: { /api: { target: http://localhost:8082, changeOrigin: true } } } }这里的含义是前端发出的/api开头的请求会被开发服务器代理转发到8082端口的后端服务。浏览器看到的请求地址始终是8080不会触发跨域限制。6.2 生产环境构建与部署部署过程比很多人想象的要简单因为Spring Boot的可执行jar包和前端打包生成的静态文件可以完美结合。首先在后端项目的pom.xml中配置前端构建插件思路是在Maven打包前自动执行npm run build把Vue打包生成的dist目录里的静态文件复制到Spring Boot的static目录下。这样最终只需要打一个jar包启动后既能提供API接口又能提供前端页面的访问能力一套服务搞定所有功能。具体配置方式一般用frontend-maven-plugin在package阶段执行npm install和npm run build然后用maven-resources-plugin把dist目录复制到classpath下的static目录。如果没有用上述Maven插件方式也可以手动操作先在Vue项目目录执行npm run build然后把dist目录的所有文件复制到Spring Boot的static目录下再用mvn clean package打jar包。两种方式效果等同手动方式更容易理解整个过程。启动时在服务器上执行java -jar battleship-1.0.0.jar即可。浏览器输入http://服务器IP:8082前端页面和后端API都在同一个端口提供服务不需要额外配置Nginx。这是Spring Boot大包模式的优点特别适合中小型项目的简单部署。6.3 常见的部署文档应该包含哪些内容部署文档不是随便写几句“运行即可”一份可执行的部署文档应包含以下内容环境要求JDK版本、MySQL版本、Node版本、数据库初始化步骤SQL脚本创建表结构和初始数据、配置文件修改说明数据库账号密码、端口号、上传路径、构建步骤前端打包、后端打包、启动命令jar启动、日志查看、访问路径与默认账号。把这些写清楚任何人拿到项目都能独立完成部署这也是课程设计份量的一部分。7. 常见问题排查与避坑指北7.1 数据库连接相关的坑排在第一的就是MySQL 8.x的驱动问题。如果你使用MySQL 8.xSpring Boot的application.yml里driver-class-name要写com.mysql.cj.jdbc.Driver而不是老的com.mysql.jdbc.Driver后者的类已经被移除了。另外一个就是URL必须带时区参数serverTimezoneAsia/Shanghai否则连接会报时区错误。字段命名问题也很典型。很多同学的数据库字段是下划线命名如ship_name而实体类属性是驼峰命名shipName。MyBatis需要一个配置来自动映射这两者map-underscore-to-camel-case这个配置打开之后数据库字段和实体类属性就能自动对应省去大量为每个字段写resultMap的时间。7.2 前端开发中的典型问题Vue安装依赖时经常遇到的下载缓慢甚至卡死问题不必多说了换源后基本能解决。还有一个常见问题是npm install报错提示node-sass安装失败这是因为node-sass对Node版本有严格匹配而且经常要从GitHub下载二进制文件网络稳定性影响很大。解决办法是删掉node_modules和package-lock.json然后换成dart-sasssass包或者直接用对应的Node版本安装。这个问题的根源是node-sass这个包本身的历史兼容性问题。前端联调阶段最费时的往往是接口返回字段名不一致。后端返回{shipName: Yamato}前端组件绑定的却是v-modelship_name数据自然渲染不出来。解决这个问题没有捷径只能前后端统一字段命名规范要在接口设计阶段就定好字段名规则。项目里我统一采用驼峰命名数据库字段转驼峰由MyBatis自动处理前端直接使用驼峰字段完全对齐。7.3 Spring Boot启动失败的几个原因端口被占用是最常见的启动失败原因。Spring Boot默认8080端口如果有其他服务占用日志里会明确提示Port already in use解决方式就是改端口或者杀掉占用进程。我见过不少同学在这个问题上困惑很久其实看第一行日志就能定位。第二个常见原因是数据库连接不上导致的启动失败。Spring Boot启动时如果数据源连不上默认配置下整个应用会启动失败。排查时先确认MySQL服务是否启动、账号密码是否正确、数据库是否创建成功、URL是否匹配。如果都不想暴露数据库密码到配置文件里可以先用一个本地测试环境把基础流程跑通再切换到正式配置。第三个值得一提的点是Lombok的依赖。如果IDEA里没有安装Lombok插件而且pom.xml中勾选了Lombok依赖项目会在编译阶段报找不到getter方法。不要怀疑自己的代码写错了先去装插件。IDEA对Lombok的支持需要额外安装插件这一点在首次从命令行clone项目打开IDEA时经常遇到。7.4 前端表格数据不显示的排查路径后台管理表格打开后空白相信不少人遇到过。排查路径我有一次按顺序整理出来了先打开浏览器开发者工具看Network看接口请求是否发出。如果接口没发检查前端调用的函数名、路由和api封装文件路径是否正确。如果接口发了但状态码是500看后端控制台报错信息根据堆栈去定位是SQL语法错误还是代码逻辑问题。如果接口返回了数据但还是空白那问题在字段绑定环节需要检查返回的JSON字段名和表格的prop属性是否一致。分支判断完之后基本能锁定问题范围不至于像无头苍蝇一样乱试。这个排查路径本身就是一种工程化思维遇到问题先分层定位不要跳步猜。8. 从课程设计到真实项目这个系统还能怎么扩展做完基础功能后如果想让项目更有亮点或者想让自己的能力提升一个层次有一些很自然的扩展方向。一个是搜索能力的升级。现在的舰船搜索走的是数据库模糊查询数据量小的时候没问题但如果舰船资料和攻略文章数量到了十万级甚至百万级SQL的LIKE性能就会成为瓶颈。引入Elasticsearch做全文检索引擎是常见的演进方向舰船名、攻略内容都能在毫秒级返回搜索匹配结果。这个改造对后端架构的影响主要在增删改的时候需要同步维护ES索引其他部分影响不大。另一个是缓存策略的优化。现在每个列表接口每次请求都直接查数据库Portal端和后台管理端的重复查询量不小。引入Redis做热点数据缓存把舰船详情这类读多写少的数据放到缓存里设置合理的过期时间接口响应速度会有明显提升。这个点是面试和答辩时很加分的性能优化项。登录鉴权也可以升级。目前的token方案是单机内存存储如果将系统部署成多实例比如两台服务器一起跑同一个jar不同实例之间就无法共享登录状态。升级方案就是引入Spring Security配合JWT方案或者仍然用token但把存储转移到Redis。这样在真实生产环境里才能经受住多实例部署的考验。如果你正好要做类似的百科科普类系统换个游戏题材或者做成通用百科都是很好的练手路径。核心功能模块分类管理、内容管理、搜索、后台管理的代码结构可以完全复用只需要替换数据内容和前端样式。我个人在实际操作中最大的体会是做这种综合项目难度不在某个单独的技术点上而在技术栈之间的串接和联调。Spring Boot好写、Vue也好写但它们之间如何高效通信、数据格式如何约束、部署时如何一体化才是真正考验项目掌握程度的地方。希望这份全过程拆解能让你在做类似项目时少踩几个坑把时间花在更有价值的功能打磨上。最后再分享一个小技巧项目里所有接口的JSON输出字段可以在后端统一加一个全局过滤器做字段名一致性校验避免前端联调时因为字段命名问题反复拉扯。
返回列表