前后端分离这四个字很多搞Web开发的朋友已经听过无数次了但真正动手把一套完整的系统从零搭起来还是会遇到一堆文档里查不到的坑。我最近用SpringBootVueMyBatisMySQL这套主流组合写了一个电子产品销售系统涵盖了商品展示、购物车、订单管理、后台管理等完整的电商闭环。这篇文章会把这套系统的设计思路、核心接口实现、前端页面构建、联调部署全过程拆开讲清楚尤其把操作中容易翻车的细节都标记出来适合正在学SpringBoot和Vue、想找一个完整项目练手的人也给打算做前后端分离项目的同学一个可以直接参考的样板。这套项目我用的是经典的三层架构加RESTful API设计后端SpringBoot提供纯接口服务前端Vue负责页面渲染和用户交互MyBatis作为持久层框架操作MySQL。整套代码量不算大但麻雀虽小五脏俱全从用户登录态管理到商品分页查询、从购物车数据持久化到订单状态流转都有涉及。读完这篇文章你能对整个前后端分离项目的开发流程建立完整认知也能拿到一份可以直接放到简历上的实战经验。1. 技术选型为什么是SpringBootVueMyBatisMySQL1.1 前后端分离的核心逻辑先聊清楚什么叫前后端分离以及为什么这套项目要这么做。传统的Java Web开发用JSP或者Thymeleaf后端把数据塞进模板里由服务器渲染成HTML再返回给浏览器前端代码和后端逻辑搅在一块改个页面样式往往要重新打包整个应用。前后端分离的思路是把前端和后端看成两个独立的应用。前端用Vue这类框架自己管理页面路由和数据渲染通过AJAX请求调用后端的JSON接口获取数据两边只通过HTTP协议通信。这样做的好处非常直接后端团队专注于接口的稳定性和业务逻辑前端团队专注于页面交互和用户体验互不干扰。而且同一套后端接口理论上是通用的——以后要做App、小程序接口很可能不需要改。当你真正把项目拆成两个工程分别启动时你会意识到一个事实前后端分离带来的不只是架构上的清晰更是开发模式上的解耦。前端开发不再依赖后端环境的启动后端调试也不再需要关心页面上某个按钮的点击事件。这也是当前企业级项目普遍采用这种模式的核心原因。1.2 四个核心组件的角色分工这套系统里的四个技术组件每一个承担的角色都非常明确缺一个都要出问题。SpringBoot是整个后端服务的地基。它基于Spring框架但通过自动配置大幅简化了传统Spring项目的繁琐配置。以前用SSMSpringSpringMVCMyBatis搭项目要写一堆XML配置文件数据源配置、事务管理配置、MyBatis的SqlSessionFactory配置等等。SpringBoot把这些都自动化了只需要在application.yml里写上数据库连接信息框架自己会把该建的对象建好。我选SpringBoot还有个重要原因它内置了Tomcat打包成jar后直接java -jar就能运行部署成本极低。Vue在前端负责所有页面逻辑。这套项目中我用的是Vue 2搭配Element UI组件库。Vue的核心优势是响应式数据绑定你只需要维护好JavaScript里的数据对象页面上的展示会自动跟着变化不用像jQuery那样手动操作DOM。对于销售系统这种数据交互频繁的场景Vue能明显减少代码量。MyBatis是数据库操作层的关键工具。它做的事情本质上是把Java方法和SQL语句关联起来你定义一个接口方法在XML文件里写对应的SQLMyBatis负责把参数传进去、把查询结果映射成Java对象。相比JPA的全自动ORMMyBatis更灵活可控复杂的多表查询写起来更直观尤其适合这种表结构相对清晰的业务系统。MySQL负责数据存储。电子产品销售系统涉及用户、商品、订单、购物车等结构化数据MySQL这种关系型数据库天然适合。配合Navicat或命令行工具做数据管理开发效率很高。这套组合经过了大量生产环境验证稳定性有保障社区资料也多遇到问题时几乎都能搜索到对应解法。2. 系统功能拆解与数据库设计2.1 功能模块怎么划分从一个产品销售系统的实际业务出发我先把功能拆成了两个大端用户端和管理端再按照角色细分模块。用户端面向普通消费者核心功能有四个。账号模块负责注册与登录登录成功后签发Token后续请求都要带着这个凭证商品模块负责展示电子产品列表支持按分类筛选和关键词搜索列表要分页展示购物车模块允许用户把商品加入购物车修改数量勾选后生成订单订单模块则记录了用户下单的商品明细、收货信息、订单状态用户可以查看自己的订单列表和详情。管理端面向运营人员功能相对集中。商品管理需要实现新增、编辑、上下架操作分类管理维护商品的所属类目订单管理可以查看全部订单修改订单状态比如把待发货改成已发货用户管理查看注册用户列表并支持禁用恶意账号。把需求摸清楚之后才能开始建表否则字段遗漏后面非常难受。这个确认需求的过程我建议花两三天时间仔细对一遍比代码写了一半再改表结构划算得多。2.2 数据库表设计的关键细节整个系统的数据表我设计了六张每一张表的字段都对应实际业务。用户表user用来存储账号信息字段包括id主键、username用户名、password密码、nickname昵称、phone手机号、create_time创建时间。密码字段要存加密后的密文我用的BCrypt加密验证密码时用加密工具比对千万不能明文存密码这是最基本的安全红线。商品表product存放电子产品信息字段有id、name商品名称、category_id所属分类、price单价、stock库存、image封面图、description详情描述、status上下架状态。价格字段用DECIMAL类型避免浮点运算精度问题。库存字段在后续的购买逻辑里非常关键下单时要扣减库存要防止超卖。分类表category就简单很多id和name两个核心字段再加一个sort排序值。电子产品可以分成手机、电脑、耳机、配件等类目用户端首页按分类入口进入商品列表。购物车表cart_store和订单相关的表要说明一下。购物车表字段包括id、user_id、product_id、quantity数量、checked是否选中一个用户对应多条购物车记录。订单表orders包含订单编号order_no、用户ID、总金额、收货信息、订单状态等。订单详情表order_item记录每个订单下的具体商品快照——商品名称、单价、数量、图片。为什么要做快照因为商品信息是随时可能变的价格调整了或者商品下架了已生成的订单不能跟着变所以要冗余一份当时的商品信息这是电商系统里很常见的做法。2.3 表关系与业务闭环六张表之间的关系并不复杂但要在设计阶段就理清楚。用户表和购物车表是一对多关系一个用户可以有多个购物车条目用户表和订单表是一对多关系一个用户可以下多个订单商品表和分类表是多对一关系多个商品属于同一个分类订单表和订单详情表是一对多关系一个订单对应多个商品明细。在设计时我特别留意了外键的使用。实际开发中我通常不物理建外键约束而是依靠代码逻辑保证数据一致性。原因很简单外键约束会降低数据库的写入性能而且在分库分表场景下外键基本不可用。只要业务代码里做好关联删除和逻辑校验不建外键是更符合生产实践的选择。还有一个设计要点就是订单编号。用户下单时生成唯一订单号我用的是时间戳加用户ID加随机数的组合保证并发场景下不会重复。如果直接使用数据库自增ID当订单号很容易被猜到业务量也不够安全。我在写建表语句时习惯把字符集统一设为utf8mb4这个编码支持emoji和一些特殊字符避免用户输入某些生僻字或者特殊符号时出现乱码。排序规则选择utf8mb4_general_ci就够了对中文排序要求高的场景可以选utf8mb4_unicode_ci不过一般项目用不上。3. 后端接口开发SpringBootMyBatis的实战要点3.1 后端工程结构与初始化后端项目的包结构我按照标准的三层架构来组织controller层负责接收前端请求并返回数据service层处理业务逻辑mapper层通过MyBatis与数据库交互。model包放实体类config包放配置类common包放统一返回结果和异常处理。项目初始化直接用Spring Initializr通过start.spring.io生成基础工程勾选Web、MyBatis、MySQL Driver依赖。不过这里有个小坑不同版本的SpringBoot对MyBatis Starter的支持略有差异我用的SpringBoot 2.7.x对应mybatis-spring-boot-starter 2.3.x版本选不对会出现启动报错或者Mapper扫描不到的情况。统一返回结果的设计值得单独说一下。前后端分离项目中前端需要知道接口是成功还是失败错误信息是什么。我定义了一个Result类包含code、message、data三个字段。成功时code为200业务失败时code为自定义的错误码比如参数错误400、未登录401。前端拿到响应后先判断code再决定走正常流程还是弹出错误提示。如果没有这个统一结构前端的错误处理代码会写得支离破碎。还有全局异常处理用RestControllerAdvice注解配合ExceptionHandler捕获所有异常并转成标准格式的Result。如果不做这一步框架抛出的默认异常信息会直接返回给前端既不友好也可能暴露内部细节。我第一个版本就漏了全局异常处理前端拿到500错误时只有一堆堆栈信息排查问题非常痛苦。3.2 核心接口实现流程后端最关键的两个流程一个是登录鉴权一个是购物车到订单的流转。登录我用的是JWTJSON Web Token拦截器方案。用户登录成功后后端签发一个包含用户信息的Token返回给前端前端存储下来并在后续请求的请求头中携带后端拦截器校验Token有效性。JWT的用法其实不难引入java-jwt依赖登录时生成Token拦截器里解析Token拿到用户ID放到请求上下文。需要处理的核心问题是Token过期和异常Token的响应方式要让前端在Token失效时自动跳回登录页而不是一直报500错误。拦截器中如果校验失败我直接返回401状态码和一个明确的JSON提示。商品列表和搜索其实最容易出问题的是分页。用MyBatis分页最方便的方式是引入PageHelper插件在查询前执行PageHelper.startPage(pageNum, pageSize)紧跟其后的查询语句自动被拦截并加上LIMIT。要注意的是PageHelper只对紧随其后的第一条查询生效所以千万别在startPage和查询之间插入其他数据库操作否则分页条件就错位了。页面上还需要返回总条数和总页数我用PageInfo对象封装分页结果一次全给前端。购物车加入订单的流程是这样的前端提交一个包含商品ID和数量列表的请求后端拿到后校验库存是否充足计算总金额生成订单记录和订单详情记录最后扣减库存。这三个操作必须放在同一个事务里只要有任何一个环节失败整个订单都不能生成。我用的方式是给service方法加Transactional注解如果抛异常就回滚。顺序上要注意先扣库存再生成订单还是先生成订单再扣库存本质上没有绝对标准但我的习惯是先检查库存再插入订单最后更新库存这样失败时库存不会被误扣。3.3 MyBatis的坑与实用写法MyBatis最核心的是XML映射文件但如果Mapper接口和XML文件放错位置或者命名空间不对启动时就会直接报错。SpringBoot项目里我习惯把这些XML文件放在resources/mapper目录下然后在application.yml里配置mapper-locations: classpath:mapper/*.xml。Mapper接口放在com.xxx.mapper包接口名和XML文件名必须一致这是MyBatis的硬性约定。动态SQL是MyBatis最强大的功能。商品列表的查询条件不是固定的用户可能按分类筛选、按关键词搜索、按价格区间过滤。我使用 标签配合 标签拼接SQLMyBatis会自动处理多余的AND关键字这也是我最推荐的条件查询写法。另一个值得记录的是批量操作。管理端需要批量上下架商品前端的做法是传一个商品ID数组后端就需要通过MyBatis实现批量更新。在XML中用 标签拼接IN条件或者批量UPDATE语句。数量不多的时候直接用拼接的方式没问题但如果批量数据量很大就要考虑分批处理或者是用CASE WHEN的方式。这里我给个最稳妥的实现update idbatchUpdateStatus parameterTypemap UPDATE product SET status #{status} WHERE id IN foreach collectionids itemid open( separator, close) #{id} /foreach /update参数传递上有个容易踩的坑如果你在Mapper接口方法上使用了Param注解指定参数名XML中引用参数时必须用注解指定的名字否则会占位符对不上。这说明Mapper接口的参数命名规范很重要不要依赖编译期保留参数名显式声明更安全。最后说MyBatis的缓存。MyBatis自带一级缓存和二级缓存一级缓存默认开启且作用范围是SqlSession但在Spring整合环境下SqlSession每次请求都会重建所以一级缓存实际用处不大。二级缓存需要在Mapper XML中显式配置但因为缓存的是对象引用在多线程下可能会读到脏数据我不建议在电商这种数据一致性要求高的系统里开启。让数据库自己管数据是最让人放心的方式缓存这种优化手段等真到了性能瓶颈再考虑也不迟。4. 前端构建Vue页面与状态管理4.1 从环境准备到项目初始化前端开发的第一步是搭好Node.js环境。Node.js装好之后npm包管理命令才能用。我遇到过不少新手卡在这一步注意Node.js版本不能太老也不能太新Vue 2项目建议使用Node 16或者18的LTS版本太新的版本有时会有依赖兼容问题。项目创建我用的是Vue CLI方式。特别提醒一下如果你在命令行执行vue create my-shop-frontend时发现命令不存在说明没有全局安装Vue CLI先执行npm install -g vue/cli安装即可。初始化过程中可以选择手动配置功能勾选Router和VuexCSS预处理器选Less或者直接不用按需选择即可。项目目录里src下会有几个关键目录router放路由配置store放Vuex状态管理views放页面组件api目录放所有和后端交互的请求封装。我习惯把axios请求单独封装成一个request.js文件创建axios实例、设置baseURL、添加请求拦截器注入Token、响应拦截器中统一处理错误码。4.2 路由配置与登录态控制的联动路由配置要做的事情不只是把路径和组件对应起来更重要的是控制访问权限。一套销售系统里后台管理页面绝对不允许未登录的人随便访问。我的做法是在路由定义里给需要登录的页面加meta属性里面放一个requiresAuth: true标记。然后在Axios实例里再加一层路由守卫用beforeEach钩子检查用户要访问的页面是否需要登录需要的话再看Vuex里有没有Token没有Token就强制跳到登录页。这样用户手动在地址栏输入管理端地址也会被弹回登录页。Vuex管理全局状态的重点是用户信息和Token。用户登录成功后把Token和用户昵称存到Vuex里同时写入localStorage。页面刷新时Vuex数据会丢失需要在应用启动时从localStorage读取并恢复状态——在App.vue的created生命周期里执行一次初始化操作。这个环节逻辑不复杂但漏掉的后果很直接用户刷新一下页面就掉登录了体验很糟糕。4.3 核心页面的实现思路商品列表页是用户进入系统后的主页面我采用栅格布局一行展示四个商品卡片。每个商品卡片展示图片、名称、价格和加入购物车按钮。数据来源就是在api目录中调用后端接口获取商品分页数据把返回的list渲染到页面上。Loading状态要处理好接口请求时显示加载动画请求失败时显示错误和重试按钮不然用户会误以为页面卡住了。购物车页面相对复杂一些因为需要处理多选、数量增减、金额计算这些交互。关键技巧是使用Vue的computed计算属性选中商品的总金额不用手动在每次操作后重新计算只要把计算逻辑写在computed里依赖的数据一变化金额自动刷新。这就是Vue响应式系统带来的开发效率提升如果用原生JS写这部分逻辑代码量会成倍增长。管理端的商品管理表单页面我用了Element UI的Form组件配合它的校验规则实现必填项检查。编辑和新增共用一个表单组件通过传入不同的初始数据区分模式。这里有一个小技巧打开编辑弹窗时把row对象直接交给表单会导致修改弹窗内容时表格里那一行的数据同步变化因为两个对象引用的是同一个地址。所有需要新增和编辑共存的表单一定要浅拷贝一份数据推荐用JSON序列化的方式实现深拷贝。// 数据深拷贝避免直接赋值导致的数据联动 const formData JSON.parse(JSON.stringify(this.getRowData)) this.form formDataElement UI的表格组件用起来很顺手自带分页组件和排序功能后端返回的分页数据只需绑定到对应属性上即可。有一点要记住表格里显示的字段如果后端返回的字段名和前端期望的不一致要么让后端改字段名要么在前端用formatter函数处理不要在前端页面里做复杂的字段重新组装。5. 前后端联调与部署上线5.1 跨域问题怎么干净地解决前后端分离项目联调遇到的第一个拦路虎就是跨域。前端跑在8080端口后端跑在8081端口浏览器直接请求就会报CORS错误。这里的原理是浏览器的同源策略只有当协议、域名、端口完全一致时浏览器才允许请求访问。解决方案有两条路。第一条是在开发环境下用Vue CLI的proxy代理在vue.config.js中配置devServer.proxy把/api开头的请求转发到后端地址。这样浏览器认为请求是同源的因为请求发给了前端自己的8080端口由前端开发服务器转发到后端。这是开发阶段最推荐的方式配置一次就不用再管了。第二条是在后端配置支持跨域。SpringBoot中可以用CrossOrigin注解加在Controller类上也可以写一个WebMvcConfigurer配置类统一配置跨域规则。但生产环境下一般不这么干因为前后端通过Nginx反代时是同一个域名不存在跨域问题开着跨域配置反而多一道安全风险。最好的实践是开发环境用前端代理生产环境用Nginx统一转发后端压根不用开CORS。5.2 后端打包部署的完整流程后端部署可以聊的不算复杂但每一步都有它的必要性。首先用Maven的package命令打jar包执行mvn clean package -DskipTests跳过测试构建产物出现在target目录下。SpringBoot内置的Tomcat让部署少了很多麻烦直接传jar包到服务器执行java -jar productsystem.jar就能运行。生产环境的数据库连接配置不能直接写在代码里我通常用application.yml里的profile机制区分开发和生产配置。SpringBoot支持通过spring.profiles.active参数动态指定使用哪份配置打包时默认用开发配置部署时启动命令加上--spring.profiles.activeprod就会加载application-prod.yml里的生产配置。生产配置里把数据库地址换成服务器的MySQL地址数据库密码用环境变量注入避免明文写在代码仓库里。服务器上Java进程要保证可持续运行。最基础的方式是使用nohup java -jar命令加上符号让进程后台运行但这种方式在服务器重启后有风险。我用的方案是把启动命令写成一个Shell脚本服务器重启后手动执行脚本即可。更进阶一点的可以用systemd配置成系统服务开机自启动但这些后面项目上线时再优化也来得及。端口和防火墙也要提前确认。后端端口部署在服务器上需要确保安全组和防火墙放行该端口否则外部访问不到。如果端口被占用使用lsof命令先查一下端口是被哪个进程占用再决定清理进程还是换端口。5.3 前端打包与Nginx托管前端部署比后端简单很多。在项目根目录执行npm run buildVue CLI会先进行代码检查、然后打包压缩生成一个dist目录里面是静态文件——一个HTML入口和一堆JS、CSS文件。这些文件不依赖Node环境只需要一个Web服务器托管即可。生产部署最常用的是Nginx。把dist目录上传到服务器的某个路径下然后修改Nginx配置前端页面文件用root指令指向dist目录所有API请求用location配置转发到后端服务。Nginx的配置文件配置好后执行nginx -t检查语法然后nginx -s reload重载配置生效。server { listen 80; server_name your-domain.com; root /usr/share/nginx/html/dist; index index.html; location /api/ { proxy_pass http://127.0.0.1:8081; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location / { try_files $uri $uri/ /index.html; } }这段配置里有两处关键。第一处是location /api/把所有API请求转发给本机8081端口的后端服务解决了生产环境的跨域问题。第二处是try_files指令指定如果用户直接访问某个子路由路径如/product/123恰好这个文件不存在就返回index.html让前端路由接管处理。如果不配置这一行刷新页面的时会出现404因为Nginx找不到那个路径对应的实际文件。6. 常见问题与排查技巧实录6.1 项目启动阶段的经典报错数据库连不上是我遇到频率最高的一个问题。刚配好MySQL时后端启动一直报Communications link failure或者Access denied。排查时要分清是网络问题还是认证问题先用命令在服务器本机执行mysql -u username -p试试能不能连上能连上说明网络没问题再确认用户名密码和授权情况。MySQL 8.0之后的认证插件跟旧版本驱动有兼容性问题如果用的MySQL 8.0但JDBC驱动是5.x就会报Unknown character set或者加密集群错误解决办法是升级mysql-connector-java到8.x并配置serverTimezone参数。中心还有一个频繁踩坑的点是时区问题。MySQL 8.x默认时区和中国时区不同不设置的话数据库的时间字段和Java程序的时间会有时差。在JDBC连接串中加上serverTimezoneAsia/Shanghai和useSSLfalse这两个参数建议固定写上。端口冲突也有很多人遇到。执行java -jar启动时如果提示端口被占用先查一下那个端口是不是被其他服务占了。可以用netstat -ano命令查看端口占用情况找到对应进程PID后强制结束才可以重新启动。开发期间后端前端一起跑端口选择上尽量错开后端用8081前端用8080一眼能分清。6.2 MyBatis相关的非常经典的坑第一个MyBatis高频坑是XML文件没有编译到classpath目录。Maven项目默认只把resources下的文件打进classpath如果XML文件放在src/main/java目录下打包时会直接被忽略。解决的方案早就确定了XML放resources/mapper目录下并在pom.xml的build节点里配置resources资源路径确保XML被包含进jar包。另一个相关的问题是修改XML文件后没生效这是因为项目一直跑着旧版本的class需要先clean再重新编译。第二个高频坑是参数传递丢失。用Param指定参数名之后XML里的#{xxx}名称必须和它完全一致写错一个字母就会报BindingException。这个问题排查起来很费劲我犯过一次把参数名写错日志只提示找不到参数根本不知道从哪里查起后来把日志级别改成DEBUG看到MyBatis输出的Preparing语句才发现占位符不存在。遇到绑定相关的异常先检查Mapper接口的Param注解和XML中的参数引用是否完全对应这个检查胜过看日志猜半天。第三个问题是动态SQL里的字段名写错。MyBatis默认开启驼峰命名映射后数据库列名下划线转Java属性驼峰。但如果你没有开启map-underscore-to-camel-case这一项查询返回的create_time字段无法映射到createTime属性全部是null这个问题的特征很奇怪——数据明明有值Java对象里就是取不到。我建议在application.yml中把该项配置设为true配合统一的命名规范能省掉不少麻烦。6.3 前后端联调阶段的典型问题404错误是最常见的联调问题。前端请求的URL和后端Controller的映射路径不一致就会出现这种情况。解决时先把浏览器开发者工具打开看请求URL和实际后端接口路径差在哪别急着改代码。如果后端用的路径是/product/list但前端请求的是/products/list一字之差就是完全不同的接口或者后端地址端口改了前端配置文件没同步更新类似的问题听上去低级但反复出现。JSON格式对不上的问题也值得一提。后端返回字段是createTime前端期望的是createdAt或者后端返回的日期是一串时间戳前端不知道怎么显示成日期字符串。最理想的是在前后端约定接口文档时就统一这些约定但实际项目中往往是后端先行开发然后前端拿字段名做适配。好消息是现在前后端对接已经比较标准化Restful风格和JSON字段命名一般使用驼峰式只要遵循这个惯例这类的对接成本会低很多。另外还有Token失效后的状态处理。前端的请求拦截器在后端返回401时需要做两件事清除本地的Token和用户信息并且跳转到登录页。如果不处理用户会看到一个长时间卡在加载状态的页面或者报错弹窗而实际原因只是令牌过期。现在的实现是在响应拦截器里统一捕获401错误导向登录页用户重新登录后还能接着用之前收藏的商品体验才过得去。6.4 部署环境的运行期问题服务器上跑起来以后遇到的问题是前端页面能打开但接口全部请求失败。这个现象几乎都是Nginx转发配置不对引起的检查Nginx的error.log日志如果看到connect() failed说明proxy_pass指向的后端地址不通先确认后端进程是否还活着再检查地址端口是否填写正确。Tomcat的线程池配置也是一些较深的坑。高并发情况下如果后端接口处理太慢大量请求会堵塞在容器线程池表现为所有接口响应都变得缓慢即使轻量级请求也排队。SpringBoot中可以通过server.tomcat.max-threads参数调整线程池大小对话时要结合实际场景调整没有一个适用于所有项目的固定值。我一般先保持默认压测后再调整。数据库连接池的配置也会出问题尤其是并发场景下连接数不够会抛异常。HikariCP是SpringBoot默认的连接池它的配置就写在application.yml里。maximum-pool-size默认值比较保守如果QPS较高建议调大一些但也不能盲目调很大因为每个连接都会占用数据库资源太大会适得其反。要注意对核心接口做压测看峰值的连接数需求量再设置一个安全余量。我在实际操作中的体会是排查这些问题的关键要先弄清楚报错发生在哪一层——是浏览器请求没发出还是Nginx转发失败还是后端接口本身报错还是数据库语句执行出错。从上到下逐层排查效率会高很多而不是东看一眼西看一眼地浪费时间。这套项目做下来最大的感受是前后端分离真的不只是写了两个工程那么简单它倒逼你把接口设计、数据格式、部署方案全部提前想清楚。遇到问题不要慌大多数坑都是网上别人踩过的只是你还没搜到关键词。最后再分享一个小技巧所有和数据库相关的字段、接口相关的路径前后端开发过程中一定要做一次书面约定哪怕只是写在项目README里也能让你少熬好几个小时的夜。